mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-28 07:18:30 +00:00
Rewrite Windows console input handling
This change removes our use of ENABLE_VIRTUAL_TERMINAL_INPUT (which isn't very good) in favor of having read() translate Windows Console input events to ANSI/XTERM sequences by hand. This makes it possible to capture important keystrokes (e.g. ctrl-space) that weren't possible before. Most importantly this change also removes the stdin/sigwinch worker threads, which never really worked that well. Interactive TTY sessions will now work reliably when a Cosmo process spawns or forks another Cosmo process, e.g. unbourne.com launching emacs.com.
This commit is contained in:
parent
ececec4c94
commit
d6c2830850
27 changed files with 635 additions and 464 deletions
|
@ -17,32 +17,39 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#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/fmt/itoa.h"
|
||||
#include "libc/intrin/nomultics.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/vk.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/inputrecord.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
|
@ -50,33 +57,385 @@
|
|||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
__static_yoink("WinMainStdin");
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
|
||||
static const struct {
|
||||
uint16_t vk;
|
||||
uint32_t normal_str;
|
||||
uint32_t shift_str;
|
||||
uint32_t ctrl_str;
|
||||
uint32_t shift_ctrl_str;
|
||||
} kVirtualKey[] = {
|
||||
#define SW(s) W4(s "\0\0")
|
||||
#define W4(s) (s[3] + 0u) << 24 | s[2] << 16 | s[1] << 8 | s[0]
|
||||
#define VK(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \
|
||||
{ vk, SW(normal_str), SW(shift_str), SW(ctrl_str), SW(shift_ctrl_str) }
|
||||
VK(kNtVkInsert, "2~", "2;2~", "2;5~", "2;6~"),
|
||||
VK(kNtVkEnd, "4~", "4;2~", "4;5~", "4;6~"),
|
||||
VK(kNtVkDown, "B", "1;2B", "1;5B", "1;6B"),
|
||||
VK(kNtVkNext, "6~", "6;2~", "6;5~", "6;6~"),
|
||||
VK(kNtVkLeft, "D", "1;2D", "1;5D", "1;6D"),
|
||||
VK(kNtVkClear, "G", "1;2G", "1;5G", "1;6G"),
|
||||
VK(kNtVkRight, "C", "1;2C", "1;5C", "1;6C"),
|
||||
VK(kNtVkUp, "A", "1;2A", "1;5A", "1;6A"),
|
||||
VK(kNtVkHome, "1~", "1;2~", "1;5~", "1;6~"),
|
||||
VK(kNtVkPrior, "5~", "5;2~", "5;5~", "5;6~"),
|
||||
VK(kNtVkDelete, "3~", "3;2~", "3;5~", "3;6~"),
|
||||
VK(kNtVkNumpad0, "2~", "2;2~", "2;5~", "2;6~"),
|
||||
VK(kNtVkNumpad1, "4~", "4;2~", "4;5~", "4;6~"),
|
||||
VK(kNtVkNumpad2, "B", "1;2B", "1;5B", "1;6B"),
|
||||
VK(kNtVkNumpad3, "6~", "6;2~", "6;5~", "6;6~"),
|
||||
VK(kNtVkNumpad4, "D", "1;2D", "1;5D", "1;6D"),
|
||||
VK(kNtVkNumpad5, "G", "1;2G", "1;5G", "1;6G"),
|
||||
VK(kNtVkNumpad6, "C", "1;2C", "1;5C", "1;6C"),
|
||||
VK(kNtVkNumpad7, "A", "1;2A", "1;5A", "1;6A"),
|
||||
VK(kNtVkNumpad8, "1~", "1;2~", "1;5~", "1;6~"),
|
||||
VK(kNtVkNumpad9, "5~", "5;2~", "5;5~", "5;6~"),
|
||||
VK(kNtVkDecimal, "3~", "3;2~", "3;5~", "3;6~"),
|
||||
VK(kNtVkF1, "[A", "23~", "11^", "23^"),
|
||||
VK(kNtVkF2, "[B", "24~", "12^", "24^"),
|
||||
VK(kNtVkF3, "[C", "25~", "13^", "25^"),
|
||||
VK(kNtVkF4, "[D", "26~", "14^", "26^"),
|
||||
VK(kNtVkF5, "[E", "28~", "15^", "28^"),
|
||||
VK(kNtVkF6, "17~", "29~", "17^", "29^"),
|
||||
VK(kNtVkF7, "18~", "31~", "18^", "31^"),
|
||||
VK(kNtVkF8, "19~", "32~", "19^", "32^"),
|
||||
VK(kNtVkF9, "20~", "33~", "20^", "33^"),
|
||||
VK(kNtVkF10, "21~", "34~", "21^", "34^"),
|
||||
VK(kNtVkF11, "23~", "23$", "23^", "23@"),
|
||||
VK(kNtVkF12, "24~", "24$", "24^", "24@"),
|
||||
#undef VK
|
||||
#undef W4
|
||||
#undef SW
|
||||
};
|
||||
|
||||
static textwindows int ProcessSignal(int sig, struct Fd *f) {
|
||||
if (f) {
|
||||
if (_weaken(__sig_raise)) {
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
_weaken(__sig_raise)(sig, SI_KERNEL);
|
||||
pthread_mutex_lock(&f->lock);
|
||||
if (!(__sighandflags[sig] & SA_RESTART)) {
|
||||
return eintr();
|
||||
}
|
||||
} else if (sig != SIGWINCH) {
|
||||
TerminateThisProcess(sig);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows uint32_t GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
||||
for (int i = 0; i < ARRAYLEN(kVirtualKey); ++i) {
|
||||
if (kVirtualKey[i].vk == vk) {
|
||||
if (shift && ctrl) {
|
||||
return kVirtualKey[i].shift_ctrl_str;
|
||||
} else if (shift) {
|
||||
return kVirtualKey[i].shift_str;
|
||||
} else if (ctrl) {
|
||||
return kVirtualKey[i].ctrl_str;
|
||||
} else {
|
||||
return kVirtualKey[i].normal_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
|
||||
static textwindows void EchoTerminalInput(struct Fd *f, char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
if (f->kind == kFdConsole) {
|
||||
hOutput = f->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 int ProcessKeyEvent(const struct NtInputRecord *r, char *p,
|
||||
uint16_t *utf16hs, struct Fd *f) {
|
||||
|
||||
uint16_t c = r->Event.KeyEvent.uChar.UnicodeChar;
|
||||
uint16_t vk = r->Event.KeyEvent.wVirtualKeyCode;
|
||||
uint16_t cks = r->Event.KeyEvent.dwControlKeyState;
|
||||
|
||||
// ignore keyup events
|
||||
if (!r->Event.KeyEvent.bKeyDown && (!c || vk != kNtVkMenu)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ignore numpad being used to compose a character
|
||||
if ((cks & kNtLeftAltPressed) && !(cks & kNtEnhancedKey) &&
|
||||
(vk == kNtVkInsert || vk == kNtVkEnd || vk == kNtVkDown ||
|
||||
vk == kNtVkNext || vk == kNtVkLeft || vk == kNtVkClear ||
|
||||
vk == kNtVkRight || vk == kNtVkHome || vk == kNtVkUp ||
|
||||
vk == kNtVkPrior || vk == kNtVkNumpad0 || vk == kNtVkNumpad1 ||
|
||||
vk == kNtVkNumpad2 || vk == kNtVkNumpad3 || vk == kNtVkNumpad4 ||
|
||||
vk == kNtVkNumpad5 || vk == kNtVkNumpad6 || vk == kNtVkNumpad7 ||
|
||||
vk == kNtVkNumpad8 || vk == kNtVkNumpad9)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
|
||||
// process virtual keys
|
||||
if (!c) {
|
||||
uint32_t w;
|
||||
w = GetVirtualKey(vk, !!(cks & kNtShiftPressed),
|
||||
!!(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed)));
|
||||
if (!w) return 0;
|
||||
p[n++] = 033;
|
||||
if (cks & (kNtLeftAltPressed | kNtRightAltPressed)) {
|
||||
p[n++] = 033;
|
||||
}
|
||||
p[n++] = '[';
|
||||
do p[n++] = w;
|
||||
while ((w >>= 8));
|
||||
return n;
|
||||
}
|
||||
|
||||
// translate utf-16 into utf-32
|
||||
if (IsHighSurrogate(c)) {
|
||||
*utf16hs = c;
|
||||
return 0;
|
||||
}
|
||||
if (IsLowSurrogate(c)) {
|
||||
c = MergeUtf16(*utf16hs, c);
|
||||
}
|
||||
|
||||
// enter sends \r in a raw terminals
|
||||
// make it a multics newline instead
|
||||
if (c == '\r' && !(__ttymagic & kFdTtyNoCr2Nl)) {
|
||||
c = '\n';
|
||||
}
|
||||
|
||||
// microsoft doesn't encode ctrl-space (^@) as nul
|
||||
// detecting it is also impossible w/ kNtEnableVirtualTerminalInput
|
||||
if (c == ' ' && (cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
// make it possible to distinguish ctrl-h (^H) from backspace (^?)
|
||||
if (c == kNtVkBack) {
|
||||
c = 0177;
|
||||
}
|
||||
|
||||
// handle ctrl-c and ctrl-\, which tcsetattr() is able to remap
|
||||
if (!(__ttymagic & kFdTtyNoIsigs)) {
|
||||
if (c == __vintr && __vintr != _POSIX_VDISABLE) {
|
||||
return ProcessSignal(SIGINT, f);
|
||||
} else if (c == __vquit && __vquit != _POSIX_VDISABLE) {
|
||||
return ProcessSignal(SIGQUIT, f);
|
||||
}
|
||||
}
|
||||
|
||||
// handle ctrl-d the end of file keystroke
|
||||
if (c == __veof && __veof != _POSIX_VDISABLE) {
|
||||
return enodata();
|
||||
}
|
||||
|
||||
// insert esc prefix when alt is held
|
||||
if ((cks & (kNtLeftAltPressed | kNtRightAltPressed)) &&
|
||||
!(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed)) &&
|
||||
r->Event.KeyEvent.bKeyDown) {
|
||||
p[n++] = 033;
|
||||
}
|
||||
|
||||
// convert utf-32 to utf-8
|
||||
uint64_t w = tpenc(c);
|
||||
do p[n++] = w;
|
||||
while ((w >>= 8));
|
||||
return n;
|
||||
}
|
||||
|
||||
// To use the tty mouse events feature:
|
||||
// - write(1, "\e[?1000;1002;1015;1006h") to enable
|
||||
// - write(1, "\e[?1000;1002;1015;1006l") to disable
|
||||
// See o//examples/ttyinfo.com and o//tool/viz/life.com
|
||||
static textwindows int ProcessMouseEvent(const struct NtInputRecord *r, char *b,
|
||||
struct Fd *f) {
|
||||
int e = 0;
|
||||
char *p = b;
|
||||
uint32_t currentbs = f ? f->mousebuttons : 0;
|
||||
uint32_t ev = r->Event.MouseEvent.dwEventFlags;
|
||||
uint32_t bs = r->Event.MouseEvent.dwButtonState;
|
||||
ev &= kNtMouseMoved | kNtMouseWheeled;
|
||||
bs &= kNtFromLeft1stButtonPressed | kNtRightmostButtonPressed;
|
||||
if (ev & kNtMouseWheeled) {
|
||||
// scroll wheel (unnatural mode)
|
||||
if (!(r->Event.MouseEvent.dwControlKeyState &
|
||||
(kNtShiftPressed | kNtLeftCtrlPressed | kNtRightCtrlPressed |
|
||||
kNtLeftAltPressed | kNtRightAltPressed))) {
|
||||
bool isup = ((int)r->Event.MouseEvent.dwButtonState >> 16) > 0;
|
||||
if (__ttymagic & kFdTtyXtMouse) {
|
||||
e = isup ? 80 : 81;
|
||||
goto OutputXtermMouseEvent;
|
||||
} else {
|
||||
// we disable mouse highlighting when the tty is put in raw mode
|
||||
// to mouse wheel events with widely understood vt100 arrow keys
|
||||
*p++ = 033;
|
||||
*p++ = '[';
|
||||
if (isup) {
|
||||
*p++ = 'A'; // \e[A up
|
||||
} else {
|
||||
*p++ = 'B'; // \e[B down
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((bs || currentbs) && (__ttymagic & kFdTtyXtMouse)) {
|
||||
if (bs && (ev & kNtMouseMoved) && currentbs) {
|
||||
e |= 32; // dragging
|
||||
}
|
||||
if ((bs | currentbs) & kNtRightmostButtonPressed) {
|
||||
e |= 2; // right
|
||||
}
|
||||
OutputXtermMouseEvent:
|
||||
*p++ = 033;
|
||||
*p++ = '[';
|
||||
*p++ = '<';
|
||||
p = FormatInt32(p, e);
|
||||
*p++ = ';';
|
||||
p = FormatInt32(p, r->Event.MouseEvent.dwMousePosition.X + 1);
|
||||
*p++ = ';';
|
||||
p = FormatInt32(p, r->Event.MouseEvent.dwMousePosition.Y + 1);
|
||||
if (!bs && currentbs) {
|
||||
*p++ = 'm'; // up
|
||||
} else {
|
||||
*p++ = 'M'; // down
|
||||
}
|
||||
if (f) {
|
||||
f->mousebuttons = bs;
|
||||
}
|
||||
}
|
||||
return p - b;
|
||||
}
|
||||
|
||||
textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
|
||||
char p[hasatleast 32],
|
||||
uint16_t *utf16hs, struct Fd *f) {
|
||||
switch (r->EventType) {
|
||||
case kNtKeyEvent:
|
||||
return ProcessKeyEvent(r, p, utf16hs, f);
|
||||
case kNtMouseEvent:
|
||||
return ProcessMouseEvent(r, p, f);
|
||||
case kNtWindowBufferSizeEvent:
|
||||
return ProcessSignal(SIGWINCH, f);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows ssize_t ReadFromWindowsConsole(struct Fd *f, void *data,
|
||||
size_t size) {
|
||||
ssize_t rc;
|
||||
int e = errno;
|
||||
uint16_t utf16hs = 0;
|
||||
pthread_mutex_lock(&f->lock);
|
||||
for (;;) {
|
||||
if (f->eoftty) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
uint32_t got = MIN(size, f->buflen);
|
||||
uint32_t remain = f->buflen - got;
|
||||
if (got) memcpy(data, f->buf, got);
|
||||
if (remain) memmove(f->buf, f->buf + got, remain);
|
||||
f->buflen = remain;
|
||||
if (got) {
|
||||
rc = got;
|
||||
break;
|
||||
}
|
||||
uint32_t events_available;
|
||||
if (!GetNumberOfConsoleInputEvents(f->handle, &events_available)) {
|
||||
rc = __winerr();
|
||||
break;
|
||||
}
|
||||
if (events_available) {
|
||||
uint32_t n;
|
||||
struct NtInputRecord r;
|
||||
if (!ReadConsoleInput(f->handle, &r, 1, &n)) {
|
||||
rc = __winerr();
|
||||
break;
|
||||
}
|
||||
rc = ConvertConsoleInputToAnsi(&r, f->buf, &utf16hs, f);
|
||||
if (rc == -1) {
|
||||
if (errno == ENODATA) {
|
||||
f->eoftty = true;
|
||||
errno = e;
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f->buflen = rc;
|
||||
} else {
|
||||
if (f->flags & O_NONBLOCK) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
uint32_t ms = __SIG_POLL_INTERVAL_MS;
|
||||
if (__ttymagic & kFdTtyNoBlock) {
|
||||
if (!__vtime) {
|
||||
rc = 0;
|
||||
break;
|
||||
} else {
|
||||
ms = __vtime * 100;
|
||||
}
|
||||
}
|
||||
if ((rc = _check_interrupts(kSigOpRestartable))) {
|
||||
break;
|
||||
}
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, ms);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno == ECANCELED) {
|
||||
rc = ecanceled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
if (rc > 0 && (__ttymagic & kFdTtyEchoing)) {
|
||||
EchoTerminalInput(f, data, size);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
||||
int64_t offset) {
|
||||
|
||||
// perform the read i/o operation
|
||||
bool32 ok;
|
||||
struct Fd *f;
|
||||
uint32_t got;
|
||||
int64_t handle;
|
||||
uint32_t remain;
|
||||
char *targetdata;
|
||||
uint32_t targetsize;
|
||||
struct PosixThread *pt;
|
||||
|
||||
f = g_fds.p + fd;
|
||||
handle = __resolve_stdin_handle(f->handle);
|
||||
handle = f->handle;
|
||||
pt = _pthread_self();
|
||||
pt->abort_errno = EAGAIN;
|
||||
size = MIN(size, 0x7ffff000);
|
||||
|
||||
bool pwriting = offset != -1;
|
||||
bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk;
|
||||
bool nonblock = !!(f->flags & O_NONBLOCK);
|
||||
pt->abort_errno = EAGAIN;
|
||||
|
||||
if (pwriting && !seekable) {
|
||||
return espipe();
|
||||
|
@ -85,33 +444,9 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
|||
offset = 0;
|
||||
}
|
||||
|
||||
uint32_t dwConsoleMode;
|
||||
bool is_console_input =
|
||||
g_fds.stdin.handle
|
||||
? f->handle == g_fds.stdin.handle
|
||||
: !seekable && (f->kind == kFdConsole ||
|
||||
GetConsoleMode(handle, &dwConsoleMode));
|
||||
|
||||
StartOver:
|
||||
size = MIN(size, 0x7ffff000);
|
||||
|
||||
// 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 && f->buflen) {
|
||||
ReturnDataFromBuffer:
|
||||
got = MIN(size, f->buflen);
|
||||
remain = f->buflen - got;
|
||||
if (got) memcpy(data, f->buf, got);
|
||||
if (remain) memmove(f->buf, f->buf + got, remain);
|
||||
f->buflen = remain;
|
||||
return got;
|
||||
}
|
||||
if (is_console_input && size && size < 3 && (__ttymagic & kFdTtyMunging)) {
|
||||
targetdata = f->buf;
|
||||
targetsize = sizeof(f->buf);
|
||||
} else {
|
||||
targetdata = data;
|
||||
targetsize = size;
|
||||
uint32_t cm;
|
||||
if (!seekable && (f->kind == kFdConsole || GetConsoleMode(handle, &cm))) {
|
||||
return ReadFromWindowsConsole(f, data, size);
|
||||
}
|
||||
|
||||
if (!pwriting && seekable) {
|
||||
|
@ -123,7 +458,7 @@ StartOver:
|
|||
.Pointer = offset};
|
||||
// the win32 manual says it's important to *not* put &got here
|
||||
// since for overlapped i/o, we always use GetOverlappedResult
|
||||
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
|
||||
ok = ReadFile(handle, data, size, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
BlockingOperation:
|
||||
if (!nonblock) {
|
||||
|
@ -172,27 +507,6 @@ StartOver:
|
|||
}
|
||||
|
||||
if (ok) {
|
||||
if (is_console_input) {
|
||||
if (__ttymagic & kFdTtyMunging) {
|
||||
switch (_weaken(__munge_terminal_input)(targetdata, &got)) {
|
||||
case DO_NOTHING:
|
||||
break;
|
||||
case DO_RESTART:
|
||||
goto StartOver;
|
||||
case DO_EINTR:
|
||||
return eintr();
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoing) {
|
||||
_weaken(__echo_terminal_input)(f, targetdata, got);
|
||||
}
|
||||
}
|
||||
if (targetdata != data) {
|
||||
f->buflen = got;
|
||||
goto ReturnDataFromBuffer;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue