feat: libc - add SYS_GETCHAR_NB and a non-blocking stdin readiness probe

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NQRTGoYgnZQsh7uCh6Jsxm
This commit is contained in:
2026-08-08 14:42:38 +02:00
co-authored by Claude Opus 5
parent fe10216f8b
commit e8407afcdb
11 changed files with 125 additions and 17 deletions
+53 -13
View File
@@ -101,6 +101,7 @@ static inline long _zos_syscall4(long nr, long a1, long a2, long a3, long a4) {
#define SYS_FREE 12
#define SYS_GETMILLISECONDS 14
#define SYS_GETCHAR 18
#define SYS_GETCHAR_NB 166
#define SYS_SPAWN 20
#define SYS_WAITPID 23
#define SYS_GETARGS 25
@@ -1478,9 +1479,23 @@ FILE *stdin = &_stdin_file;
FILE *stdout = &_stdout_file;
FILE *stderr = &_stderr_file;
/* ------------------------------------------------------------------------
stdin line editing.
Input is gathered a line at a time so backspace can still take a character
back. The gather is incremental: _stdin_pump() may be called without
blocking, in which case a half-typed line simply stays in the buffer until
the next call. That is what lets montauk_stdin_ready() answer "is a line
available?" without stealing keystrokes -- see the select() shim in the
NetSurf port, which is the reason this is not just a blocking read.
_stdin_line_ready distinguishes "buffer holds a complete line" from "buffer
holds a partial one"; readers must never look at the buffer without it.
------------------------------------------------------------------------ */
static char _stdin_linebuf[512];
static size_t _stdin_line_len = 0;
static size_t _stdin_line_pos = 0;
static int _stdin_line_ready = 0;
static void _stdin_echo_char(char c) {
if (c == '\b') {
@@ -1492,13 +1507,25 @@ static void _stdin_echo_char(char c) {
_zos_syscall1(SYS_PUTCHAR, (unsigned char)c);
}
static int _stdin_fill_line(void) {
_stdin_line_len = 0;
_stdin_line_pos = 0;
/* Gather keystrokes into _stdin_linebuf. Returns 1 when a complete line is
buffered. With block == 0, returns 0 as soon as input runs dry, keeping
whatever has been typed so far for the next call. */
static int _stdin_pump(int block) {
if (_stdin_line_ready) {
if (_stdin_line_pos < _stdin_line_len) return 1;
/* Previous line fully read; start gathering the next one. */
_stdin_line_ready = 0;
_stdin_line_len = 0;
_stdin_line_pos = 0;
}
while (_stdin_line_len + 1 < sizeof(_stdin_linebuf)) {
int c = (int)_zos_syscall0(SYS_GETCHAR);
if (c <= 0) continue;
for (;;) {
int c = block ? (int)_zos_syscall0(SYS_GETCHAR)
: (int)_zos_syscall0(SYS_GETCHAR_NB);
if (c <= 0) {
if (block) continue;
return 0;
}
if (c == '\r') c = '\n';
@@ -1513,6 +1540,7 @@ static int _stdin_fill_line(void) {
if (c == '\n') {
_stdin_linebuf[_stdin_line_len++] = (char)c;
_stdin_echo_char((char)c);
_stdin_line_ready = 1;
return 1;
}
@@ -1520,15 +1548,27 @@ static int _stdin_fill_line(void) {
_stdin_linebuf[_stdin_line_len++] = (char)c;
_stdin_echo_char((char)c);
}
}
if (_stdin_line_len > 0) {
_stdin_linebuf[_stdin_line_len++] = '\n';
_stdin_echo_char('\n');
return 1;
/* Full buffer: break the line here rather than dropping input. */
if (_stdin_line_len + 1 >= sizeof(_stdin_linebuf)) {
_stdin_linebuf[_stdin_line_len++] = '\n';
_stdin_echo_char('\n');
_stdin_line_ready = 1;
return 1;
}
}
}
return 0;
static int _stdin_fill_line(void) {
return _stdin_pump(1);
}
int montauk_stdin_ready(void) {
return _stdin_pump(0);
}
int montauk_stdin_wait(void) {
return _stdin_pump(1);
}
FILE *fopen(const char *path, const char *mode) {
@@ -1742,7 +1782,7 @@ int fgetc(FILE *stream) {
}
if (stream->is_std == 1) {
if (_stdin_line_pos >= _stdin_line_len) {
if (!_stdin_line_ready || _stdin_line_pos >= _stdin_line_len) {
if (!_stdin_fill_line()) {
stream->eof = 1;
return EOF;