MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <stdlib.h>
4#include <stdio.h>
5#include <string.h>
6#include <signal.h>
7#include <time.h>
8#include <uthash.h>
9#include <uv.h>
10
11#ifdef _WIN32
12#define WIN32_LEAN_AND_MEAN
13#include <windows.h>
14#include <io.h>
15#include <psapi.h>
16#define STDIN_FILENO 0
17#define STDOUT_FILENO 1
18#define STDERR_FILENO 2
19#else
20#include <termios.h>
21#include <unistd.h>
22#include <sys/ioctl.h>
23#include <sys/time.h>
24#include <sys/resource.h>
25#include <sys/stat.h>
26#include <grp.h>
27#include <pwd.h>
28#endif
29
30#include "ant.h"
31#include "errors.h"
32#include "output.h"
33#include "utils.h"
34#include "tty_ctrl.h"
35#include "internal.h"
36#include "descriptors.h"
37#include "runtime.h"
38#include "silver/engine.h"
39#include "gc/modules.h"
40
41#include "modules/process.h"
42#include "modules/tty.h"
43#include "modules/symbol.h"
44#include "modules/buffer.h"
45#include "modules/napi.h"
46#include "modules/timer.h"
47#include "modules/string_decoder.h"
48
49#ifndef _WIN32
50extern char **environ;
51#else
52#define environ _environ
53#endif
54
55#define DEFAULT_MAX_LISTENERS 10
56#define INITIAL_LISTENER_CAPACITY 4
57
58typedef struct {
59 ant_value_t listener;
60 bool once;
61} ProcessEventListener;
62
63typedef struct {
64 char *event_type;
65 ProcessEventListener *listeners;
66 int listener_count;
67 int listener_capacity;
68 int emitting;
69 bool free_deferred;
70 UT_hash_handle hh;
71} ProcessEventType;
72
73static int max_listeners = DEFAULT_MAX_LISTENERS;
74static ProcessEventType *process_events = NULL;
75
76typedef struct {
77 uv_signal_t handle;
78 int signum;
79 UT_hash_handle hh;
80} SignalHandle;
81
82static SignalHandle *signal_handles = NULL;
83static ProcessEventType *stdin_events = NULL;
84static ProcessEventType *stdout_events = NULL;
85static ProcessEventType *stderr_events = NULL;
86
87typedef struct {
88 uv_tty_t tty;
89 bool tty_initialized;
90 bool reading;
91 bool keypress_enabled;
92 ant_value_t decoder;
93 int escape_state;
94 int escape_len;
95 char escape_buf[16];
96} stdin_state_t;
97
98static stdin_state_t stdin_state = {0};
99static uint64_t process_start_time = 0;
100
101#ifndef _WIN32
102static uv_signal_t sigwinch_handle;
103static bool sigwinch_initialized = false;
104#endif
105
106typedef struct {
107 const char *name;
108 int signum;
109 UT_hash_handle hh_name;
110 UT_hash_handle hh_num;
111} SignalEntry;
112
113static SignalEntry *signals_by_name = NULL;
114static SignalEntry *signals_by_num = NULL;
115
116static void init_signal_map(void) {
117 static bool initialized = false;
118 if (initialized) return;
119
120 #define S(sig) { #sig, sig, {0}, {0} }
121 static SignalEntry entries[] = {
122 S(SIGINT), S(SIGILL), S(SIGABRT), S(SIGFPE),
123 S(SIGSEGV), S(SIGTERM),
124 #ifdef SIGHUP
125 S(SIGHUP),
126 #endif
127 #ifdef SIGQUIT
128 S(SIGQUIT),
129 #endif
130 #ifdef SIGTRAP
131 S(SIGTRAP),
132 #endif
133 #ifdef SIGKILL
134 S(SIGKILL),
135 #endif
136 #ifdef SIGBUS
137 S(SIGBUS),
138 #endif
139 #ifdef SIGSYS
140 S(SIGSYS),
141 #endif
142 #ifdef SIGPIPE
143 S(SIGPIPE),
144 #endif
145 #ifdef SIGALRM
146 S(SIGALRM),
147 #endif
148 #ifdef SIGURG
149 S(SIGURG),
150 #endif
151 #ifdef SIGSTOP
152 S(SIGSTOP),
153 #endif
154 #ifdef SIGTSTP
155 S(SIGTSTP),
156 #endif
157 #ifdef SIGCONT
158 S(SIGCONT),
159 #endif
160 #ifdef SIGCHLD
161 S(SIGCHLD),
162 #endif
163 #ifdef SIGTTIN
164 S(SIGTTIN),
165 #endif
166 #ifdef SIGTTOU
167 S(SIGTTOU),
168 #endif
169 #ifdef SIGXCPU
170 S(SIGXCPU),
171 #endif
172 #ifdef SIGXFSZ
173 S(SIGXFSZ),
174 #endif
175 #ifdef SIGVTALRM
176 S(SIGVTALRM),
177 #endif
178 #ifdef SIGPROF
179 S(SIGPROF),
180 #endif
181 #ifdef SIGUSR1
182 S(SIGUSR1),
183 #endif
184 #ifdef SIGUSR2
185 S(SIGUSR2),
186 #endif
187 #ifdef SIGEMT
188 S(SIGEMT),
189 #endif
190 #ifdef SIGIO
191 S(SIGIO),
192 #endif
193 #ifdef SIGWINCH
194 S(SIGWINCH),
195 #endif
196 #ifdef SIGINFO
197 S(SIGINFO),
198 #endif
199 #ifdef SIGPWR
200 S(SIGPWR),
201 #endif
202 #ifdef SIGSTKFLT
203 S(SIGSTKFLT),
204 #endif
205 #ifdef SIGPOLL
206 S(SIGPOLL),
207 #endif
208 };
209 #undef S
210
211 size_t count = sizeof(entries) / sizeof(entries[0]);
212 for (size_t i = 0; i < count; i++) {
213 HASH_ADD_KEYPTR(hh_name, signals_by_name, entries[i].name, strlen(entries[i].name), &entries[i]);
214 HASH_ADD(hh_num, signals_by_num, signum, sizeof(int), &entries[i]);
215 }
216
217 initialized = true;
218}
219
220static int get_signal_number(const char *name) {
221 init_signal_map();
222 SignalEntry *entry = NULL;
223 HASH_FIND(hh_name, signals_by_name, name, strlen(name), entry);
224 return entry ? entry->signum : -1;
225}
226
227static const char *get_signal_name(int signum) {
228 init_signal_map();
229 SignalEntry *entry = NULL;
230 HASH_FIND(hh_num, signals_by_num, &signum, sizeof(int), entry);
231 return entry ? entry->name : NULL;
232}
233
234static ProcessEventType *find_or_create_event(ProcessEventType **table, const char *event_type) {
235 ProcessEventType *evt = NULL;
236 HASH_FIND_STR(*table, event_type, evt);
237
238 if (evt == NULL) {
239 evt = malloc(sizeof(ProcessEventType));
240 *evt = (ProcessEventType){
241 .event_type = strdup(event_type),
242 .emitting = 0,
243 .listener_count = 0,
244 .free_deferred = false,
245 .listener_capacity = INITIAL_LISTENER_CAPACITY,
246 .listeners = malloc(sizeof(ProcessEventListener) * INITIAL_LISTENER_CAPACITY),
247 };
248 HASH_ADD_KEYPTR(hh, *table, evt->event_type, strlen(evt->event_type), evt);
249 }
250
251 return evt;
252}
253
254static bool ensure_listener_capacity(ProcessEventType *evt) {
255 if (evt->listener_count >= evt->listener_capacity) {
256 int new_capacity = evt->listener_capacity * 2;
257 ProcessEventListener *new_listeners = realloc(evt->listeners, sizeof(ProcessEventListener) * new_capacity);
258 if (!new_listeners) return false;
259 evt->listeners = new_listeners;
260 evt->listener_capacity = new_capacity;
261 }
262 return true;
263}
264
265static void free_event_type(ProcessEventType **events, ProcessEventType *evt) {
266 if (!evt) return;
267 if (evt->emitting > 0) { evt->free_deferred = true; return; }
268 HASH_DEL(*events, evt);
269
270 free(evt->listeners);
271 free(evt->event_type); free(evt);
272}
273
274static void check_listener_warning(const char *event) {
275 ProcessEventType *evt = NULL;
276 HASH_FIND_STR(process_events, event, evt);
277 if (evt && evt->listener_count == max_listeners) fprintf(stderr,
278 "Warning: Possible EventEmitter memory leak detected. "
279 "%d '%s' listeners added. Use process.setMaxListeners() to increase limit.\n",
280 evt->listener_count, event
281 );
282}
283
284static void uv_signal_handler(uv_signal_t *handle, int signum) {
285 const char *name = get_signal_name(signum);
286 if (name) {
287 ant_value_t sig_arg = js_mkstr(rt->js, name, strlen(name));
288 emit_process_event(name, &sig_arg, 1);
289 }
290}
291
292static void start_signal_watch(int signum) {
293 SignalHandle *sh = NULL;
294 HASH_FIND_INT(signal_handles, &signum, sh);
295 if (sh) return;
296
297 sh = malloc(sizeof(SignalHandle));
298 sh->signum = signum;
299 uv_signal_init(uv_default_loop(), &sh->handle);
300 uv_signal_start(&sh->handle, uv_signal_handler, signum);
301 uv_unref((uv_handle_t *)&sh->handle);
302 HASH_ADD_INT(signal_handles, signum, sh);
303}
304
305static void on_signal_handle_close(uv_handle_t *handle) {
306 SignalHandle *sh = (SignalHandle *)handle;
307 free(sh);
308}
309
310static void stop_signal_watch(int signum) {
311 SignalHandle *sh = NULL;
312 HASH_FIND_INT(signal_handles, &signum, sh);
313 if (!sh) return;
314
315 HASH_DEL(signal_handles, sh);
316 uv_signal_stop(&sh->handle);
317 uv_close((uv_handle_t *)&sh->handle, on_signal_handle_close);
318}
319
320
321void emit_process_event(const char *event_type, ant_value_t *args, int nargs) {
322 if (!rt->js) return;
323
324 ProcessEventType *evt = NULL;
325 HASH_FIND_STR(process_events, event_type, evt);
326
327 if (evt == NULL || evt->listener_count == 0) return;
328 evt->emitting++; int i = 0;
329
330 while (i < evt->listener_count) {
331 ProcessEventListener *listener = &evt->listeners[i];
332 sv_vm_call(rt->js->vm, rt->js, listener->listener, js_mkundef(), args, nargs, NULL, false);
333
334 if (listener->once) {
335 for (int j = i; j < evt->listener_count - 1; j++) {
336 evt->listeners[j] = evt->listeners[j + 1];
337 } evt->listener_count--;
338 } else i++;
339 } evt->emitting--;
340
341 if (evt->listener_count == 0 || evt->free_deferred) {
342 int signum = get_signal_number(event_type);
343 if (signum > 0) stop_signal_watch(signum);
344 if (evt->emitting == 0) free_event_type(&process_events, evt);
345 }
346}
347
348bool process_has_event_listeners(const char *event_type) {
349 ProcessEventType *evt = NULL;
350 if (!event_type) return false;
351 HASH_FIND_STR(process_events, event_type, evt);
352 return evt && evt->listener_count > 0;
353}
354
355static void emit_stdio_event(ProcessEventType **events, const char *event_type, ant_value_t *args, int nargs) {
356 if (!rt->js) return;
357 ProcessEventType *evt = NULL;
358
359 HASH_FIND_STR(*events, event_type, evt);
360 if (evt == NULL || evt->listener_count == 0) return;
361
362 evt->emitting++;
363 int i = 0;
364 while (i < evt->listener_count) {
365 ProcessEventListener *listener = &evt->listeners[i];
366 sv_vm_call(rt->js->vm, rt->js, listener->listener, js_mkundef(), args, nargs, NULL, false);
367 if (listener->once) {
368 for (int j = i; j < evt->listener_count - 1; j++)
369 evt->listeners[j] = evt->listeners[j + 1];
370 evt->listener_count--;
371 } else i++;
372 } evt->emitting--;
373
374 if ((evt->listener_count == 0 || evt->free_deferred) && evt->emitting == 0)
375 free_event_type(events, evt);
376}
377
378static const char *stdin_escape_name(const char *seq, int len) {
379 if (len < 2) return NULL;
380
381 if (seq[0] == '[') {
382 if (seq[1] >= '0' && seq[1] <= '9') {
383 int num = 0;
384 int idx = 1;
385
386 while (idx < len && seq[idx] >= '0' && seq[idx] <= '9') {
387 num = num * 10 + (seq[idx] - '0');
388 idx++;
389 }
390
391 if (idx < len && seq[idx] == '~') {
392 typedef struct {
393 int code;
394 const char *name;
395 } esc_num_map_t;
396
397 static const esc_num_map_t esc_num_map[] = {
398 { 1, "home" },
399 { 2, "insert" },
400 { 3, "delete" },
401 { 4, "end" },
402 { 5, "pageup" },
403 { 6, "pagedown" },
404 { 7, "home" },
405 { 8, "end" },
406 { 15, "f5" },
407 { 17, "f6" },
408 { 18, "f7" },
409 { 19, "f8" },
410 { 20, "f9" },
411 { 21, "f10" },
412 { 23, "f11" },
413 { 24, "f12" },
414 };
415
416 for (size_t i = 0; i < sizeof(esc_num_map) / sizeof(esc_num_map[0]); i++) {
417 if (esc_num_map[i].code == num) return esc_num_map[i].name;
418 }
419 }
420 return NULL;
421 }
422
423 typedef struct {
424 char code;
425 bool needs_tilde;
426 const char *name;
427 } esc_map_t;
428
429 static const esc_map_t esc_map[] = {
430 { 'A', false, "up" },
431 { 'B', false, "down" },
432 { 'C', false, "right" },
433 { 'D', false, "left" },
434 { 'H', false, "home" },
435 { 'F', false, "end" },
436 { 'Z', false, "tab" },
437 { '2', true, "insert" },
438 { '3', true, "delete" },
439 { '5', true, "pageup" },
440 { '6', true, "pagedown" },
441 };
442
443 for (size_t i = 0; i < sizeof(esc_map) / sizeof(esc_map[0]); i++) {
444 if (seq[1] != esc_map[i].code) continue;
445 if (esc_map[i].needs_tilde) {
446 return (len >= 3 && seq[2] == '~') ? esc_map[i].name : NULL;
447 }
448 return esc_map[i].name;
449 }
450 return NULL;
451 }
452
453 if (seq[0] == 'O') {
454 switch (seq[1]) {
455 case 'P': return "f1";
456 case 'Q': return "f2";
457 case 'R': return "f3";
458 case 'S': return "f4";
459 default: return NULL;
460 }
461 }
462
463 return NULL;
464}
465
466static void emit_keypress_event(
467 ant_t *js,
468 const char *str,
469 size_t str_len,
470 const char *name,
471 bool ctrl,
472 bool meta,
473 bool shift,
474 const char *sequence,
475 size_t sequence_len
476) {
477 ant_value_t str_val = js_mkstr(js, str ? str : "", str ? str_len : 0);
478 ant_value_t key_obj = js_mkobj(js);
479
480 if (name) {
481 js_set(js, key_obj, "name", js_mkstr(js, name, strlen(name)));
482 } else {
483 js_set(js, key_obj, "name", js_mkundef());
484 }
485
486 js_set(js, key_obj, "ctrl", js_bool(ctrl));
487 js_set(js, key_obj, "meta", js_bool(meta));
488 js_set(js, key_obj, "shift", js_bool(shift));
489
490 if (sequence) {
491 js_set(js, key_obj, "sequence", js_mkstr(js, sequence, sequence_len));
492 }
493
494 ant_value_t args[2] = { str_val, key_obj };
495 emit_stdio_event(&stdin_events, "keypress", args, 2);
496}
497
498static void process_keypress_data(ant_t *js, const char *data, size_t len) {
499 for (size_t i = 0; i < len; i++) {
500 unsigned char c = (unsigned char)data[i];
501
502 if (stdin_state.escape_state == 1) {
503 stdin_state.escape_buf[stdin_state.escape_len++] = (char)c;
504 if (c == '[' || c == 'O') {
505 stdin_state.escape_state = 2;
506 continue;
507 }
508
509 emit_keypress_event(js, "\x1b", 1, "escape", false, false, false, "\x1b", 1);
510 stdin_state.escape_state = 0;
511 stdin_state.escape_len = 0;
512 }
513
514 if (stdin_state.escape_state == 2) {
515 stdin_state.escape_buf[stdin_state.escape_len++] = (char)c;
516 if ((c >= 'A' && c <= 'Z') || c == '~' || stdin_state.escape_len >= 15) {
517 char sequence[18];
518 size_t seq_len = 0;
519 sequence[seq_len++] = '\x1b';
520 memcpy(sequence + seq_len, stdin_state.escape_buf, (size_t)stdin_state.escape_len);
521 seq_len += (size_t)stdin_state.escape_len;
522
523 const char *name = stdin_escape_name(stdin_state.escape_buf, stdin_state.escape_len);
524 if (!name) name = "escape";
525
526 emit_keypress_event(js, "", 0, name, false, false, false, sequence, seq_len);
527 stdin_state.escape_state = 0;
528 stdin_state.escape_len = 0;
529 }
530 continue;
531 }
532
533 if (c == 27) {
534 stdin_state.escape_state = 1;
535 stdin_state.escape_len = 0;
536 continue;
537 }
538
539 if (c == '\r' || c == '\n') {
540 emit_keypress_event(js, "\r", 1, "return", false, false, false, "\r", 1);
541 continue;
542 }
543
544 if (c == 127 || c == 8) {
545 emit_keypress_event(js, "", 0, "backspace", false, false, false, NULL, 0);
546 continue;
547 }
548
549 if (c == '\t') {
550 emit_keypress_event(js, "\t", 1, "tab", false, false, false, "\t", 1);
551 continue;
552 }
553
554 if (c < 32) {
555 char name_buf[2] = { (char)('a' + c - 1), '\0' };
556 char seq = (char)c;
557 emit_keypress_event(js, &seq, 1, name_buf, true, false, false, &seq, 1);
558 continue;
559 }
560
561 char ch = (char)c;
562 char name_buf[2] = { ch, '\0' };
563 emit_keypress_event(js, &ch, 1, name_buf, false, false, false, &ch, 1);
564 }
565
566 if (stdin_state.escape_state == 1) {
567 emit_keypress_event(js, "\x1b", 1, "escape", false, false, false, "\x1b", 1);
568 stdin_state.escape_state = 0;
569 stdin_state.escape_len = 0;
570 }
571}
572
573static bool remove_listener_from_events(ProcessEventType **events, const char *event, ant_value_t listener) {
574 ProcessEventType *evt = NULL;
575 HASH_FIND_STR(*events, event, evt);
576 if (!evt) return false;
577
578 for (int i = 0; i < evt->listener_count; i++) {
579 if (evt->listeners[i].listener != listener) continue;
580 memmove(
581 &evt->listeners[i], &evt->listeners[i + 1],
582 (size_t)(evt->listener_count - i - 1) * sizeof(ProcessEventListener)
583 );
584
585 if (--evt->listener_count == 0) {
586 free_event_type(events, evt);
587 return true;
588 }
589
590 return false;
591 }
592
593 return false;
594}
595
596static bool stdin_is_tty(void) {
597 return uv_guess_handle(STDIN_FILENO) == UV_TTY;
598}
599
600static bool stdout_is_tty(void) {
601 return uv_guess_handle(STDOUT_FILENO) == UV_TTY;
602}
603
604static bool stderr_is_tty(void) {
605 return uv_guess_handle(STDERR_FILENO) == UV_TTY;
606}
607
608static void get_tty_size(int fd, int *rows, int *cols) {
609 int out_rows = 24, out_cols = 80;
610#ifndef _WIN32
611 struct winsize ws;
612 if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
613 if (ws.ws_row > 0) out_rows = ws.ws_row;
614 if (ws.ws_col > 0) out_cols = ws.ws_col;
615 }
616#else
617 CONSOLE_SCREEN_BUFFER_INFO csbi;
618 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
619 int width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
620 int height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
621 if (height > 0) out_rows = height;
622 if (width > 0) out_cols = width;
623 }
624#endif
625 if (rows) *rows = out_rows;
626 if (cols) *cols = out_cols;
627}
628
629static bool stdin_set_raw_mode(bool enable) {
630 if (!stdin_is_tty()) return false;
631 return tty_set_raw_mode(STDIN_FILENO, enable);
632}
633
634static void stdin_alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
635 (void)handle;
636 buf->base = malloc(suggested_size);
637#ifdef _WIN32
638 buf->len = (ULONG)suggested_size;
639#else
640 buf->len = suggested_size;
641#endif
642}
643
644static void on_stdin_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
645 (void)stream;
646 if (nread > 0 && rt->js) {
647 ArrayBufferData *ab = create_array_buffer_data((size_t)nread);
648 if (ab) memcpy(ab->data, buf->base, (size_t)nread);
649 ant_value_t raw_val = ab
650 ? create_typed_array(rt->js, TYPED_ARRAY_UINT8, ab, 0, (size_t)nread, "Buffer")
651 : js_mkstr(rt->js, buf->base, (size_t)nread);
652 ant_value_t data_val = is_object_type(stdin_state.decoder)
653 ? string_decoder_decode_value(rt->js, stdin_state.decoder, raw_val, false)
654 : raw_val;
655 if (is_err(data_val)) data_val = raw_val;
656 emit_stdio_event(&stdin_events, "data", &data_val, 1);
657 if (stdin_state.keypress_enabled) process_keypress_data(rt->js, buf->base, (size_t)nread);
658 }
659 if (buf->base) free(buf->base);
660}
661
662static void stdin_start_reading(void) {
663 if (stdin_state.reading) return;
664 if (!stdin_state.tty_initialized) {
665 uv_loop_t *loop = uv_default_loop();
666 if (uv_tty_init(loop, &stdin_state.tty, STDIN_FILENO, 1) != 0) return;
667 stdin_state.tty.data = NULL;
668 stdin_state.tty_initialized = true;
669 uv_unref((uv_handle_t *)&stdin_state.tty);
670 }
671 uv_ref((uv_handle_t *)&stdin_state.tty);
672 stdin_state.reading = true;
673 uv_read_start((uv_stream_t *)&stdin_state.tty, stdin_alloc_buffer, on_stdin_read);
674}
675
676static void stdin_stop_reading(void) {
677 if (!stdin_state.reading) return;
678 uv_read_stop((uv_stream_t *)&stdin_state.tty);
679 stdin_state.reading = false;
680 uv_unref((uv_handle_t *)&stdin_state.tty);
681}
682
683#ifndef _WIN32
684static void on_sigwinch(uv_signal_t *handle, int signum) {
685 if (!rt->js) return;
686
687 ant_value_t process_obj = js_get(rt->js, js_glob(rt->js), "process");
688 if (!is_special_object(process_obj)) return;
689
690 ant_value_t stdout_obj = js_get(rt->js, process_obj, "stdout");
691 if (!is_special_object(stdout_obj)) return;
692
693 emit_stdio_event(&stdout_events, "resize", NULL, 0);
694}
695#endif
696
697static void start_sigwinch_handler(void) {
698#ifndef _WIN32
699 if (sigwinch_initialized) return;
700 uv_loop_t *loop = uv_default_loop();
701 if (uv_signal_init(loop, &sigwinch_handle) != 0) return;
702 if (uv_signal_start(&sigwinch_handle, on_sigwinch, SIGWINCH) != 0) {
703 uv_close((uv_handle_t *)&sigwinch_handle, NULL);
704 return;
705 }
706 uv_unref((uv_handle_t *)&sigwinch_handle);
707 sigwinch_initialized = true;
708#endif
709}
710
711static ant_value_t js_stdin_set_raw_mode(ant_t *js, ant_value_t *args, int nargs) {
712 bool enable = nargs > 0 ? js_truthy(js, args[0]) : true;
713 return js_bool(stdin_set_raw_mode(enable));
714}
715
716static ant_value_t js_stdin_set_encoding(ant_t *js, ant_value_t *args, int nargs) {
717 ant_value_t this_obj = js_getthis(js);
718
719 ant_value_t encoding = nargs > 0 && !is_undefined(args[0]) ? args[0] : js_mkstr(js, "utf8", 4);
720 ant_value_t decoder = string_decoder_create(js, encoding);
721 ant_value_t encoding_str = 0;
722
723 if (is_err(decoder)) return decoder;
724 encoding_str = js_tostring_val(js, encoding);
725 if (is_err(encoding_str)) return encoding_str;
726
727 stdin_state.decoder = decoder;
728 js_set(js, this_obj, "encoding", encoding_str);
729
730 return this_obj;
731}
732
733static ant_value_t js_stdin_resume(ant_t *js, ant_value_t *args, int nargs) {
734 (void)args; (void)nargs;
735 stdin_start_reading();
736 return js_getthis(js);
737}
738
739static ant_value_t js_stdin_pause(ant_t *js, ant_value_t *args, int nargs) {
740 (void)args; (void)nargs;
741 stdin_stop_reading();
742 return js_getthis(js);
743}
744
745static ant_value_t js_stdin_on(ant_t *js, ant_value_t *args, int nargs) {
746 ant_value_t this_obj = js_getthis(js);
747 if (nargs < 2) return this_obj;
748
749 char *event = js_getstr(js, args[0], NULL);
750 if (!event || vtype(args[1]) != T_FUNC) return this_obj;
751
752 ProcessEventType *evt = find_or_create_event(&stdin_events, event);
753 if (!ensure_listener_capacity(evt)) return this_obj;
754
755 evt->listeners[evt->listener_count].listener = args[1];
756 evt->listeners[evt->listener_count].once = false;
757 evt->listener_count++;
758
759 if (strcmp(event, "data") == 0) stdin_start_reading();
760
761 return this_obj;
762}
763
764static ant_value_t js_stdin_remove_all_listeners(ant_t *js, ant_value_t *args, int nargs) {
765 ant_value_t this_obj = js_getthis(js);
766
767 if (nargs < 1) {
768 ProcessEventType *evt, *tmp;
769 HASH_ITER(hh, stdin_events, evt, tmp) {
770 free_event_type(&stdin_events, evt);
771 }
772 stdin_stop_reading();
773 return this_obj;
774 }
775
776 char *event = js_getstr(js, args[0], NULL);
777 if (!event) return this_obj;
778
779 ProcessEventType *evt = NULL;
780 HASH_FIND_STR(stdin_events, event, evt);
781 if (evt) free_event_type(&stdin_events, evt);
782 if (strcmp(event, "data") == 0) stdin_stop_reading();
783
784 return this_obj;
785}
786
787static ant_value_t js_stdin_remove_listener(ant_t *js, ant_value_t *args, int nargs) {
788 ant_value_t this_obj = js_getthis(js);
789 if (nargs < 2) return this_obj;
790
791 char *event = js_getstr(js, args[0], NULL);
792 if (!event) return this_obj;
793
794 bool now_empty = remove_listener_from_events(&stdin_events, event, args[1]);
795 if (now_empty && strcmp(event, "data") == 0) stdin_stop_reading();
796
797 return this_obj;
798}
799
800static ant_value_t process_write_stream(ant_t *js, ant_value_t *args, int nargs, FILE *stream, int fd) {
801 if (nargs < 1) return js_false;
802
803 size_t len = 0;
804 char *data = js_getstr(js, args[0], &len);
805 ant_output_stream_t *out = NULL;
806
807 if (!data) return js_false;
808 if (uv_guess_handle(fd) == UV_TTY) {
809 return js_bool(tty_ctrl_write_fd(fd, data, len));
810 }
811
812 out = ant_output_stream(stream);
813 ant_output_stream_begin(out);
814
815 if (!ant_output_stream_append(out, data, len)) return js_false;
816 return ant_output_stream_flush(out) ? js_true : js_false;
817}
818
819static ant_value_t js_stdout_write(ant_t *js, ant_value_t *args, int nargs) {
820 return process_write_stream(js, args, nargs, stdout, STDOUT_FILENO);
821}
822
823static ant_value_t js_stdout_on(ant_t *js, ant_value_t *args, int nargs) {
824 ant_value_t this_obj = js_getthis(js);
825 if (nargs < 2) return this_obj;
826
827 char *event = js_getstr(js, args[0], NULL);
828 if (!event || vtype(args[1]) != T_FUNC) return this_obj;
829
830 ProcessEventType *evt = find_or_create_event(&stdout_events, event);
831 if (!ensure_listener_capacity(evt)) return this_obj;
832
833 evt->listeners[evt->listener_count].listener = args[1];
834 evt->listeners[evt->listener_count].once = false;
835 evt->listener_count++;
836
837 if (strcmp(event, "resize") == 0) start_sigwinch_handler();
838
839 return this_obj;
840}
841
842static ant_value_t js_stdout_once(ant_t *js, ant_value_t *args, int nargs) {
843 ant_value_t this_obj = js_getthis(js);
844 if (nargs < 2) return this_obj;
845
846 char *event = js_getstr(js, args[0], NULL);
847 if (!event || vtype(args[1]) != T_FUNC) return this_obj;
848
849 ProcessEventType *evt = find_or_create_event(&stdout_events, event);
850 if (!ensure_listener_capacity(evt)) return this_obj;
851
852 evt->listeners[evt->listener_count].listener = args[1];
853 evt->listeners[evt->listener_count].once = true;
854 evt->listener_count++;
855
856 if (strcmp(event, "resize") == 0) start_sigwinch_handler();
857
858 return this_obj;
859}
860
861static ant_value_t js_stdout_remove_all_listeners(ant_t *js, ant_value_t *args, int nargs) {
862 ant_value_t this_obj = js_getthis(js);
863
864 if (nargs < 1) {
865 ProcessEventType *evt, *tmp;
866 HASH_ITER(hh, stdout_events, evt, tmp) {
867 free_event_type(&stdout_events, evt);
868 }
869 return this_obj;
870 }
871
872 char *event = js_getstr(js, args[0], NULL);
873 if (!event) return this_obj;
874
875 ProcessEventType *evt = NULL;
876 HASH_FIND_STR(stdout_events, event, evt);
877 if (evt) free_event_type(&stdout_events, evt);
878
879 return this_obj;
880}
881
882static ant_value_t js_stdout_remove_listener(ant_t *js, ant_value_t *args, int nargs) {
883 ant_value_t this_obj = js_getthis(js);
884 if (nargs < 2) return this_obj;
885
886 char *event = js_getstr(js, args[0], NULL);
887 if (!event) return this_obj;
888
889 remove_listener_from_events(&stdout_events, event, args[1]);
890 return this_obj;
891}
892
893static ant_value_t js_stdout_get_window_size(ant_t *js, ant_value_t *args, int nargs) {
894 (void)args; (void)nargs;
895 int rows = 0, cols = 0;
896 get_tty_size(STDOUT_FILENO, &rows, &cols);
897 ant_value_t arr = js_mkarr(js);
898 js_arr_push(js, arr, js_mknum(cols));
899 js_arr_push(js, arr, js_mknum(rows));
900 return arr;
901}
902
903static ant_value_t js_stdout_rows_getter(ant_t *js, ant_value_t *args, int nargs) {
904 (void)args; (void)nargs;
905 int rows = 0, cols = 0;
906 get_tty_size(STDOUT_FILENO, &rows, &cols);
907 return js_mknum(rows);
908}
909
910static ant_value_t js_stdout_columns_getter(ant_t *js, ant_value_t *args, int nargs) {
911 int rows = 0, cols = 0;
912 get_tty_size(STDOUT_FILENO, &rows, &cols);
913 return js_mknum(cols);
914}
915
916static ant_value_t js_stderr_write(ant_t *js, ant_value_t *args, int nargs) {
917 return process_write_stream(js, args, nargs, stderr, STDERR_FILENO);
918}
919
920static ant_value_t js_stderr_on(ant_t *js, ant_value_t *args, int nargs) {
921 ant_value_t this_obj = js_getthis(js);
922 if (nargs < 2) return this_obj;
923
924 char *event = js_getstr(js, args[0], NULL);
925 if (!event || vtype(args[1]) != T_FUNC) return this_obj;
926
927 ProcessEventType *evt = find_or_create_event(&stderr_events, event);
928 if (!ensure_listener_capacity(evt)) return this_obj;
929
930 evt->listeners[evt->listener_count].listener = args[1];
931 evt->listeners[evt->listener_count].once = false;
932 evt->listener_count++;
933
934 return this_obj;
935}
936
937static ant_value_t js_stderr_once(ant_t *js, ant_value_t *args, int nargs) {
938 ant_value_t this_obj = js_getthis(js);
939 if (nargs < 2) return this_obj;
940
941 char *event = js_getstr(js, args[0], NULL);
942 if (!event || vtype(args[1]) != T_FUNC) return this_obj;
943
944 ProcessEventType *evt = find_or_create_event(&stderr_events, event);
945 if (!ensure_listener_capacity(evt)) return this_obj;
946
947 evt->listeners[evt->listener_count].listener = args[1];
948 evt->listeners[evt->listener_count].once = true;
949 evt->listener_count++;
950
951 return this_obj;
952}
953
954static ant_value_t js_stderr_remove_all_listeners(ant_t *js, ant_value_t *args, int nargs) {
955 ant_value_t this_obj = js_getthis(js);
956
957 if (nargs < 1) {
958 ProcessEventType *evt, *tmp;
959 HASH_ITER(hh, stderr_events, evt, tmp) {
960 free_event_type(&stderr_events, evt);
961 }
962 return this_obj;
963 }
964
965 char *event = js_getstr(js, args[0], NULL);
966 if (!event) return this_obj;
967
968 ProcessEventType *evt = NULL;
969 HASH_FIND_STR(stderr_events, event, evt);
970 if (evt) free_event_type(&stderr_events, evt);
971
972 return this_obj;
973}
974
975static ant_value_t js_stderr_remove_listener(ant_t *js, ant_value_t *args, int nargs) {
976 ant_value_t this_obj = js_getthis(js);
977 if (nargs < 2) return this_obj;
978
979 char *event = js_getstr(js, args[0], NULL);
980 if (!event) return this_obj;
981
982 remove_listener_from_events(&stderr_events, event, args[1]);
983 return this_obj;
984}
985
986static ant_value_t process_uptime(ant_t *js, ant_value_t *args, int nargs) {
987 (void)args; (void)nargs;
988 uint64_t now = uv_hrtime();
989 double seconds = (double)(now - process_start_time) / 1e9;
990 return js_mknum(seconds);
991}
992
993static ant_value_t process_hrtime(ant_t *js, ant_value_t *args, int nargs) {
994 uint64_t now = uv_hrtime();
995
996 if (nargs > 0 && vtype(args[0]) == T_ARR) {
997 ant_value_t prev_sec = js_get(js, args[0], "0");
998 ant_value_t prev_nsec = js_get(js, args[0], "1");
999 if (vtype(prev_sec) == T_NUM && vtype(prev_nsec) == T_NUM) {
1000 uint64_t prev = (uint64_t)js_getnum(prev_sec) * 1000000000ULL + (uint64_t)js_getnum(prev_nsec);
1001 now = now - prev;
1002 }
1003 }
1004
1005 ant_value_t arr = js_mkarr(js);
1006
1007 uint64_t secs = now / 1000000000ULL;
1008 uint64_t nsecs = now % 1000000000ULL;
1009
1010 js_arr_push(js, arr, js_mknum((double)secs));
1011 js_arr_push(js, arr, js_mknum((double)nsecs));
1012
1013 return arr;
1014}
1015
1016static ant_value_t process_hrtime_bigint(ant_t *js, ant_value_t *args, int nargs) {
1017 (void)args; (void)nargs;
1018 uint64_t now = uv_hrtime();
1019 char buf[32];
1020 snprintf(buf, sizeof(buf), "%llu", (unsigned long long)now);
1021 return js_mkbigint(js, buf, strlen(buf), false);
1022}
1023
1024static ant_value_t process_memory_usage(ant_t *js, ant_value_t *args, int nargs) {
1025 (void)args; (void)nargs;
1026 ant_value_t obj = js_mkobj(js);
1027
1028 size_t rss = 0;
1029 uv_resident_set_memory(&rss);
1030 js_set(js, obj, "rss", js_mknum((double)rss));
1031
1032#ifdef _WIN32
1033 PROCESS_MEMORY_COUNTERS_EX pmc;
1034 if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) {
1035 js_set(js, obj, "heapTotal", js_mknum((double)pmc.WorkingSetSize));
1036 js_set(js, obj, "heapUsed", js_mknum((double)pmc.PrivateUsage));
1037 } else {
1038 js_set(js, obj, "heapTotal", js_mknum(0));
1039 js_set(js, obj, "heapUsed", js_mknum(0));
1040 }
1041#else
1042 struct rusage usage;
1043 if (getrusage(RUSAGE_SELF, &usage) == 0) {
1044 js_set(js, obj, "heapTotal", js_mknum((double)rss));
1045 js_set(js, obj, "heapUsed", js_mknum((double)rss));
1046 } else {
1047 js_set(js, obj, "heapTotal", js_mknum(0));
1048 js_set(js, obj, "heapUsed", js_mknum(0));
1049 }
1050#endif
1051
1052 js_set(js, obj, "external", js_mknum(0));
1053 js_set(js, obj, "arrayBuffers", js_mknum(0));
1054
1055 return obj;
1056}
1057
1058static ant_value_t process_memory_usage_rss(ant_t *js, ant_value_t *args, int nargs) {
1059 (void)args; (void)nargs;
1060 size_t rss = 0;
1061 uv_resident_set_memory(&rss);
1062 return js_mknum((double)rss);
1063}
1064
1065static ant_value_t process_cpu_usage(ant_t *js, ant_value_t *args, int nargs) {
1066 ant_value_t obj = js_mkobj(js);
1067 uv_rusage_t rusage;
1068
1069 if (uv_getrusage(&rusage) == 0) {
1070 int64_t user_usec = rusage.ru_utime.tv_sec * 1000000LL + rusage.ru_utime.tv_usec;
1071 int64_t sys_usec = rusage.ru_stime.tv_sec * 1000000LL + rusage.ru_stime.tv_usec;
1072
1073 if (nargs > 0 && is_special_object(args[0])) {
1074 ant_value_t prev_user = js_get(js, args[0], "user");
1075 ant_value_t prev_system = js_get(js, args[0], "system");
1076 if (vtype(prev_user) == T_NUM) user_usec -= (int64_t)js_getnum(prev_user);
1077 if (vtype(prev_system) == T_NUM) sys_usec -= (int64_t)js_getnum(prev_system);
1078 }
1079
1080 js_set(js, obj, "user", js_mknum((double)user_usec));
1081 js_set(js, obj, "system", js_mknum((double)sys_usec));
1082 } else {
1083 js_set(js, obj, "user", js_mknum(0));
1084 js_set(js, obj, "system", js_mknum(0));
1085 }
1086
1087 return obj;
1088}
1089
1090static ant_value_t process_kill(ant_t *js, ant_value_t *args, int nargs) {
1091 if (nargs < 1) return js_mkerr(js, "process.kill requires at least 1 argument");
1092 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "pid must be a number");
1093
1094 int pid = (int)js_getnum(args[0]);
1095 int sig = SIGTERM;
1096
1097 if (nargs > 1) {
1098 if (vtype(args[1]) == T_NUM) {
1099 sig = (int)js_getnum(args[1]);
1100 } else if (vtype(args[1]) == T_STR) {
1101 char *sig_name = js_getstr(js, args[1], NULL);
1102 if (sig_name) {
1103 int signum = get_signal_number(sig_name);
1104 if (signum > 0) sig = signum;
1105 else return js_mkerr(js, "Unknown signal");
1106 }
1107 }
1108 }
1109
1110 int result = uv_kill(pid, sig);
1111 if (result != 0) return js_mkerr(js, "Failed to send signal");
1112 return js_true;
1113}
1114
1115static ant_value_t process_abort(ant_params_t) {
1116 abort();
1117 return js_mkundef();
1118}
1119
1120static ant_value_t process_chdir(ant_t *js, ant_value_t *args, int nargs) {
1121 if (nargs < 1) return js_mkerr(js, "process.chdir requires 1 argument");
1122
1123 char *dir = js_getstr(js, args[0], NULL);
1124 if (!dir) return js_mkerr(js, "directory must be a string");
1125
1126 int result = uv_chdir(dir);
1127 if (result != 0) return js_mkerr(js, "ENOENT: no such file or directory, chdir");
1128 return js_mkundef();
1129}
1130
1131static ant_value_t process_umask(ant_t *js, ant_value_t *args, int nargs) {
1132#ifdef _WIN32
1133 (void)args; (void)nargs;
1134 return js_mknum(0);
1135#else
1136 if (nargs > 0 && vtype(args[0]) == T_NUM) {
1137 int new_mask = (int)js_getnum(args[0]);
1138 int old_mask = umask((mode_t)new_mask);
1139 return js_mknum(old_mask);
1140 }
1141 int cur = umask(0);
1142 umask((mode_t)cur);
1143 return js_mknum(cur);
1144#endif
1145}
1146
1147#ifndef _WIN32
1148static ant_value_t process_getuid(ant_t *js, ant_value_t *args, int nargs) {
1149 (void)args; (void)nargs;
1150 return js_mknum((double)getuid());
1151}
1152
1153static ant_value_t process_geteuid(ant_t *js, ant_value_t *args, int nargs) {
1154 (void)args; (void)nargs;
1155 return js_mknum((double)geteuid());
1156}
1157
1158static ant_value_t process_getgid(ant_t *js, ant_value_t *args, int nargs) {
1159 (void)args; (void)nargs;
1160 return js_mknum((double)getgid());
1161}
1162
1163static ant_value_t process_getegid(ant_t *js, ant_value_t *args, int nargs) {
1164 (void)args; (void)nargs;
1165 return js_mknum((double)getegid());
1166}
1167
1168static ant_value_t process_getgroups(ant_t *js, ant_value_t *args, int nargs) {
1169 (void)args; (void)nargs;
1170 int ngroups = getgroups(0, NULL);
1171 if (ngroups < 0) return js_mkarr(js);
1172
1173 gid_t *groups = malloc(sizeof(gid_t) * (size_t)ngroups);
1174 if (!groups) return js_mkarr(js);
1175
1176 ngroups = getgroups(ngroups, groups);
1177 ant_value_t arr = js_mkarr(js);
1178 for (int i = 0; i < ngroups; i++) {
1179 js_arr_push(js, arr, js_mknum((double)groups[i]));
1180 }
1181 free(groups);
1182 return arr;
1183}
1184
1185static ant_value_t process_setuid(ant_t *js, ant_value_t *args, int nargs) {
1186 if (nargs < 1) return js_mkerr(js, "process.setuid requires 1 argument");
1187
1188 uid_t uid;
1189 if (vtype(args[0]) == T_NUM) {
1190 uid = (uid_t)js_getnum(args[0]);
1191 } else if (vtype(args[0]) == T_STR) {
1192 char *name = js_getstr(js, args[0], NULL);
1193 struct passwd *pwd = getpwnam(name);
1194 if (!pwd) return js_mkerr(js, "setuid user not found");
1195 uid = pwd->pw_uid;
1196 } else {
1197 return js_mkerr(js, "uid must be a number or string");
1198 }
1199
1200 if (setuid(uid) != 0) return js_mkerr(js, "setuid failed");
1201 return js_mkundef();
1202}
1203
1204static ant_value_t process_setgid(ant_t *js, ant_value_t *args, int nargs) {
1205 if (nargs < 1) return js_mkerr(js, "process.setgid requires 1 argument");
1206
1207 gid_t gid;
1208 if (vtype(args[0]) == T_NUM) {
1209 gid = (gid_t)js_getnum(args[0]);
1210 } else if (vtype(args[0]) == T_STR) {
1211 char *name = js_getstr(js, args[0], NULL);
1212 struct group *grp = getgrnam(name);
1213 if (!grp) return js_mkerr(js, "setgid group not found");
1214 gid = grp->gr_gid;
1215 } else {
1216 return js_mkerr(js, "gid must be a number or string");
1217 }
1218
1219 if (setgid(gid) != 0) return js_mkerr(js, "setgid failed");
1220 return js_mkundef();
1221}
1222
1223static ant_value_t process_seteuid(ant_t *js, ant_value_t *args, int nargs) {
1224 if (nargs < 1) return js_mkerr(js, "process.seteuid requires 1 argument");
1225
1226 uid_t uid;
1227 if (vtype(args[0]) == T_NUM) {
1228 uid = (uid_t)js_getnum(args[0]);
1229 } else if (vtype(args[0]) == T_STR) {
1230 char *name = js_getstr(js, args[0], NULL);
1231 struct passwd *pwd = getpwnam(name);
1232 if (!pwd) return js_mkerr(js, "seteuid user not found");
1233 uid = pwd->pw_uid;
1234 } else {
1235 return js_mkerr(js, "uid must be a number or string");
1236 }
1237
1238 if (seteuid(uid) != 0) return js_mkerr(js, "seteuid failed");
1239 return js_mkundef();
1240}
1241
1242static ant_value_t process_setegid(ant_t *js, ant_value_t *args, int nargs) {
1243 if (nargs < 1) return js_mkerr(js, "process.setegid requires 1 argument");
1244
1245 gid_t gid;
1246 if (vtype(args[0]) == T_NUM) {
1247 gid = (gid_t)js_getnum(args[0]);
1248 } else if (vtype(args[0]) == T_STR) {
1249 char *name = js_getstr(js, args[0], NULL);
1250 struct group *grp = getgrnam(name);
1251 if (!grp) return js_mkerr(js, "setegid group not found");
1252 gid = grp->gr_gid;
1253 } else {
1254 return js_mkerr(js, "gid must be a number or string");
1255 }
1256
1257 if (setegid(gid) != 0) return js_mkerr(js, "setegid failed");
1258 return js_mkundef();
1259}
1260
1261static ant_value_t process_setgroups(ant_t *js, ant_value_t *args, int nargs) {
1262 if (nargs < 1 || vtype(args[0]) != T_ARR) {
1263 return js_mkerr(js, "process.setgroups requires an array");
1264 }
1265
1266 ant_value_t len_val = js_get(js, args[0], "length");
1267 int len = (int)js_getnum(len_val);
1268
1269 gid_t *groups = malloc(sizeof(gid_t) * (size_t)len);
1270 if (!groups) return js_mkerr(js, "allocation failed");
1271
1272 for (int i = 0; i < len; i++) {
1273 char idx[16];
1274 snprintf(idx, sizeof(idx), "%d", i);
1275 ant_value_t val = js_get(js, args[0], idx);
1276 if (vtype(val) == T_NUM) {
1277 groups[i] = (gid_t)js_getnum(val);
1278 } else if (vtype(val) == T_STR) {
1279 char *name = js_getstr(js, val, NULL);
1280 struct group *grp = getgrnam(name);
1281 if (!grp) { free(groups); return js_mkerr(js, "group not found"); }
1282 groups[i] = grp->gr_gid;
1283 } else {
1284 free(groups);
1285 return js_mkerr(js, "group id must be number or string");
1286 }
1287 }
1288
1289 if (setgroups(len, groups) != 0) {
1290 free(groups);
1291 return js_mkerr(js, "setgroups failed");
1292 }
1293 free(groups);
1294 return js_mkundef();
1295}
1296
1297static ant_value_t process_initgroups(ant_t *js, ant_value_t *args, int nargs) {
1298 if (nargs < 2) return js_mkerr(js, "process.initgroups requires 2 arguments");
1299
1300 char *user = js_getstr(js, args[0], NULL);
1301 if (!user) return js_mkerr(js, "user must be a string");
1302
1303 gid_t gid;
1304 if (vtype(args[1]) == T_NUM) {
1305 gid = (gid_t)js_getnum(args[1]);
1306 } else if (vtype(args[1]) == T_STR) {
1307 char *name = js_getstr(js, args[1], NULL);
1308 struct group *grp = getgrnam(name);
1309 if (!grp) return js_mkerr(js, "group not found");
1310 gid = grp->gr_gid;
1311 } else {
1312 return js_mkerr(js, "gid must be a number or string");
1313 }
1314
1315 if (initgroups(user, gid) != 0) return js_mkerr(js, "initgroups failed");
1316 return js_mkundef();
1317}
1318#endif
1319
1320static ant_value_t env_getter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) {
1321 CSTR_BUF(buf, 256);
1322 char *key_str = CSTR_INIT(buf, key, key_len);
1323 if (!key_str) return js_mkundef();
1324
1325 char *value = getenv(key_str);
1326 cstr_free(&buf);
1327
1328 if (value == NULL) return js_mkundef();
1329 return js_mkstr(js, value, strlen(value));
1330}
1331
1332static bool env_setter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value) {
1333 ant_value_t str_val = coerce_to_str(js, value);
1334 if (is_err(str_val)) return false;
1335 setprop_cstr(js, obj, key, key_len, str_val);
1336
1337 CSTR_BUF(buf, 256);
1338 char *key_str = CSTR_INIT(buf, key, key_len);
1339 if (key_str) {
1340 size_t val_len;
1341 char *val_str = js_getstr(js, str_val, &val_len);
1342 if (val_str) setenv(key_str, val_str, 1);
1343 cstr_free(&buf);
1344 }
1345
1346 return true;
1347}
1348
1349static bool env_deleter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) {
1350 CSTR_BUF(buf, 256);
1351 char *key_str = CSTR_INIT(buf, key, key_len);
1352 if (key_str) { unsetenv(key_str); cstr_free(&buf); }
1353 return true;
1354}
1355
1356static void load_dotenv_file(ant_t *js, ant_value_t env_obj) {
1357 FILE *fp = fopen(".env", "r");
1358 if (fp == NULL) return;
1359
1360 char line[1024];
1361 while (fgets(line, sizeof(line), fp) != NULL) {
1362 size_t len = strlen(line);
1363 if (len > 0 && line[len - 1] == '\n') {
1364 line[len - 1] = '\0';
1365 len--;
1366 }
1367 if (len > 0 && line[len - 1] == '\r') {
1368 line[len - 1] = '\0';
1369 len--;
1370 }
1371
1372 if (len == 0 || line[0] == '#') continue;
1373 char *equals = strchr(line, '=');
1374 if (equals == NULL) continue;
1375
1376 *equals = '\0';
1377 char *key = line;
1378 char *value = equals + 1;
1379
1380 while (*key == ' ' || *key == '\t') key++;
1381 char *key_end = key + strlen(key) - 1;
1382 while (key_end > key && (*key_end == ' ' || *key_end == '\t')) {
1383 *key_end = '\0';
1384 key_end--;
1385 }
1386
1387 while (*value == ' ' || *value == '\t') value++;
1388 char *value_end = value + strlen(value) - 1;
1389 while (value_end > value && (*value_end == ' ' || *value_end == '\t')) {
1390 *value_end = '\0';
1391 value_end--;
1392 }
1393
1394 if (strlen(value) >= 2 &&
1395 ((value[0] == '"' && value[strlen(value) - 1] == '"')
1396 || (value[0] == '\'' && value[strlen(value) - 1] == '\''))) {
1397 value[strlen(value) - 1] = '\0';
1398 value++;
1399 }
1400
1401 js_set(js, env_obj, key, js_mkstr(js, value, strlen(value)));
1402 }
1403
1404 fclose(fp);
1405}
1406
1407static ant_value_t process_exit(ant_t *js, ant_value_t *args, int nargs) {
1408 int code = 0;
1409
1410 if (nargs > 0 && vtype(args[0]) == T_NUM) {
1411 code = (int)js_getnum(args[0]);
1412 }
1413
1414 exit(code);
1415 return js_mkundef();
1416}
1417
1418typedef struct {
1419 char *buf;
1420 size_t pos;
1421 size_t cap;
1422} env_str_ctx;
1423
1424typedef void (*env_iter_cb)(
1425 ant_t *js,
1426 const char *key,
1427 size_t key_len,
1428 const char *value,
1429 size_t val_len,
1430 void *ctx
1431);
1432
1433static void env_foreach(ant_t *js, ant_value_t env_obj, env_iter_cb cb, void *ctx) {
1434 for (char **env = environ; *env != NULL; env++) {
1435 char *entry = *env;
1436 char *equals = strchr(entry, '=');
1437 if (equals == NULL) continue;
1438
1439 size_t key_len = (size_t)(equals - entry);
1440 char *value = equals + 1;
1441 cb(js, entry, key_len, value, strlen(value), ctx);
1442 }
1443
1444 ant_iter_t iter = js_prop_iter_begin(js, env_obj);
1445 const char *key; size_t key_len; ant_value_t value;
1446
1447 while (js_prop_iter_next(&iter, &key, &key_len, &value)) {
1448 if (vtype(value) != T_STR) continue;
1449
1450 CSTR_BUF(buf, 256);
1451 char *key_str = CSTR_INIT(buf, key, key_len);
1452 if (!key_str) continue;
1453
1454 if (getenv(key_str)) {
1455 cstr_free(&buf);
1456 continue;
1457 } cstr_free(&buf);
1458
1459 size_t val_len;
1460 char *val_str = js_getstr(js, value, &val_len);
1461 cb(js, key, key_len, val_str ? val_str : "", val_str ? val_len : 0, ctx);
1462 }
1463
1464 js_prop_iter_end(&iter);
1465}
1466
1467static void env_to_object_cb(ant_t *js, const char *key, size_t key_len, const char *value, size_t val_len, void *ctx) {
1468 ant_value_t obj = *(ant_value_t *)ctx;
1469 CSTR_BUF(buf, 256);
1470 char *key_str = CSTR_INIT(buf, key, key_len);
1471 if (!key_str) return;
1472 js_set(js, obj, key_str, js_mkstr(js, value, val_len));
1473 cstr_free(&buf);
1474}
1475
1476static ant_value_t env_to_object(ant_t *js, ant_value_t *args, int nargs) {
1477 ant_value_t obj = js_mkobj(js);
1478 env_foreach(js, js->this_val, env_to_object_cb, &obj);
1479 return obj;
1480}
1481
1482static void env_tostring_cb(ant_t *js, const char *key, size_t key_len, const char *value, size_t val_len, void *ctx) {
1483 env_str_ctx *c = ctx;
1484 size_t entry_len = key_len + 1 + val_len;
1485
1486 if (c->pos + entry_len + 2 >= c->cap) {
1487 size_t new_cap = c->cap * 2 + entry_len;
1488 char *new_buf = realloc(c->buf, new_cap);
1489 if (!new_buf) return;
1490 c->buf = new_buf;
1491 c->cap = new_cap;
1492 }
1493
1494 if (c->pos > 0) c->buf[c->pos++] = '\n';
1495 memcpy(c->buf + c->pos, key, key_len);
1496 c->pos += key_len;
1497 c->buf[c->pos++] = '=';
1498 memcpy(c->buf + c->pos, value, val_len);
1499 c->pos += val_len;
1500}
1501
1502static ant_value_t env_toString(ant_t *js, ant_value_t *args, int nargs) {
1503 env_str_ctx ctx = { .buf = malloc(4096), .pos = 0, .cap = 4096 };
1504 if (!ctx.buf) return js_mkstr(js, "", 0);
1505
1506 env_foreach(js, js->this_val, env_tostring_cb, &ctx);
1507 ctx.buf[ctx.pos] = '\0';
1508
1509 ant_value_t ret = js_mkstr(js, ctx.buf, ctx.pos);
1510 free(ctx.buf);
1511 return ret;
1512}
1513
1514static void env_keys_cb(ant_t *js, const char *key, size_t key_len, const char *value, size_t val_len, void *ctx) {
1515 ant_value_t arr = *(ant_value_t *)ctx;
1516 js_arr_push(js, arr, js_mkstr(js, key, key_len));
1517}
1518
1519static ant_value_t env_keys(ant_t *js, ant_value_t obj) {
1520 ant_value_t arr = js_mkarr(js);
1521 env_foreach(js, obj, env_keys_cb, &arr);
1522 return arr;
1523}
1524
1525static ant_value_t process_cwd(ant_t *js, ant_value_t *args, int nargs) {
1526 char cwd[4096];
1527 if (getcwd(cwd, sizeof(cwd)) != NULL) {
1528 return js_mkstr(js, cwd, strlen(cwd));
1529 }
1530 return js_mkundef();
1531}
1532
1533static ant_value_t process_add(ant_t *js, ant_value_t *args, int nargs, bool once, bool prepend) {
1534 if (nargs < 2) {
1535 return js_mkerr(js, once ? "process.once requires 2 arguments" : "process.on requires 2 arguments");
1536 }
1537
1538 char *event = js_getstr(js, args[0], NULL);
1539 if (!event) return js_mkerr(js, "event must be a string");
1540
1541 uint8_t listener_type = vtype(args[1]);
1542 if (listener_type != T_FUNC && listener_type != T_CFUNC) {
1543 return js_mkerr(js, "listener must be a function");
1544 }
1545
1546 int signum = get_signal_number(event);
1547 if (signum > 0) start_signal_watch(signum);
1548
1549 ProcessEventType *evt = find_or_create_event(&process_events, event);
1550 if (!ensure_listener_capacity(evt)) return js_mkerr(js, "failed to allocate listener");
1551
1552 if (prepend && evt->listener_count > 0) {
1553 memmove(
1554 &evt->listeners[1],
1555 &evt->listeners[0],
1556 (size_t)evt->listener_count * sizeof(ProcessEventListener)
1557 );
1558 evt->listeners[0].listener = args[1];
1559 evt->listeners[0].once = once;
1560 } else {
1561 evt->listeners[evt->listener_count].listener = args[1];
1562 evt->listeners[evt->listener_count].once = once;
1563 }
1564
1565 evt->listener_count++;
1566 check_listener_warning(event);
1567 return js_get(js, js_glob(js), "process");
1568}
1569
1570static ant_value_t process_on(ant_t *js, ant_value_t *args, int nargs) {
1571 return process_add(js, args, nargs, false, false);
1572}
1573
1574static ant_value_t process_once(ant_t *js, ant_value_t *args, int nargs) {
1575 return process_add(js, args, nargs, true, false);
1576}
1577
1578static ant_value_t process_prepend_listener(ant_t *js, ant_value_t *args, int nargs) {
1579 return process_add(js, args, nargs, false, true);
1580}
1581
1582static ant_value_t process_prepend_once_listener(ant_t *js, ant_value_t *args, int nargs) {
1583 return process_add(js, args, nargs, true, true);
1584}
1585
1586static ant_value_t process_off(ant_t *js, ant_value_t *args, int nargs) {
1587 ant_value_t process_obj = js_get(js, js_glob(js), "process");
1588 if (nargs < 2) return process_obj;
1589
1590 char *event = js_getstr(js, args[0], NULL);
1591 if (!event) return process_obj;
1592
1593 ProcessEventType *evt = NULL;
1594 HASH_FIND_STR(process_events, event, evt);
1595 if (!evt) return process_obj;
1596
1597 for (int i = 0; i < evt->listener_count; i++) {
1598 if (evt->listeners[i].listener == args[1]) {
1599 for (int j = i; j < evt->listener_count - 1; j++) {
1600 evt->listeners[j] = evt->listeners[j + 1];
1601 } evt->listener_count--;
1602 break;
1603 }
1604 }
1605
1606 if (evt->listener_count == 0) {
1607 int signum = get_signal_number(event);
1608 if (signum > 0) stop_signal_watch(signum);
1609 }
1610
1611 return process_obj;
1612}
1613
1614static ant_value_t process_remove_all_listeners(ant_t *js, ant_value_t *args, int nargs) {
1615 ant_value_t process_obj = js_get(js, js_glob(js), "process");
1616
1617 if (nargs > 0 && vtype(args[0]) == T_STR) {
1618 char *event = js_getstr(js, args[0], NULL);
1619 if (event) {
1620 ProcessEventType *evt = NULL;
1621 HASH_FIND_STR(process_events, event, evt);
1622 if (evt) {
1623 int signum = get_signal_number(event);
1624 if (signum > 0) stop_signal_watch(signum);
1625 free_event_type(&process_events, evt);
1626 }
1627 }
1628 } else {
1629 ProcessEventType *evt, *tmp;
1630 HASH_ITER(hh, process_events, evt, tmp) {
1631 int signum = get_signal_number(evt->event_type);
1632 if (signum > 0) stop_signal_watch(signum);
1633 free_event_type(&process_events, evt);
1634 }
1635 }
1636
1637 return process_obj;
1638}
1639
1640static ant_value_t process_emit(ant_t *js, ant_value_t *args, int nargs) {
1641 if (nargs < 1) return js_false;
1642
1643 char *event = js_getstr(js, args[0], NULL);
1644 if (!event) return js_false;
1645
1646 emit_process_event(event, nargs > 1 ? &args[1] : NULL, nargs - 1);
1647 return js_true;
1648}
1649
1650static bool process_is_error_object(ant_value_t value) {
1651 if (is_err(value)) return true;
1652 return is_object_type(value) && js_get_slot(value, SLOT_ERROR_BRAND) == js_true;
1653}
1654
1655static void process_get_warning_options(
1656 ant_t *js, ant_value_t options,
1657 const char **type, const char **code, const char **detail,
1658 ant_offset_t *detail_len
1659) {
1660 if (!is_object_type(options)) return;
1661
1662 ant_value_t type_val = js_get(js, options, "type");
1663 ant_value_t code_val = js_get(js, options, "code");
1664 ant_value_t detail_val = js_get(js, options, "detail");
1665
1666 if (vtype(type_val) == T_STR) *type = js_getstr(js, type_val, NULL);
1667 if (vtype(code_val) == T_STR) *code = js_getstr(js, code_val, NULL);
1668 if (vtype(detail_val) == T_STR) *detail = (const char *)(uintptr_t)vstr(js, detail_val, detail_len);
1669}
1670
1671static ant_value_t process_make_warning_object(
1672 ant_t *js, const char *type, js_cstr_t msg, const char *code,
1673 const char *detail, ant_offset_t detail_len
1674) {
1675 ant_value_t warning_obj = js_mkobj(js);
1676 js_set_proto_init(warning_obj, js_get_ctor_proto(js, "Error", 5));
1677 js_set_slot(warning_obj, SLOT_ERROR_BRAND, js_true);
1678 js_set(js, warning_obj, "name", js_mkstr(js, type, strlen(type)));
1679 js_set(js, warning_obj, "message", js_mkstr(js, msg.ptr, msg.len));
1680
1681 if (code) js_set(js, warning_obj, "code", js_mkstr(js, code, strlen(code)));
1682 if (detail) js_set(js, warning_obj, "detail", js_mkstr(js, detail, detail_len));
1683
1684 js_capture_stack(js, warning_obj);
1685 return warning_obj;
1686}
1687
1688static ant_value_t process_emit_warning(ant_t *js, ant_value_t *args, int nargs) {
1689 if (nargs < 1) return js_mkundef();
1690
1691 ant_value_t warning = args[0];
1692 const char *type = "Warning";
1693 const char *code = NULL;
1694 const char *detail = NULL;
1695 ant_offset_t detail_len = 0;
1696
1697 if (nargs >= 2) {
1698 if (vtype(args[1]) == T_STR) type = js_getstr(js, args[1], NULL);
1699 else process_get_warning_options(js, args[1], &type, &code, &detail, &detail_len);
1700 }
1701
1702 if (nargs >= 3 && vtype(args[2]) == T_STR) code = js_getstr(js, args[2], NULL);
1703
1704 char msg_buf[512];
1705 js_cstr_t msg = {0};
1706 ant_value_t warning_event_arg = warning;
1707 bool is_error = process_is_error_object(warning);
1708
1709 if (is_error) {
1710 ant_value_t warning_obj = js_as_obj(warning);
1711 ant_offset_t prop_len = 0;
1712 const char *name_prop = get_str_prop(js, warning_obj, "name", 4, &prop_len);
1713 if (name_prop) type = name_prop;
1714
1715 const char *message_prop = get_str_prop(js, warning_obj, "message", 7, &prop_len);
1716 msg.ptr = message_prop ? message_prop : "";
1717 msg.len = message_prop ? prop_len : 0;
1718 msg.needs_free = false;
1719
1720 code = get_str_prop(js, warning_obj, "code", 4, NULL);
1721 detail = get_str_prop(js, warning_obj, "detail", 6, &detail_len);
1722 warning_event_arg = warning_obj;
1723 } else {
1724 msg = js_to_cstr(js, warning, msg_buf, sizeof(msg_buf));
1725 warning_event_arg = process_make_warning_object(js, type, msg, code, detail, detail_len);
1726 }
1727
1728 fprintf(stderr, "(%s:%d) ", "ant", (int)getpid());
1729 if (code) fprintf(stderr, "[%s] ", code);
1730
1731 fprintf(stderr, "%s: %.*s\n", type ? type : "Warning", (int)msg.len, msg.ptr);
1732 if (detail) fprintf(stderr, "%.*s\n", (int)detail_len, detail);
1733
1734 emit_process_event("warning", &warning_event_arg, 1);
1735 if (msg.needs_free) free((void *)msg.ptr);
1736
1737 return js_mkundef();
1738}
1739
1740static ant_value_t process_listener_count(ant_t *js, ant_value_t *args, int nargs) {
1741 if (nargs < 1) return js_mknum(0);
1742
1743 char *event = js_getstr(js, args[0], NULL);
1744 if (!event) return js_mknum(0);
1745
1746 ProcessEventType *evt = NULL;
1747 HASH_FIND_STR(process_events, event, evt);
1748
1749 return js_mknum(evt ? evt->listener_count : 0);
1750}
1751
1752static ant_value_t process_listeners_impl(ant_t *js, ant_value_t *args, int nargs, bool raw) {
1753 (void)raw;
1754 ant_value_t result = js_mkarr(js);
1755 if (nargs < 1) return result;
1756
1757 char *event = js_getstr(js, args[0], NULL);
1758 if (!event) return result;
1759
1760 ProcessEventType *evt = NULL;
1761 HASH_FIND_STR(process_events, event, evt);
1762 if (!evt) return result;
1763
1764 for (int i = 0; i < evt->listener_count; i++) {
1765 js_arr_push(js, result, evt->listeners[i].listener);
1766 }
1767
1768 return result;
1769}
1770
1771static ant_value_t process_listeners(ant_t *js, ant_value_t *args, int nargs) {
1772 return process_listeners_impl(js, args, nargs, false);
1773}
1774
1775static ant_value_t process_raw_listeners(ant_t *js, ant_value_t *args, int nargs) {
1776 return process_listeners_impl(js, args, nargs, true);
1777}
1778
1779static ant_value_t process_event_names(ant_t *js, ant_value_t *args, int nargs) {
1780 (void)args;
1781 (void)nargs;
1782 ant_value_t result = js_mkarr(js);
1783 ProcessEventType *evt = NULL;
1784 ProcessEventType *tmp = NULL;
1785
1786 HASH_ITER(hh, process_events, evt, tmp) {
1787 if (evt->listener_count > 0) {
1788 js_arr_push(js, result, js_mkstr(js, evt->event_type, strlen(evt->event_type)));
1789 }
1790 }
1791
1792 return result;
1793}
1794
1795static ant_value_t process_set_max_listeners(ant_t *js, ant_value_t *args, int nargs) {
1796 if (nargs < 1) return js_mkerr(js, "setMaxListeners requires 1 argument");
1797 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "n must be a number");
1798
1799 int n = (int)js_getnum(args[0]);
1800 if (n < 0) return js_mkerr(js, "n must be non-negative");
1801
1802 max_listeners = n;
1803 return js_get(js, js_glob(js), "process");
1804}
1805
1806static ant_value_t process_get_max_listeners(ant_t *js, ant_value_t *args, int nargs) {
1807 return js_mknum(max_listeners);
1808}
1809
1810static ant_value_t process_next_tick(ant_t *js, ant_value_t *args, int nargs) {
1811 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "process.nextTick requires a callback");
1812
1813 ant_value_t cb = args[0];
1814 if (vtype(cb) != T_FUNC && vtype(cb) != T_CFUNC)
1815 return js_mkerr_typed(js, JS_ERR_TYPE, "process.nextTick callback is not a function");
1816
1817 if (nargs <= 1) queue_next_tick(js, cb);
1818 else queue_next_tick_with_args(js, cb, args + 1, nargs - 1);
1819
1820 return js_mkundef();
1821}
1822
1823static void process_set_methods(ant_t *js, ant_value_t obj, bool include_event_methods) {
1824 js_set(js, obj, "exit", js_mkfun(process_exit));
1825 js_set(js, obj, "cwd", js_mkfun(process_cwd));
1826 js_set(js, obj, "chdir", js_mkfun(process_chdir));
1827 js_set(js, obj, "uptime", js_mkfun(process_uptime));
1828 js_set(js, obj, "cpuUsage", js_mkfun(process_cpu_usage));
1829 js_set(js, obj, "kill", js_mkfun(process_kill));
1830 js_set(js, obj, "abort", js_mkfun(process_abort));
1831 js_set(js, obj, "umask", js_mkfun(process_umask));
1832 js_set(js, obj, "nextTick", js_mkfun(process_next_tick));
1833 js_set(js, obj, "emitWarning", js_mkfun(process_emit_warning));
1834 js_set(js, obj, "dlopen", js_mkfun(napi_process_dlopen_js));
1835
1836 ant_value_t mem_usage_fn = js_heavy_mkfun(js, process_memory_usage, js_mkundef());
1837 js_set(js, mem_usage_fn, "rss", js_mkfun(process_memory_usage_rss));
1838 js_set(js, obj, "memoryUsage", mem_usage_fn);
1839
1840 ant_value_t hrtime_fn = js_heavy_mkfun(js, process_hrtime, js_mkundef());
1841 js_set(js, hrtime_fn, "bigint", js_mkfun(process_hrtime_bigint));
1842 js_set(js, obj, "hrtime", hrtime_fn);
1843
1844 if (include_event_methods) {
1845 js_set(js, obj, "on", js_mkfun(process_on));
1846 js_set_exact(js, obj, "addListener", js_get(js, obj, "on"));
1847 js_set(js, obj, "once", js_mkfun(process_once));
1848 js_set(js, obj, "prependListener", js_mkfun(process_prepend_listener));
1849 js_set(js, obj, "prependOnceListener", js_mkfun(process_prepend_once_listener));
1850 js_set(js, obj, "off", js_mkfun(process_off));
1851 js_set_exact(js, obj, "removeListener", js_get(js, obj, "off"));
1852 js_set(js, obj, "removeAllListeners", js_mkfun(process_remove_all_listeners));
1853 js_set(js, obj, "emit", js_mkfun(process_emit));
1854 js_set(js, obj, "listenerCount", js_mkfun(process_listener_count));
1855 js_set(js, obj, "listeners", js_mkfun(process_listeners));
1856 js_set(js, obj, "rawListeners", js_mkfun(process_raw_listeners));
1857 js_set(js, obj, "eventNames", js_mkfun(process_event_names));
1858 js_set(js, obj, "setMaxListeners", js_mkfun(process_set_max_listeners));
1859 js_set(js, obj, "getMaxListeners", js_mkfun(process_get_max_listeners));
1860 }
1861
1862#ifndef _WIN32
1863 js_set(js, obj, "getuid", js_mkfun(process_getuid));
1864 js_set(js, obj, "geteuid", js_mkfun(process_geteuid));
1865 js_set(js, obj, "getgid", js_mkfun(process_getgid));
1866 js_set(js, obj, "getegid", js_mkfun(process_getegid));
1867 js_set(js, obj, "getgroups", js_mkfun(process_getgroups));
1868 js_set(js, obj, "setuid", js_mkfun(process_setuid));
1869 js_set(js, obj, "setgid", js_mkfun(process_setgid));
1870 js_set(js, obj, "seteuid", js_mkfun(process_seteuid));
1871 js_set(js, obj, "setegid", js_mkfun(process_setegid));
1872 js_set(js, obj, "setgroups", js_mkfun(process_setgroups));
1873 js_set(js, obj, "initgroups", js_mkfun(process_initgroups));
1874#endif
1875}
1876
1877ant_value_t process_library(ant_t *js) {
1878 ant_value_t process_obj = js_get(js, js_glob(js), "process");
1879 js_set(js, process_obj, "default", process_obj);
1880 js_set_slot_wb(js, process_obj, SLOT_DEFAULT, process_obj);
1881 return process_obj;
1882}
1883
1884void init_process_module() {
1885 ant_t *js = rt->js;
1886 ant_value_t global = js_glob(js);
1887
1888 stdin_state.decoder = js_mkundef();
1889 process_start_time = uv_hrtime();
1890 ant_value_t process_proto = js_mkobj(js);
1891
1892 process_set_methods(js, process_proto, true);
1893 js_set_sym(js, process_proto, get_toStringTag_sym(), js_mkstr(js, "process", 7));
1894
1895 ant_value_t process_obj = js_mkobj(js);
1896 ant_value_t env_obj = js_mkobj(js);
1897
1898 js_set_proto_init(process_obj, process_proto);
1899 process_set_methods(js, process_obj, false);
1900
1901 load_dotenv_file(js, env_obj);
1902 js_set_keys(env_obj, env_keys);
1903
1904 js_set_getter(env_obj, env_getter);
1905 js_set_setter(env_obj, env_setter);
1906 js_set_deleter(env_obj, env_deleter);
1907
1908 js_set(js, env_obj, "toObject", js_mkfun(env_to_object));
1909 js_set(js, env_obj, "toString", js_mkfun(env_toString));
1910 js_set(js, process_obj, "env", env_obj);
1911
1912 ant_value_t argv_arr = js_mkarr(js);
1913 for (int i = 0; i < rt->argc; i++) {
1914 js_arr_push(js, argv_arr, js_mkstr(js, rt->argv[i], strlen(rt->argv[i])));
1915 }
1916
1917 js_set(js, process_obj, "argv", argv_arr);
1918 js_set(js, process_obj, "execArgv", js_mkarr(js));
1919 js_set(js, process_obj, "argv0", rt->argc > 0 ? js_mkstr(js, rt->argv[0], strlen(rt->argv[0])) : js_mkstr(js, "ant", 3));
1920 js_set(js, process_obj, "execPath", rt->argc > 0 ? js_mkstr(js, rt->argv[0], strlen(rt->argv[0])) : js_mkundef());
1921
1922 js_set(js, process_obj, "pid", js_mknum((double)getpid()));
1923 js_set(js, process_obj, "ppid", js_mknum((double)getppid()));
1924
1925 ant_value_t versions_obj = js_mkobj(js);
1926 js_set(js, versions_obj, "ant", js_mkstr(js, ANT_VERSION, strlen(ANT_VERSION)));
1927 char uv_ver[32];
1928 snprintf(uv_ver, sizeof(uv_ver), "%d.%d.%d", UV_VERSION_MAJOR, UV_VERSION_MINOR, UV_VERSION_PATCH);
1929 js_set(js, versions_obj, "uv", js_mkstr(js, uv_ver, strlen(uv_ver)));
1930 js_set(js, process_obj, "versions", versions_obj);
1931
1932 ant_value_t release_obj = js_mkobj(js);
1933 js_set(js, release_obj, "name", js_mkstr(js, "ant", 3));
1934 js_set(js, process_obj, "release", release_obj);
1935
1936 // process.platform
1937 #if defined(__APPLE__)
1938 js_set(js, process_obj, "platform", js_mkstr(js, "darwin", 6));
1939 #elif defined(__linux__)
1940 js_set(js, process_obj, "platform", js_mkstr(js, "linux", 5));
1941 #elif defined(_WIN32) || defined(_WIN64)
1942 js_set(js, process_obj, "platform", js_mkstr(js, "win32", 5));
1943 #elif defined(__FreeBSD__)
1944 js_set(js, process_obj, "platform", js_mkstr(js, "freebsd", 7));
1945 #else
1946 js_set(js, process_obj, "platform", js_mkstr(js, "unknown", 7));
1947 #endif
1948
1949 // process.arch
1950 #if defined(__x86_64__) || defined(_M_X64)
1951 js_set(js, process_obj, "arch", js_mkstr(js, "x64", 3));
1952 #elif defined(__i386__) || defined(_M_IX86)
1953 js_set(js, process_obj, "arch", js_mkstr(js, "ia32", 4));
1954 #elif defined(__aarch64__) || defined(_M_ARM64)
1955 js_set(js, process_obj, "arch", js_mkstr(js, "arm64", 5));
1956 #elif defined(__arm__) || defined(_M_ARM)
1957 js_set(js, process_obj, "arch", js_mkstr(js, "arm", 3));
1958 #else
1959 js_set(js, process_obj, "arch", js_mkstr(js, "unknown", 7));
1960 #endif
1961
1962 ant_value_t stdin_proto = js_mkobj(js);
1963 js_set(js, stdin_proto, "setRawMode", js_mkfun(js_stdin_set_raw_mode));
1964 js_set(js, stdin_proto, "setEncoding", js_mkfun(js_stdin_set_encoding));
1965 js_set(js, stdin_proto, "resume", js_mkfun(js_stdin_resume));
1966 js_set(js, stdin_proto, "pause", js_mkfun(js_stdin_pause));
1967 js_set(js, stdin_proto, "on", js_mkfun(js_stdin_on));
1968 js_set(js, stdin_proto, "off", js_mkfun(js_stdin_remove_listener));
1969 js_set_exact(js, stdin_proto, "removeListener", js_get(js, stdin_proto, "off"));
1970 js_set(js, stdin_proto, "removeAllListeners", js_mkfun(js_stdin_remove_all_listeners));
1971 js_set_sym(js, stdin_proto, get_toStringTag_sym(), js_mkstr(js, "ReadStream", 10));
1972
1973 ant_value_t stdin_obj = js_mkobj(js);
1974 js_set_proto_init(stdin_obj, stdin_proto);
1975 js_set(js, stdin_obj, "isTTY", js_bool(stdin_is_tty()));
1976 js_set(js, stdin_obj, "encoding", js_mkundef());
1977 js_set(js, process_obj, "stdin", stdin_obj);
1978
1979 ant_value_t stdout_proto = js_mkobj(js);
1980 js_set(js, stdout_proto, "write", js_mkfun(js_stdout_write));
1981 js_set(js, stdout_proto, "on", js_mkfun(js_stdout_on));
1982 js_set(js, stdout_proto, "once", js_mkfun(js_stdout_once));
1983 js_set(js, stdout_proto, "off", js_mkfun(js_stdout_remove_listener));
1984 js_set_exact(js, stdout_proto, "removeListener", js_get(js, stdout_proto, "off"));
1985 js_set(js, stdout_proto, "removeAllListeners", js_mkfun(js_stdout_remove_all_listeners));
1986 js_set(js, stdout_proto, "getWindowSize", js_mkfun(js_stdout_get_window_size));
1987 js_set_sym(js, stdout_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11));
1988
1989 ant_value_t stdout_obj = js_mkobj(js);
1990 js_set_proto_init(stdout_obj, stdout_proto);
1991 js_set(js, stdout_obj, "isTTY", js_bool(stdout_is_tty()));
1992 js_set_getter_desc(js, stdout_obj, "rows", 4, js_mkfun(js_stdout_rows_getter), JS_DESC_E | JS_DESC_C);
1993 js_set_getter_desc(js, stdout_obj, "columns", 7, js_mkfun(js_stdout_columns_getter), JS_DESC_E | JS_DESC_C);
1994 js_set(js, process_obj, "stdout", stdout_obj);
1995
1996 ant_value_t stderr_proto = js_mkobj(js);
1997 js_set(js, stderr_proto, "write", js_mkfun(js_stderr_write));
1998 js_set(js, stderr_proto, "on", js_mkfun(js_stderr_on));
1999 js_set(js, stderr_proto, "once", js_mkfun(js_stderr_once));
2000 js_set(js, stderr_proto, "off", js_mkfun(js_stderr_remove_listener));
2001 js_set_exact(js, stderr_proto, "removeListener", js_get(js, stderr_proto, "off"));
2002 js_set(js, stderr_proto, "removeAllListeners", js_mkfun(js_stderr_remove_all_listeners));
2003 js_set_sym(js, stderr_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11));
2004
2005 ant_value_t stderr_obj = js_mkobj(js);
2006 js_set_proto_init(stderr_obj, stderr_proto);
2007 js_set(js, stderr_obj, "isTTY", js_bool(stderr_is_tty()));
2008 js_set(js, process_obj, "stderr", stderr_obj);
2009
2010 js_set(js, global, "process", process_obj);
2011}
2012
2013
2014bool has_active_stdin(void) {
2015 return stdin_state.reading;
2016}
2017
2018void gc_mark_process(ant_t *js, gc_mark_fn mark) {
2019 ProcessEventType *tables[] = {process_events, stdin_events, stdout_events, stderr_events};
2020 for (int t = 0; t < 4; t++) {
2021 ProcessEventType *evt, *tmp;
2022 HASH_ITER(hh, tables[t], evt, tmp)
2023 for (int i = 0; i < evt->listener_count; i++) mark(js, evt->listeners[i].listener);
2024 }
2025 if (is_object_type(stdin_state.decoder)) mark(js, stdin_state.decoder);
2026}
2027
2028void process_enable_keypress_events(void) {
2029 stdin_state.keypress_enabled = true;
2030 stdin_state.escape_state = 0;
2031 stdin_state.escape_len = 0;
2032}