MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <errno.h>
4#include <limits.h>
5#include <math.h>
6#include <stdio.h>
7#include <string.h>
8
9#ifdef _WIN32
10#include <io.h>
11#define ANT_WRITE_FD _write
12#else
13#include <unistd.h>
14#define ANT_WRITE_FD write
15#endif
16
17#include "tty_ctrl.h"
18#include "internal.h"
19
20bool tty_ctrl_write_fd(int fd, const char *data, size_t len) {
21 if (fd < 0 || !data) return false;
22 if (len == 0) return true;
23
24 size_t off = 0;
25 while (off < len) {
26#ifdef _WIN32
27 size_t rem = len - off;
28 unsigned int chunk = (rem > (size_t)INT_MAX) ? (unsigned int)INT_MAX : (unsigned int)rem;
29 int wrote = ANT_WRITE_FD(fd, data + off, chunk);
30 if (wrote <= 0) return false;
31 off += (size_t)wrote;
32#else
33 ssize_t wrote = ANT_WRITE_FD(fd, data + off, len - off);
34 if (wrote < 0) {
35 if (errno == EINTR) continue;
36 return false;
37 }
38 if (wrote == 0) return false;
39 off += (size_t)wrote;
40#endif
41 }
42
43 return true;
44}
45
46bool tty_ctrl_write_stream(FILE *stream, const char *data, size_t len, bool flush) {
47 if (!stream || !data) return false;
48 if (len > 0 && fwrite(data, 1, len, stream) != len) return false;
49 if (flush && fflush(stream) != 0) return false;
50 return true;
51}
52
53bool tty_ctrl_parse_int_value(ant_value_t value, int *out) {
54 if (vtype(value) != T_NUM) return false;
55 double d = js_getnum(value);
56 if (!isfinite(d)) return false;
57 if (d < (double)INT_MIN || d > (double)INT_MAX) return false;
58 int i = (int)d;
59 if ((double)i != d) return false;
60 *out = i;
61 return true;
62}
63
64int tty_ctrl_normalize_clear_line_dir(int dir) {
65 if (dir < 0) return -1;
66 if (dir > 0) return 1;
67 return 0;
68}
69
70int tty_ctrl_normalize_coord(int value) {
71 return value < 0 ? 0 : value;
72}
73
74bool tty_ctrl_parse_clear_line_dir(ant_value_t *args, int nargs, int dir_index, int *dir_out) {
75 int dir = 0;
76 if (nargs > dir_index && vtype(args[dir_index]) != T_UNDEF) {
77 if (!tty_ctrl_parse_int_value(args[dir_index], &dir)) return false;
78 }
79 *dir_out = tty_ctrl_normalize_clear_line_dir(dir);
80 return true;
81}
82
83bool tty_ctrl_parse_cursor_to_args(
84 ant_value_t *args,
85 int nargs,
86 int x_index,
87 int y_index,
88 tty_ctrl_cursor_to_args_t *out
89) {
90 if (!out || nargs <= x_index) return false;
91
92 int x = 0;
93 if (!tty_ctrl_parse_int_value(args[x_index], &x)) return false;
94 out->x = tty_ctrl_normalize_coord(x);
95 out->has_y = false;
96 out->y = 0;
97
98 if (nargs > y_index && vtype(args[y_index]) != T_UNDEF) {
99 int y = 0;
100 if (!tty_ctrl_parse_int_value(args[y_index], &y)) return false;
101 out->has_y = true;
102 out->y = tty_ctrl_normalize_coord(y);
103 }
104
105 return true;
106}
107
108bool tty_ctrl_parse_move_cursor_args(
109 ant_value_t *args,
110 int nargs,
111 int dx_index,
112 int dy_index,
113 int *dx,
114 int *dy
115) {
116 if (!dx || !dy) return false;
117 if (nargs <= dy_index) return false;
118 if (!tty_ctrl_parse_int_value(args[dx_index], dx)) return false;
119 if (!tty_ctrl_parse_int_value(args[dy_index], dy)) return false;
120 return true;
121}
122
123const char *tty_ctrl_clear_line_seq(int dir, size_t *len_out) {
124 dir = tty_ctrl_normalize_clear_line_dir(dir);
125 const char *seq = "\033[2K\r";
126 if (dir < 0) seq = "\033[1K";
127 else if (dir > 0) seq = "\033[0K";
128
129 if (len_out) *len_out = strlen(seq);
130 return seq;
131}
132
133const char *tty_ctrl_clear_screen_down_seq(size_t *len_out) {
134 static const char seq[] = "\033[0J";
135 if (len_out) *len_out = sizeof(seq) - 1;
136 return seq;
137}
138
139bool tty_ctrl_build_cursor_to(char *buf, size_t buf_size, int x, bool has_y, int y, size_t *len_out) {
140 if (!buf || buf_size == 0) return false;
141
142 int n = 0;
143 if (has_y) n = snprintf(buf, buf_size, "\033[%d;%dH", y + 1, x + 1);
144 else n = snprintf(buf, buf_size, "\033[%dG", x + 1);
145
146 if (n < 0 || (size_t)n >= buf_size) return false;
147 if (len_out) *len_out = (size_t)n;
148 return true;
149}
150
151bool tty_ctrl_build_move_cursor_axis(char *buf, size_t buf_size, int delta, bool horizontal, size_t *len_out) {
152 if (!buf || buf_size == 0) return false;
153
154 if (delta == 0) {
155 if (len_out) *len_out = 0;
156 return true;
157 }
158
159 int amount = (delta < 0) ? -delta : delta;
160 char cmd = 0;
161 if (horizontal) cmd = (delta > 0) ? 'C' : 'D';
162 else cmd = (delta > 0) ? 'B' : 'A';
163
164 int n = snprintf(buf, buf_size, "\033[%d%c", amount, cmd);
165 if (n < 0 || (size_t)n >= buf_size) return false;
166
167 if (len_out) *len_out = (size_t)n;
168 return true;
169}
170
171ant_value_t tty_ctrl_bool_result(ant_t *js, bool ok) {
172 return js_bool(ok);
173}