Page MenuHomePhorge

tty.c
No OneTemporary

Size
22 KB
Referenced Files
None
Subscribers
None
#include <compat.h> // IWYU pragma: keep
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <uv.h>
#ifdef _WIN32
#include <io.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define ANT_WRITE_FD _write
#define ANT_ISATTY _isatty
#define ANT_STDIN_FD 0
#define ANT_STDOUT_FD 1
#define ANT_STDERR_FD 2
#else
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#define ANT_WRITE_FD write
#define ANT_ISATTY isatty
#define ANT_STDIN_FD STDIN_FILENO
#define ANT_STDOUT_FD STDOUT_FILENO
#define ANT_STDERR_FD STDERR_FILENO
#endif
#include "ant.h"
#include "descriptors.h"
#include "errors.h"
#include "internal.h"
#include "runtime.h"
#include "silver/engine.h"
#include "modules/symbol.h"
#include "modules/tty.h"
static inline bool is_callable(jsval_t value) {
uint8_t t = vtype(value);
return t == T_FUNC || t == T_CFUNC;
}
static bool parse_int(jsval_t value, int *out) {
if (vtype(value) != T_NUM) return false;
double d = js_getnum(value);
if (!isfinite(d)) return false;
if (d < (double)INT_MIN || d > (double)INT_MAX) return false;
int i = (int)d;
if ((double)i != d) return false;
*out = i;
return true;
}
static bool parse_fd(jsval_t value, int *fd_out) {
int fd = 0;
if (!parse_int(value, &fd)) return false;
if (fd < 0) return false;
*fd_out = fd;
return true;
}
static bool is_tty_fd(int fd) {
if (fd < 0) return false;
return uv_guess_handle(fd) == UV_TTY;
}
static int stream_fd_from_this(ant_t *js, int fallback_fd) {
jsval_t this_obj = js_getthis(js);
if (!is_special_object(this_obj)) return fallback_fd;
jsval_t fd_val = js_get(js, this_obj, "fd");
int fd = 0;
if (!parse_fd(fd_val, &fd)) return fallback_fd;
return fd;
}
static bool write_to_fd(int fd, const char *data, size_t len) {
if (fd < 0 || !data) return false;
if (len == 0) return true;
size_t off = 0;
while (off < len) {
#ifdef _WIN32
size_t rem = len - off;
unsigned int chunk = (rem > (size_t)INT_MAX) ? (unsigned int)INT_MAX : (unsigned int)rem;
int wrote = ANT_WRITE_FD(fd, data + off, chunk);
if (wrote <= 0) return false;
off += (size_t)wrote;
#else
ssize_t wrote = ANT_WRITE_FD(fd, data + off, len - off);
if (wrote < 0) {
if (errno == EINTR) continue;
return false;
}
if (wrote == 0) return false;
off += (size_t)wrote;
#endif
}
return true;
}
static void get_tty_size(int fd, int *rows, int *cols) {
int out_rows = 24;
int out_cols = 80;
#ifdef _WIN32
HANDLE handle = INVALID_HANDLE_VALUE;
if (fd == ANT_STDOUT_FD) {
handle = GetStdHandle(STD_OUTPUT_HANDLE);
} else if (fd == ANT_STDERR_FD) {
handle = GetStdHandle(STD_ERROR_HANDLE);
} else {
intptr_t os_handle = _get_osfhandle(fd);
if (os_handle != -1) handle = (HANDLE)os_handle;
}
if (handle != INVALID_HANDLE_VALUE) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(handle, &csbi)) {
int width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
int height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
if (height > 0) out_rows = height;
if (width > 0) out_cols = width;
}
}
#else
struct winsize ws;
if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
if (ws.ws_row > 0) out_rows = (int)ws.ws_row;
if (ws.ws_col > 0) out_cols = (int)ws.ws_col;
}
#endif
if (rows) *rows = out_rows;
if (cols) *cols = out_cols;
}
static bool str_case_eq(const char *a, const char *b) {
if (!a || !b) return false;
while (*a && *b) {
int ca = tolower((unsigned char)*a);
int cb = tolower((unsigned char)*b);
if (ca != cb) return false;
a++;
b++;
}
return *a == '\0' && *b == '\0';
}
static bool str_case_contains(const char *haystack, const char *needle) {
if (!haystack || !needle) return false;
size_t needle_len = strlen(needle);
if (needle_len == 0) return true;
size_t hay_len = strlen(haystack);
if (needle_len > hay_len) return false;
for (size_t i = 0; i + needle_len <= hay_len; i++) {
bool match = true;
for (size_t j = 0; j < needle_len; j++) {
int ca = tolower((unsigned char)haystack[i + j]);
int cb = tolower((unsigned char)needle[j]);
if (ca != cb) {
match = false;
break;
}
}
if (match) return true;
}
return false;
}
static bool read_env_object_value(
ant_t *js,
jsval_t env_obj,
const char *key,
char *buf,
size_t buf_len
) {
if (!is_special_object(env_obj) || !key || !buf || buf_len == 0) return false;
jsval_t value = js_get(js, env_obj, key);
if (vtype(value) == T_UNDEF || vtype(value) == T_NULL) return false;
jsval_t str_val = js_tostring_val(js, value);
size_t len = 0;
char *str = js_getstr(js, str_val, &len);
if (!str) return false;
if (len >= buf_len) len = buf_len - 1;
memcpy(buf, str, len);
buf[len] = '\0';
return true;
}
static const char *get_env_value(
ant_t *js,
jsval_t env_obj,
const char *key,
char *buf,
size_t buf_len
) {
if (read_env_object_value(js, env_obj, key, buf, buf_len)) return buf;
return getenv(key);
}
static int force_color_depth(const char *force_color) {
if (!force_color) return 0;
if (*force_color == '\0') return 4;
if (strcmp(force_color, "0") == 0) return 1;
if (strcmp(force_color, "1") == 0) return 4;
if (strcmp(force_color, "2") == 0) return 8;
if (strcmp(force_color, "3") == 0) return 24;
return 4;
}
static int detect_color_depth(ant_t *js, int fd, jsval_t env_obj) {
if (!is_tty_fd(fd)) return 1;
char scratch[128];
const char *force_color = get_env_value(js, env_obj, "FORCE_COLOR", scratch, sizeof(scratch));
int forced = force_color_depth(force_color);
if (forced > 0) return forced;
const char *no_color = get_env_value(js, env_obj, "NO_COLOR", scratch, sizeof(scratch));
if (no_color && *no_color) return 1;
const char *colorterm = get_env_value(js, env_obj, "COLORTERM", scratch, sizeof(scratch));
if (colorterm) {
if (str_case_contains(colorterm, "truecolor") || str_case_contains(colorterm, "24bit")) {
return 24;
}
}
#ifdef _WIN32
return 24;
#else
const char *term = get_env_value(js, env_obj, "TERM", scratch, sizeof(scratch));
if (!term) return 4;
if (str_case_eq(term, "dumb")) return 1;
if (str_case_contains(term, "256color")) return 8;
if (str_case_contains(term, "color")
|| str_case_contains(term, "xterm")
|| str_case_contains(term, "screen")
|| str_case_contains(term, "ansi")
|| str_case_contains(term, "linux")
|| str_case_contains(term, "cygwin")
|| str_case_contains(term, "vt100")
) return 4;
return 4;
#endif
}
static int palette_size_for_depth(int depth) {
switch (depth) {
case 1: return 2;
case 4: return 16;
case 8: return 256;
case 24: return 16777216;
default: return 2;
}
}
static jsval_t make_stream_error(ant_t *js, const char *op, int fd) {
return js_mkerr_typed(js, JS_ERR_GENERIC, "tty stream %s failed for fd %d", op, fd);
}
static void invoke_callback_if_needed(ant_t *js, jsval_t cb, jsval_t arg) {
if (!is_callable(cb)) return;
jsval_t cb_args[1] = { arg };
sv_vm_call(js->vm, js, cb, js_mkundef(), cb_args, 1, NULL, false);
}
static jsval_t maybe_callback_or_throw(
ant_t *js,
jsval_t this_obj,
jsval_t cb,
bool ok,
const char *op,
int fd
) {
if (is_callable(cb)) {
if (ok) {
invoke_callback_if_needed(js, cb, js_mknull());
} else {
jsval_t err = make_stream_error(js, op, fd);
invoke_callback_if_needed(js, cb, err);
}
return this_obj;
}
if (!ok) return make_stream_error(js, op, fd);
return this_obj;
}
#ifndef _WIN32
static struct {
int fd;
bool active;
struct termios saved;
} raw_state = { .fd = -1, .active = false };
#endif
static bool set_raw_mode_fd(int fd, bool enable) {
#ifdef _WIN32
intptr_t os_handle = _get_osfhandle(fd);
if (os_handle == -1) return false;
HANDLE handle = (HANDLE)os_handle;
DWORD mode = 0;
if (!GetConsoleMode(handle, &mode)) return false;
if (enable) {
mode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
} else {
mode |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
}
return SetConsoleMode(handle, mode) != 0;
#else
if (!is_tty_fd(fd)) return false;
if (enable) {
if (raw_state.active && raw_state.fd == fd) return true;
struct termios saved;
if (tcgetattr(fd, &saved) == -1) return false;
struct termios raw = saved;
raw.c_lflag &= ~(ICANON | ECHO | ISIG);
raw.c_iflag &= ~(IXON | ICRNL);
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSANOW, &raw) == -1) return false;
raw_state.fd = fd;
raw_state.saved = saved;
raw_state.active = true;
return true;
}
if (!(raw_state.active && raw_state.fd == fd)) return true;
if (tcsetattr(fd, TCSANOW, &raw_state.saved) == -1) return false;
raw_state.fd = -1;
raw_state.active = false;
return true;
#endif
}
static jsval_t get_process_stream(ant_t *js, const char *name) {
jsval_t process_obj = js_get(js, js_glob(js), "process");
if (!is_special_object(process_obj)) return js_mkundef();
jsval_t stream = js_get(js, process_obj, name);
if (!is_special_object(stream)) return js_mkundef();
return stream;
}
static void ensure_stream_common_props(ant_t *js, jsval_t stream, int fd) {
if (!is_special_object(stream)) return;
js_set(js, stream, "fd", js_mknum((double)fd));
js_set(js, stream, "isTTY", js_bool(is_tty_fd(fd)));
}
static jsval_t tty_isatty(ant_t *js, jsval_t *args, int nargs) {
if (nargs < 1) return js_false;
int fd = 0;
if (!parse_fd(args[0], &fd)) return js_false;
return js_bool(ANT_ISATTY(fd) != 0);
}
static jsval_t tty_stream_write(ant_t *js, jsval_t *args, int nargs) {
jsval_t this_obj = js_getthis(js);
if (nargs < 1) return js_false;
size_t len = 0;
char *data = js_getstr(js, args[0], &len);
if (!data) return js_false;
jsval_t cb = js_mkundef();
if (nargs > 1 && is_callable(args[1])) cb = args[1];
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
bool ok = write_to_fd(fd, data, len);
if (is_callable(cb)) {
if (ok) invoke_callback_if_needed(js, cb, js_mknull());
else invoke_callback_if_needed(js, cb, make_stream_error(js, "write", fd));
}
if (!ok) return js_false;
(void)this_obj;
return js_true;
}
static jsval_t tty_write_stream_rows_getter(ant_t *js, jsval_t *args, int nargs) {
(void)args; (void)nargs;
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
if (!is_tty_fd(fd)) return js_mkundef();
int rows = 0;
int cols = 0;
get_tty_size(fd, &rows, &cols);
return js_mknum((double)rows);
}
static jsval_t tty_write_stream_columns_getter(ant_t *js, jsval_t *args, int nargs) {
(void)args; (void)nargs;
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
if (!is_tty_fd(fd)) return js_mkundef();
int rows = 0;
int cols = 0;
get_tty_size(fd, &rows, &cols);
return js_mknum((double)cols);
}
static jsval_t tty_write_stream_get_window_size(ant_t *js, jsval_t *args, int nargs) {
(void)args; (void)nargs;
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
int rows = 0;
int cols = 0;
get_tty_size(fd, &rows, &cols);
jsval_t arr = js_mkarr(js);
js_arr_push(js, arr, js_mknum((double)cols));
js_arr_push(js, arr, js_mknum((double)rows));
return arr;
}
static jsval_t tty_write_stream_clear_line(ant_t *js, jsval_t *args, int nargs) {
jsval_t this_obj = js_getthis(js);
int dir = 0;
if (nargs > 0 && vtype(args[0]) != T_UNDEF && !parse_int(args[0], &dir)) {
return js_mkerr_typed(js, JS_ERR_TYPE, "clearLine(dir) requires a numeric dir");
}
jsval_t cb = js_mkundef();
if (nargs > 1 && is_callable(args[1])) cb = args[1];
const char *seq = "\033[2K\r";
if (dir < 0) seq = "\033[1K";
else if (dir > 0) seq = "\033[0K";
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
bool ok = write_to_fd(fd, seq, strlen(seq));
return maybe_callback_or_throw(js, this_obj, cb, ok, "clearLine", fd);
}
static jsval_t tty_write_stream_clear_screen_down(ant_t *js, jsval_t *args, int nargs) {
jsval_t this_obj = js_getthis(js);
jsval_t cb = js_mkundef();
if (nargs > 0 && is_callable(args[0])) cb = args[0];
static const char seq[] = "\033[0J";
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
bool ok = write_to_fd(fd, seq, sizeof(seq) - 1);
return maybe_callback_or_throw(js, this_obj, cb, ok, "clearScreenDown", fd);
}
static jsval_t tty_write_stream_cursor_to(ant_t *js, jsval_t *args, int nargs) {
jsval_t this_obj = js_getthis(js);
if (nargs < 1 || !parse_int(args[0], &(int){0})) {
return js_mkerr_typed(js, JS_ERR_TYPE, "cursorTo(x[, y][, callback]) requires numeric x");
}
int x = 0;
(void)parse_int(args[0], &x);
if (x < 0) x = 0;
bool has_y = false;
int y = 0;
jsval_t cb = js_mkundef();
if (nargs > 1) {
if (is_callable(args[1])) {
cb = args[1];
} else if (vtype(args[1]) == T_UNDEF) {
// no-op
} else if (parse_int(args[1], &y)) {
has_y = true;
if (y < 0) y = 0;
if (nargs > 2 && is_callable(args[2])) cb = args[2];
} else {
return js_mkerr_typed(js, JS_ERR_TYPE, "cursorTo y must be a number when provided");
}
}
char seq[64];
int n = 0;
if (has_y) n = snprintf(seq, sizeof(seq), "\033[%d;%dH", y + 1, x + 1);
else n = snprintf(seq, sizeof(seq), "\033[%dG", x + 1);
if (n < 0 || (size_t)n >= sizeof(seq)) return js_mkerr(js, "Failed to build cursor sequence");
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
bool ok = write_to_fd(fd, seq, (size_t)n);
return maybe_callback_or_throw(js, this_obj, cb, ok, "cursorTo", fd);
}
static jsval_t tty_write_stream_move_cursor(ant_t *js, jsval_t *args, int nargs) {
jsval_t this_obj = js_getthis(js);
if (nargs < 2) {
return js_mkerr_typed(js, JS_ERR_TYPE, "moveCursor(dx, dy[, callback]) requires dx and dy");
}
int dx = 0;
int dy = 0;
if (!parse_int(args[0], &dx) || !parse_int(args[1], &dy)) {
return js_mkerr_typed(js, JS_ERR_TYPE, "moveCursor(dx, dy[, callback]) requires numeric dx and dy");
}
jsval_t cb = js_mkundef();
if (nargs > 2 && is_callable(args[2])) cb = args[2];
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
bool ok = true;
if (dx != 0) {
char seq_x[32];
int n_x = 0;
if (dx > 0) n_x = snprintf(seq_x, sizeof(seq_x), "\033[%dC", dx);
else n_x = snprintf(seq_x, sizeof(seq_x), "\033[%dD", -dx);
if (n_x < 0 || (size_t)n_x >= sizeof(seq_x)) return js_mkerr(js, "Failed to build moveCursor sequence");
if (!write_to_fd(fd, seq_x, (size_t)n_x)) ok = false;
}
if (ok && dy != 0) {
char seq_y[32];
int n_y = 0;
if (dy > 0) n_y = snprintf(seq_y, sizeof(seq_y), "\033[%dB", dy);
else n_y = snprintf(seq_y, sizeof(seq_y), "\033[%dA", -dy);
if (n_y < 0 || (size_t)n_y >= sizeof(seq_y)) return js_mkerr(js, "Failed to build moveCursor sequence");
if (!write_to_fd(fd, seq_y, (size_t)n_y)) ok = false;
}
return maybe_callback_or_throw(js, this_obj, cb, ok, "moveCursor", fd);
}
static jsval_t tty_write_stream_get_color_depth(ant_t *js, jsval_t *args, int nargs) {
jsval_t env_obj = js_mkundef();
if (nargs > 0 && is_special_object(args[0])) env_obj = args[0];
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
int depth = detect_color_depth(js, fd, env_obj);
return js_mknum((double)depth);
}
static jsval_t tty_write_stream_has_colors(ant_t *js, jsval_t *args, int nargs) {
int count = 16;
jsval_t env_obj = js_mkundef();
if (nargs > 0) {
if (vtype(args[0]) == T_NUM) {
int parsed_count = 16;
if (!parse_int(args[0], &parsed_count)) {
return js_mkerr_typed(js, JS_ERR_TYPE, "hasColors(count[, env]) count must be an integer");
}
count = parsed_count;
} else if (is_special_object(args[0])) {
env_obj = args[0];
} else if (vtype(args[0]) != T_UNDEF) {
return js_mkerr_typed(js, JS_ERR_TYPE, "hasColors(count[, env]) invalid first argument");
}
}
if (nargs > 1 && is_special_object(args[1])) env_obj = args[1];
if (count < 1) count = 1;
int fd = stream_fd_from_this(js, ANT_STDOUT_FD);
int depth = detect_color_depth(js, fd, env_obj);
int max_colors = palette_size_for_depth(depth);
return js_bool(max_colors >= count);
}
static jsval_t tty_read_stream_set_raw_mode(ant_t *js, jsval_t *args, int nargs) {
jsval_t this_obj = js_getthis(js);
if (!is_special_object(this_obj)) {
return js_mkerr_typed(js, JS_ERR_TYPE, "setRawMode() requires a ReadStream receiver");
}
bool enable = nargs > 0 ? js_truthy(js, args[0]) : true;
jsval_t native_fn = js_get(js, this_obj, "__antNativeSetRawMode");
if (is_callable(native_fn)) {
jsval_t call_args[1];
int call_nargs = 0;
if (nargs > 0) {
call_args[0] = args[0];
call_nargs = 1;
}
jsval_t result = sv_vm_call(
js->vm, js, native_fn, this_obj,
call_nargs > 0 ? call_args : NULL, call_nargs,
NULL, false
);
if (vtype(result) == T_ERR) return result;
js_set(js, this_obj, "isRaw", js_bool(js_truthy(js, result) && enable));
return this_obj;
}
int fd = stream_fd_from_this(js, ANT_STDIN_FD);
if (!set_raw_mode_fd(fd, enable)) {
return js_mkerr_typed(js, JS_ERR_GENERIC, "Failed to set raw mode for fd %d", fd);
}
js_set(js, this_obj, "isRaw", js_bool(enable));
return this_obj;
}
static jsval_t tty_read_stream_constructor(ant_t *js, jsval_t *args, int nargs) {
if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "ReadStream(fd) requires a file descriptor");
int fd = 0;
if (!parse_fd(args[0], &fd)) {
return js_mkerr_typed(js, JS_ERR_TYPE, "ReadStream(fd) requires an integer file descriptor");
}
if (!is_tty_fd(fd)) {
return js_mkerr_typed(js, JS_ERR_TYPE, "ReadStream fd %d is not a TTY", fd);
}
if (fd == ANT_STDIN_FD) {
jsval_t stdin_obj = get_process_stream(js, "stdin");
if (is_special_object(stdin_obj)) {
ensure_stream_common_props(js, stdin_obj, fd);
if (vtype(js_get(js, stdin_obj, "isRaw")) == T_UNDEF) js_set(js, stdin_obj, "isRaw", js_false);
return stdin_obj;
}
}
jsval_t ctor = js_getcurrentfunc(js);
jsval_t proto = js_get(js, ctor, "prototype");
jsval_t obj = js_mkobj(js);
if (is_special_object(proto)) js_set_proto(js, obj, proto);
ensure_stream_common_props(js, obj, fd);
js_set(js, obj, "isRaw", js_false);
return obj;
}
static jsval_t tty_write_stream_constructor(ant_t *js, jsval_t *args, int nargs) {
if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WriteStream(fd) requires a file descriptor");
int fd = 0;
if (!parse_fd(args[0], &fd)) {
return js_mkerr_typed(js, JS_ERR_TYPE, "WriteStream(fd) requires an integer file descriptor");
}
if (!is_tty_fd(fd)) {
return js_mkerr_typed(js, JS_ERR_TYPE, "WriteStream fd %d is not a TTY", fd);
}
if (fd == ANT_STDOUT_FD || fd == ANT_STDERR_FD) {
jsval_t stream = get_process_stream(js, fd == ANT_STDOUT_FD ? "stdout" : "stderr");
if (is_special_object(stream)) {
ensure_stream_common_props(js, stream, fd);
return stream;
}
}
jsval_t ctor = js_getcurrentfunc(js);
jsval_t proto = js_get(js, ctor, "prototype");
jsval_t obj = js_mkobj(js);
if (is_special_object(proto)) js_set_proto(js, obj, proto);
ensure_stream_common_props(js, obj, fd);
return obj;
}
static void setup_readstream_proto(ant_t *js, jsval_t proto) {
if (!is_special_object(proto)) return;
js_set(js, proto, "setRawMode", js_mkfun(tty_read_stream_set_raw_mode));
js_set_sym(js, proto, get_toStringTag_sym(), js_mkstr(js, "ReadStream", 10));
}
static void setup_writestream_proto(ant_t *js, jsval_t proto) {
if (!is_special_object(proto)) return;
js_set(js, proto, "write", js_mkfun(tty_stream_write));
js_set(js, proto, "clearLine", js_mkfun(tty_write_stream_clear_line));
js_set(js, proto, "clearScreenDown", js_mkfun(tty_write_stream_clear_screen_down));
js_set(js, proto, "cursorTo", js_mkfun(tty_write_stream_cursor_to));
js_set(js, proto, "moveCursor", js_mkfun(tty_write_stream_move_cursor));
js_set(js, proto, "getWindowSize", js_mkfun(tty_write_stream_get_window_size));
js_set(js, proto, "getColorDepth", js_mkfun(tty_write_stream_get_color_depth));
js_set(js, proto, "hasColors", js_mkfun(tty_write_stream_has_colors));
js_set_getter_desc(js, proto, "rows", 4, js_mkfun(tty_write_stream_rows_getter), JS_DESC_E | JS_DESC_C);
js_set_getter_desc(js, proto, "columns", 7, js_mkfun(tty_write_stream_columns_getter), JS_DESC_E | JS_DESC_C);
js_set_sym(js, proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11));
}
void init_tty_module(void) {
ant_t *js = rt->js;
if (!js) return;
jsval_t process_obj = js_get(js, js_glob(js), "process");
if (!is_special_object(process_obj)) return;
jsval_t stdin_obj = js_get(js, process_obj, "stdin");
if (is_special_object(stdin_obj)) {
ensure_stream_common_props(js, stdin_obj, ANT_STDIN_FD);
if (vtype(js_get(js, stdin_obj, "isRaw")) == T_UNDEF) js_set(js, stdin_obj, "isRaw", js_false);
jsval_t stdin_proto = js_get_proto(js, stdin_obj);
if (is_special_object(stdin_proto)) {
jsval_t native_set_raw = js_get(js, stdin_proto, "setRawMode");
if (is_callable(native_set_raw)) {
js_set(js, stdin_proto, "__antNativeSetRawMode", native_set_raw);
}
setup_readstream_proto(js, stdin_proto);
}
}
jsval_t stdout_obj = js_get(js, process_obj, "stdout");
if (is_special_object(stdout_obj)) {
ensure_stream_common_props(js, stdout_obj, ANT_STDOUT_FD);
js_set_getter_desc(js, stdout_obj, "rows", 4, js_mkfun(tty_write_stream_rows_getter), JS_DESC_E | JS_DESC_C);
js_set_getter_desc(js, stdout_obj, "columns", 7, js_mkfun(tty_write_stream_columns_getter), JS_DESC_E | JS_DESC_C);
jsval_t stdout_proto = js_get_proto(js, stdout_obj);
setup_writestream_proto(js, stdout_proto);
}
jsval_t stderr_obj = js_get(js, process_obj, "stderr");
if (is_special_object(stderr_obj)) {
ensure_stream_common_props(js, stderr_obj, ANT_STDERR_FD);
js_set_getter_desc(js, stderr_obj, "rows", 4, js_mkfun(tty_write_stream_rows_getter), JS_DESC_E | JS_DESC_C);
js_set_getter_desc(js, stderr_obj, "columns", 7, js_mkfun(tty_write_stream_columns_getter), JS_DESC_E | JS_DESC_C);
jsval_t stderr_proto = js_get_proto(js, stderr_obj);
setup_writestream_proto(js, stderr_proto);
}
}
jsval_t tty_library(ant_t *js) {
jsval_t lib = js_mkobj(js);
jsval_t read_ctor = js_mkfun(tty_read_stream_constructor);
jsval_t read_proto = js_mkobj(js);
setup_readstream_proto(js, read_proto);
js_set(js, read_proto, "constructor", read_ctor);
js_set(js, read_ctor, "prototype", read_proto);
jsval_t write_ctor = js_mkfun(tty_write_stream_constructor);
jsval_t write_proto = js_mkobj(js);
setup_writestream_proto(js, write_proto);
js_set(js, write_proto, "constructor", write_ctor);
js_set(js, write_ctor, "prototype", write_proto);
js_set(js, lib, "isatty", js_mkfun(tty_isatty));
js_set(js, lib, "ReadStream", read_ctor);
js_set(js, lib, "WriteStream", write_ctor);
js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "tty", 3));
return lib;
}

File Metadata

Mime Type
text/x-c
Expires
Thu, Mar 26, 4:46 PM (1 d, 23 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
511714
Default Alt Text
tty.c (22 KB)

Event Timeline