@amritk/nish-x86_64-linux 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/INSTALL.md +22 -10
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +2 -1
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +44 -0
  22. package/runtime/nish.h +75 -2
  23. package/runtime/nish.mjs +23 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-bytes.c +401 -0
  26. package/runtime/runtime-cross.c +1 -1
  27. package/runtime/runtime-host.c +1 -1
  28. package/runtime/runtime-os.c +1 -1
  29. package/runtime/runtime-parallel.c +1 -1
  30. package/runtime/runtime-simd.c +231 -7
  31. package/runtime/runtime-term.c +504 -0
  32. package/runtime/runtime-wasm.c +0 -11
  33. package/runtime/runtime.c +190 -6
  34. package/runtime/shim.mjs +6 -985
  35. package/runtime/sqlite/LICENSE.md +85 -0
  36. package/runtime/sqlite/README.md +45 -0
  37. package/runtime/sqlite/nish-sqlite.c +359 -0
  38. package/runtime/sqlite/sqlite3.c +271131 -0
  39. package/runtime/sqlite/sqlite3.h +14447 -0
  40. package/scripts/build.sh +261 -53
  41. package/std/LICENSE-fast-float +23 -0
  42. package/std/LICENSE-fdlibm +8 -0
  43. package/std/README.md +31 -11
  44. package/std/base64.ts +116 -0
  45. package/std/bytes.ts +256 -0
  46. package/std/cli.ts +328 -0
  47. package/std/collections.ts +6 -5
  48. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  49. package/std/crypto/base64url.ts +125 -0
  50. package/std/crypto/bcrypt.ts +760 -0
  51. package/std/crypto/hmac.ts +138 -2
  52. package/std/crypto/x509.ts +11 -122
  53. package/std/json-tokenizer.ts +2998 -57
  54. package/std/json-value.ts +22 -3
  55. package/std/json.ts +83 -1
  56. package/std/math.ts +608 -0
  57. package/std/net/http1-server.ts +85 -8
  58. package/std/net/http3-server.ts +50 -12
  59. package/std/net/postgres.ts +1875 -0
  60. package/std/net/quic-listener.ts +20 -4
  61. package/std/regex.ts +4274 -0
  62. package/std/sqlite.ts +391 -0
  63. package/std/term-io.ts +978 -0
  64. package/std/term-pixels.ts +874 -0
  65. package/std/term-probe.ts +459 -0
  66. package/std/term-rate.ts +993 -0
  67. package/std/term.ts +2453 -0
  68. package/std/text.ts +90 -7
  69. package/std/yaml-lines.ts +141 -0
  70. package/std/yaml.ts +4675 -0
@@ -0,0 +1,504 @@
1
+ /* Nish runtime, the terminal half: reading stdin without blocking, waiting
2
+ * for it with a timeout, and the three questions a terminal UI asks of a TTY
3
+ * (is it one, how big is it, and raw mode on or off), the `nish:term` builtin
4
+ * module.
5
+ *
6
+ * A translation unit of its own for the reason runtime-net.c is one: each
7
+ * file carries its own measured `.text*` ceiling in tests/runtime-budgets.js,
8
+ * and runtime-os.c, the other half this could have joined, had 177 bytes of
9
+ * its ceiling left. Section GC means a program that calls none of these pays
10
+ * for none of them, and scripts/build.sh pairs this file with runtime.c like
11
+ * the other halves, so a link line still names one runtime. `waitStdin`
12
+ * reads its clock through runtime-os.c's `nish_monotonic_nanos`, so a program
13
+ * that waits links that function too.
14
+ *
15
+ * Errors take `nish:net`'s shape: an `int32_t` that is `>= 0` for success and
16
+ * a negated errno for a failure. Two codes a program branches on are spelled
17
+ * as numbers, because WASI numbers its errnos differently: -11 is "nothing is
18
+ * waiting" and -25 is "not a terminal", on every platform. Any other failure
19
+ * is the host's own `-errno`.
20
+ *
21
+ * stdin is never made non-blocking. `O_NONBLOCK` is a property of the open
22
+ * file description, which a shell shares with every program it starts, so
23
+ * setting it would leave the shell's own stdin non-blocking after a crash.
24
+ * `readStdin` asks `select` first instead, with a timeout of 0.
25
+ *
26
+ * Raw mode is cfmakeraw's input side without its output side: no echo, no line
27
+ * editing, no translation of CR or NL and no CR dropped, a break and a parity
28
+ * error read as plain bytes, no flow control, 8-bit bytes, a read that answers
29
+ * as soon as one byte is there. `ISIG` stays on, so Ctrl-C is still
30
+ * SIGINT and Ctrl-Z still stops the program, and `OPOST` stays on, so "\n"
31
+ * still moves to the start of the next line. The termios that was there is
32
+ * saved by each `setRawMode(true)` that enters raw mode and put back by
33
+ * `setRawMode(false)`, by an atexit handler, by every panic (runtime.c's
34
+ * `nish_fatal_hook`, because a failed check ends in `_exit`), and by each of
35
+ * the signals POSIX says end a program, SIGKILL aside: SIGINT, SIGTERM and
36
+ * their kin, SIGPIPE from a reader that went away, and the faults (SIGILL,
37
+ * SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV). A handler is installed for one
38
+ * only when the program left it at its default: it restores the terminal, the
39
+ * default comes back (`SA_RESETHAND`), and the signal is raised again, so the
40
+ * program still dies of it and its parent sees that. The handlers run on an
41
+ * alternate stack of the thread that entered raw mode, so a stack overflow
42
+ * there restores the terminal too; one on another thread has no stack to run
43
+ * it on, and the terminal stays raw. SIGTSTP, Ctrl-Z, is caught the same way,
44
+ * because no shell can be counted on for it: bash puts its own termios back
45
+ * while a job is stopped but does not give the job its raw mode back on `fg`,
46
+ * and a shell without that bookkeeping puts back nothing. Its handler restores
47
+ * the terminal, stops the program, and once a shell continues it puts the
48
+ * handler back and raw mode with it.
49
+ * A stop by SIGSTOP, which no program can catch, leaves the terminal raw at a
50
+ * shell that puts nothing back.
51
+ *
52
+ * `setTerminalRestore(bytes)` gives that way out bytes to write to stdout
53
+ * first, such as the escapes that leave an alternate screen or show the
54
+ * cursor again: once, by `write` alone, at exit, on a panic and in each of the
55
+ * handlers that end the program, before the termios comes back. Setting them
56
+ * arms the same handlers, raw mode or not. A Ctrl-Z and `setRawMode(false)`
57
+ * do not end the program, so they write nothing.
58
+ *
59
+ * WASI has `read` and `poll` on stdin and no terminal interface, so there the
60
+ * first two work (`readStdin` waiting, below) and the TTY questions answer
61
+ * "not a terminal": false, `[-25, 0]`, and -25 for entering raw mode and for
62
+ * restore bytes, which are not kept.
63
+ * Otherwise Linux and Darwin only; the file uses nothing else. */
64
+ /* `SA_RESETHAND` is XSI, so _POSIX_C_SOURCE alone does not declare it on
65
+ glibc; Darwin hides `TIOCGWINSZ` behind either unless `_DARWIN_C_SOURCE`
66
+ says otherwise. */
67
+ #define _XOPEN_SOURCE 700
68
+ #if defined(__APPLE__)
69
+ #define _DARWIN_C_SOURCE
70
+ #endif
71
+ #include "nish.h"
72
+
73
+ #include <errno.h>
74
+ #include <string.h>
75
+ #include <unistd.h>
76
+ #ifdef __wasi__
77
+ #include <poll.h>
78
+ #else
79
+ #include <signal.h>
80
+ #include <stdlib.h>
81
+ #include <sys/ioctl.h>
82
+ #include <sys/select.h>
83
+ #include <termios.h>
84
+ #endif
85
+
86
+ #ifndef __wasi__
87
+ /* How many times the SIGTSTP handler has stopped the program, so a wait the
88
+ stop interrupted can tell that from any other signal. Atomic, because the
89
+ handler on one thread counts what a wait on another reads. */
90
+ static int nish_term_stops;
91
+
92
+ static int nish_term_stop_count(void) {
93
+ return __atomic_load_n(&nish_term_stops, __ATOMIC_RELAXED);
94
+ }
95
+ #endif
96
+
97
+ /* 1 when stdin is readable within `timeout_ms` (a negative one waits for
98
+ ever), which includes its end and an error, 0 when it is not or a signal
99
+ ends the wait, and the host's `-errno` when the question fails. A Ctrl-Z
100
+ and `fg` do not end it once `setRawMode(true)` has caught SIGTSTP: no
101
+ system restarts a `select` after a handler, `SA_RESTART` or not, so one the
102
+ handler's stop interrupted is asked again, for what is left until a
103
+ deadline read once from the monotonic clock, since `select` promises
104
+ nothing of its timeout or its set after an error. The first ask is for the
105
+ whole timeout, so a wait reads the clock once up front and again only after
106
+ a stop. A stop that lands between reading the clock and `select` starting
107
+ goes unseen, and that wait runs the time it had left after `fg`; one in the
108
+ last few instructions, after the stop count is read, also has the next
109
+ other signal ask again rather than end the wait. Before raw mode the
110
+ default stop decides: Linux goes on with what is left, Darwin ends the
111
+ wait. Outside WASI this asks `select`, because Darwin's `poll` does not
112
+ support devices: it answers POLLNVAL for a terminal at once, with no input
113
+ there. Under WASI a timeout under 10 ms asks for 10: Node's WASI (uvwasi)
114
+ runs a timer that is due before its first look at the descriptor, and
115
+ counts it from before it set the look up, so a shorter wait answers
116
+ "nothing waiting" while bytes are there whenever setting up took longer
117
+ than the wait, as on a busy machine. */
118
+ static int nish_stdin_ready(int32_t timeout_ms) {
119
+ #ifdef __wasi__
120
+ struct pollfd p = {0, POLLIN, 0};
121
+ int r = poll(&p, 1, timeout_ms < 0 ? -1 : timeout_ms < 10 ? 10 : timeout_ms);
122
+ #else
123
+ fd_set in;
124
+ struct timeval tv;
125
+ int64_t left = timeout_ms * 1000LL;
126
+ int64_t end = timeout_ms > 0 ? nish_monotonic_nanos() + left * 1000 : 0;
127
+ int r, stops;
128
+ for (;;) {
129
+ tv.tv_sec = left / 1000000;
130
+ tv.tv_usec = left % 1000000;
131
+ FD_ZERO(&in);
132
+ FD_SET(0, &in);
133
+ stops = nish_term_stop_count();
134
+ r = select(1, &in, 0, 0, timeout_ms < 0 ? 0 : &tv);
135
+ if (r >= 0 || errno != EINTR || stops == nish_term_stop_count()) break;
136
+ if (timeout_ms > 0) {
137
+ left = (end - nish_monotonic_nanos()) / 1000;
138
+ if (left < 0) left = 0;
139
+ }
140
+ }
141
+ #endif
142
+ if (r < 0) return errno == EINTR ? 0 : -errno;
143
+ return r;
144
+ }
145
+
146
+ /* `readStdin(buf, off, len)`: at most `len` bytes into `buf` from `off`, the
147
+ count, 0 at the end of the stream and -11 when nothing is waiting. The
148
+ compiled call has already checked `0 <= off <= off + len <= buf.length`,
149
+ unless the program was built with `--unchecked-indexing`. A `read` that a
150
+ signal interrupts is asked again; one that would block (a descriptor some
151
+ other program made non-blocking) is -11 too, and so is a readiness check a
152
+ signal interrupts, which would otherwise fall through to a `read` that
153
+ waits. Under WASI the check is left out and the read waits, so there the
154
+ program asks `waitStdin` first. */
155
+ int32_t nish_read_stdin(nish_array *buf, int64_t off, int64_t len) {
156
+ #ifndef __wasi__
157
+ int ready = nish_stdin_ready(0);
158
+ if (ready <= 0) return ready == 0 ? -11 : ready;
159
+ #endif
160
+ ssize_t n;
161
+ do n = read(0, buf->data + off, (size_t)len);
162
+ while (n < 0 && errno == EINTR);
163
+ if (n >= 0) return (int32_t)n;
164
+ return errno == EAGAIN || errno == EWOULDBLOCK ? -11 : -errno;
165
+ }
166
+
167
+ /* `waitStdin(timeoutMs)`: true once stdin is readable, which includes its end
168
+ and an error, so a read after it answers at once; false when `timeoutMs`
169
+ passes first, or a signal ends the wait early: not a Ctrl-Z once
170
+ `setRawMode(true)` has caught SIGTSTP. A negative timeout waits for ever. */
171
+ bool nish_wait_stdin(int32_t timeout_ms) { return nish_stdin_ready(timeout_ms) != 0; }
172
+
173
+ #ifdef __wasi__
174
+ bool nish_is_terminal(int32_t fd) {
175
+ (void)fd;
176
+ return 0;
177
+ }
178
+ nish_array *nish_terminal_size(void) {
179
+ nish_array *out = nish_alloc_array(sizeof(int32_t), 2);
180
+ int32_t *w = (int32_t *)out->data;
181
+ w[0] = -25;
182
+ w[1] = 0;
183
+ return out;
184
+ }
185
+ int32_t nish_set_raw_mode(bool on) { return on ? -25 : 0; }
186
+ int32_t nish_set_terminal_restore(const nish_str *bytes) {
187
+ return bytes->len > NISH_TERM_RESTORE_MAX ? -7 : bytes->len == 0 ? 0 : -25;
188
+ }
189
+ #else
190
+ /* `isTerminal(fd)`: `isatty`. */
191
+ bool nish_is_terminal(int32_t fd) { return isatty(fd) == 1; }
192
+
193
+ /* `terminalSize()`: `[columns, rows]` of the terminal on stdout, or on stderr
194
+ when stdout is not one (the two Node keeps a size for), as a fresh
195
+ two-element `i32[]`; `[-errno, 0]` when neither is, -25 for "not a
196
+ terminal" when either answered so. A terminal no one set a size for
197
+ answers `[0, 0]`. */
198
+ nish_array *nish_terminal_size(void) {
199
+ nish_array *out = nish_alloc_array(sizeof(int32_t), 2);
200
+ int32_t *w = (int32_t *)out->data;
201
+ struct winsize ws;
202
+ int r = ioctl(1, TIOCGWINSZ, &ws);
203
+ int err = errno;
204
+ if (r != 0) r = ioctl(2, TIOCGWINSZ, &ws);
205
+ if (r == 0) {
206
+ w[0] = ws.ws_col;
207
+ w[1] = ws.ws_row;
208
+ } else {
209
+ w[0] = err == ENOTTY || errno == ENOTTY ? -25 : -errno;
210
+ w[1] = 0;
211
+ }
212
+ return out;
213
+ }
214
+
215
+ /* The termios stdin had before raw mode was entered, the raw one entered,
216
+ whether that is in force now, and whether the exit and signal handlers are
217
+ in place. One terminal per process, so one of each. */
218
+ static struct termios nish_term_saved, nish_term_raw_t;
219
+ static volatile sig_atomic_t nish_term_raw, nish_term_entering;
220
+ static int nish_term_armed;
221
+
222
+ /* Held across `setRawMode`, the atexit and panic restores, and the whole
223
+ SIGTSTP handler, stop included, so two threads entering raw mode at once
224
+ save the cooked termios once and arm the handlers once, and no mode is set
225
+ between a Ctrl-Z's restore and its re-entry: the program stops with the
226
+ terminal as it was, and comes back raw. A spin, because the runtime links
227
+ no pthread without `--threads`. A thread holds it only with SIGTSTP blocked,
228
+ so that handler never waits on the thread it interrupted, and every handler
229
+ blocks SIGTSTP too; the other handlers end the program, and never take
230
+ it. */
231
+ static int nish_term_lock;
232
+
233
+ /* Block or unblock SIGTSTP on this thread, `how` as `sigprocmask` takes it.
234
+ `sigprocmask` rather than `pthread_sigmask` for the reason the lock is a
235
+ spin; both Linux and Darwin apply it to the calling thread alone. */
236
+ static void nish_term_mask(int how, sigset_t *old) {
237
+ sigset_t s;
238
+ sigemptyset(&s);
239
+ sigaddset(&s, SIGTSTP);
240
+ sigprocmask(how, &s, old);
241
+ }
242
+
243
+ /* Block SIGTSTP and take the lock, the mask before in `old`. */
244
+ static void nish_term_hold(sigset_t *old) {
245
+ nish_term_mask(SIG_BLOCK, old);
246
+ while (__atomic_exchange_n(&nish_term_lock, 1, __ATOMIC_ACQUIRE)) {
247
+ }
248
+ }
249
+
250
+ static void nish_term_drop(const sigset_t *old) {
251
+ __atomic_store_n(&nish_term_lock, 0, __ATOMIC_RELEASE);
252
+ sigprocmask(SIG_SETMASK, old, 0);
253
+ }
254
+
255
+ /* The bytes `setTerminalRestore` left for the way out, in one of two buffers,
256
+ and which: `nish_term_out` is 0 for none, `len * 2 + buffer` for some, and
257
+ -1 once they went out, after which nothing is stored again. An update
258
+ copies into the buffer not published, under the lock, and publishes by
259
+ swapping the word last, so a signal mid-update writes the old bytes or the
260
+ new ones, never half of each; one update runs at a time, so the buffer a
261
+ writer reads is never the one being filled. `nish_term_next` is the buffer
262
+ the next update fills. */
263
+ static char nish_term_bytes[2][NISH_TERM_RESTORE_MAX];
264
+ static int nish_term_out, nish_term_next;
265
+
266
+ /* Write the published bytes to stdout, once per process: the swap to -1
267
+ takes them, so the atexit handler, a panic and a signal racing each other
268
+ write them once between them. `write` alone, finished across short writes
269
+ and retried when a signal interrupts it; the program ends after it, so
270
+ errno is not kept. */
271
+ static void nish_term_write_out(void) {
272
+ int s = __atomic_exchange_n(&nish_term_out, -1, __ATOMIC_ACQ_REL);
273
+ if (s <= 0) return;
274
+ const char *p = nish_term_bytes[s & 1];
275
+ size_t n = (size_t)(s >> 1);
276
+ while (n > 0) {
277
+ ssize_t w = write(1, p, n);
278
+ if (w < 0 && errno == EINTR) continue;
279
+ if (w <= 0) break;
280
+ p += w;
281
+ n -= (size_t)w;
282
+ }
283
+ }
284
+
285
+ /* Put the saved termios back, once. Called with the lock held, or from a
286
+ handler that ends the program, so it uses only calls that are
287
+ async-signal-safe and needs no Ctrl-Z kept out: SIGTSTP is blocked in both.
288
+ The flag is cleared after the call, so any other signal that lands during it
289
+ restores again, which is harmless, rather than not at all. The panic hook
290
+ stays while restore bytes wait. */
291
+ static void nish_term_restore(void) {
292
+ struct termios now;
293
+ if (nish_term_raw || (nish_term_entering && tcgetattr(0, &now) == 0 && now.c_lflag == nish_term_raw_t.c_lflag &&
294
+ now.c_iflag == nish_term_raw_t.c_iflag)) {
295
+ tcsetattr(0, TCSANOW, &nish_term_saved);
296
+ nish_term_raw = 0;
297
+ if (__atomic_load_n(&nish_term_out, __ATOMIC_ACQUIRE) <= 0)
298
+ __atomic_store_n(&nish_fatal_hook, 0, __ATOMIC_RELEASE);
299
+ }
300
+ }
301
+
302
+ /* The way out of the program: the restore bytes, then the termios. */
303
+ static void nish_term_finish(void) {
304
+ nish_term_write_out();
305
+ nish_term_restore();
306
+ }
307
+
308
+ /* The atexit and panic restore: under the lock, so it waits out a Ctrl-Z
309
+ handler on another thread rather than leave the terminal to its re-entry,
310
+ and with every signal blocked, so one that lands mid-write runs its handler
311
+ once the bytes are out rather than end the program with them half written. */
312
+ static void nish_term_leave(void) {
313
+ sigset_t all, old;
314
+ sigfillset(&all);
315
+ sigprocmask(SIG_BLOCK, &all, &old);
316
+ nish_term_hold(&all);
317
+ nish_term_finish();
318
+ nish_term_drop(&old);
319
+ }
320
+
321
+ /* Enter the raw termios, `when` as `tcsetattr` takes it: 0, or the host's
322
+ `-errno`. Raw once the call has switched, not before: a signal that ends the
323
+ program while it drains the output, or while SIGTTOU holds it stopped in the
324
+ background, finds the terminal as it was and leaves it, rather than setting
325
+ it from the background and being stopped again. */
326
+ static int32_t nish_term_enter(int when) {
327
+ nish_term_entering = 1;
328
+ if (tcsetattr(0, when, &nish_term_raw_t) != 0) {
329
+ nish_term_entering = 0;
330
+ return -errno;
331
+ }
332
+ nish_term_raw = 1;
333
+ nish_term_entering = 0;
334
+ __atomic_store_n(&nish_fatal_hook, nish_term_leave, __ATOMIC_RELEASE);
335
+ return 0;
336
+ }
337
+
338
+ /* Where a fault's handler runs, so a SIGSEGV from an exhausted stack can:
339
+ 64 KiB, past Darwin's MINSIGSTKSZ of 32 KiB and glibc's on any core. */
340
+ static char nish_term_alt_stack[65536];
341
+
342
+ static void nish_term_on_signal(int sig);
343
+
344
+ /* Catch `sig`: once, `SA_RESETHAND`, on the alternate stack, and with the
345
+ signal left unblocked while the handler runs (`SA_NODEFER`), so the
346
+ handler's own `raise` acts at once rather than after it returns, SIGTSTP
347
+ aside. SIGTSTP is blocked in every handler, so a Ctrl-Z cannot stop one
348
+ between its restore and its `raise`, and re-enter raw mode for the program
349
+ to die in. A handler that ends the program blocks every signal, its own
350
+ too, so a second one cannot nest into its write and end the program with
351
+ the bytes half out; its own `raise` then lands as the handler returns.
352
+ SIGTSTP's blocks only SIGTSTP, so a signal still ends a program its stop
353
+ holds. `SA_RESTART`, so a call that a Ctrl-Z and `fg` interrupt goes on
354
+ where the system restarts it. */
355
+ static void nish_term_catch(int sig) {
356
+ struct sigaction sa;
357
+ sa.sa_handler = nish_term_on_signal;
358
+ sa.sa_flags = SA_RESETHAND | SA_ONSTACK | SA_NODEFER | SA_RESTART;
359
+ sigemptyset(&sa.sa_mask);
360
+ if (sig != SIGTSTP) sigfillset(&sa.sa_mask);
361
+ sigaddset(&sa.sa_mask, SIGTSTP);
362
+ sigaction(sig, &sa, 0);
363
+ }
364
+
365
+ /* Restore the terminal and raise `sig` again at its default. `SA_RESETHAND`
366
+ put the default back, except that POSIX lets a system keep the handler for
367
+ SIGILL and SIGTRAP, so it is put back here too: `signal` is
368
+ async-signal-safe. Every signal caught but SIGTSTP ends the program in that
369
+ `raise`. SIGTSTP's handler holds the lock throughout and unblocks SIGTSTP
370
+ for the `raise` alone, which stops the program and returns once a shell
371
+ continues it, in the foreground or, after a SIGTTOU stop in the
372
+ `tcsetattr`, once it is brought there. The handler that ends the program
373
+ takes no lock, because the thread it interrupted may hold it. */
374
+ static void nish_term_on_signal(int sig) {
375
+ if (sig != SIGTSTP) {
376
+ nish_term_finish();
377
+ signal(sig, SIG_DFL);
378
+ raise(sig);
379
+ return;
380
+ }
381
+ int err = errno;
382
+ sigset_t old;
383
+ nish_term_hold(&old);
384
+ int was = nish_term_raw;
385
+ nish_term_restore();
386
+ signal(sig, SIG_DFL);
387
+ __atomic_fetch_add(&nish_term_stops, 1, __ATOMIC_RELAXED);
388
+ nish_term_mask(SIG_UNBLOCK, 0);
389
+ raise(sig);
390
+ nish_term_mask(SIG_BLOCK, 0);
391
+ nish_term_catch(sig);
392
+ if (was) nish_term_enter(TCSANOW);
393
+ nish_term_drop(&old);
394
+ errno = err;
395
+ }
396
+
397
+ /* Put the atexit handler, the alternate stack and the signal handlers in
398
+ place, once, for raw mode and for restore bytes alike. Called with the
399
+ lock held. */
400
+ static void nish_term_arm(void) {
401
+ if (nish_term_armed) return;
402
+ nish_term_armed = 1;
403
+ atexit(nish_term_leave);
404
+ /* An alternate stack the thread already has, from C code the program
405
+ links, is kept. */
406
+ stack_t alt;
407
+ if (sigaltstack(0, &alt) == 0 && (alt.ss_flags & SS_DISABLE)) {
408
+ alt.ss_sp = nish_term_alt_stack;
409
+ alt.ss_size = sizeof nish_term_alt_stack;
410
+ alt.ss_flags = 0;
411
+ sigaltstack(&alt, 0);
412
+ }
413
+ /* Every signal whose default ends the program, the real-time ones and those
414
+ a system adds (Linux's SIGIO, SIGPWR and SIGSTKFLT, Darwin's SIGEMT)
415
+ included, and SIGTSTP; not SIGKILL and SIGSTOP, which no handler sees, nor
416
+ those a default ignores or that stop the program, since the handler raises
417
+ its signal again at the default and a program that went on would go on
418
+ with its terminal restored. Off Linux SIGIO's default ignores it. A signal
419
+ the program or the C it links already handles is left alone, and one the C
420
+ library keeps for itself refuses the `sigaction`. */
421
+ unsigned spare = 1u << SIGCHLD | 1u << SIGCONT | 1u << SIGURG | 1u << SIGWINCH | 1u << SIGTTIN | 1u << SIGTTOU;
422
+ #ifdef SIGINFO
423
+ spare |= 1u << SIGINFO;
424
+ #endif
425
+ #ifndef __linux__
426
+ spare |= 1u << SIGIO;
427
+ #endif
428
+ #ifdef SIGRTMAX
429
+ int last = SIGRTMAX;
430
+ #else
431
+ int last = 31;
432
+ #endif
433
+ for (int sig = 1; sig <= last; sig++) {
434
+ struct sigaction old;
435
+ if ((sig >= 32 || !(spare >> sig & 1)) && sigaction(sig, 0, &old) == 0 && old.sa_handler == SIG_DFL)
436
+ nish_term_catch(sig);
437
+ }
438
+ }
439
+
440
+ /* `setRawMode(on)`: 0, or -25 when stdin is not a terminal and the host's
441
+ `-errno` when `tcsetattr` refuses. Entering flushes the input a line
442
+ discipline had already buffered; leaving keeps it. Asking for the mode the
443
+ terminal is already in changes nothing. A program its shell runs in the
444
+ background is stopped by SIGTTOU here, as any program that sets the
445
+ terminal is, and goes on into raw mode on `fg`. */
446
+ static int32_t nish_term_set(bool on) {
447
+ if (!on) {
448
+ nish_term_restore();
449
+ return 0;
450
+ }
451
+ if (nish_term_raw) return 0;
452
+ if (!isatty(0)) return -25;
453
+ if (tcgetattr(0, &nish_term_saved) != 0) return -errno;
454
+ struct termios *t = &nish_term_raw_t;
455
+ *t = nish_term_saved;
456
+ t->c_iflag &= ~(tcflag_t)(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | INPCK | IXON);
457
+ t->c_cflag = (t->c_cflag & ~(tcflag_t)(CSIZE | PARENB)) | CS8;
458
+ t->c_lflag &= ~(tcflag_t)(ECHO | ECHONL | ICANON | IEXTEN);
459
+ t->c_cc[VMIN] = 1;
460
+ t->c_cc[VTIME] = 0;
461
+ nish_term_arm();
462
+ return nish_term_enter(TCSAFLUSH);
463
+ }
464
+
465
+ int32_t nish_set_raw_mode(bool on) {
466
+ sigset_t old;
467
+ nish_term_hold(&old);
468
+ int32_t r = nish_term_set(on);
469
+ nish_term_drop(&old);
470
+ return r;
471
+ }
472
+
473
+ /* `setTerminalRestore(bytes)`: keep a copy of `bytes` to write to stdout on
474
+ the way out, replacing what was kept; "" keeps nothing. 0, -7 past
475
+ `NISH_TERM_RESTORE_MAX` bytes and -25 when stdout is not a terminal, both
476
+ storing nothing. Once the bytes went out, as the program ends, it stores
477
+ nothing more and answers 0. The way out is each of `setRawMode`'s but
478
+ `setRawMode(false)` and a stop, which do not end the program, so the
479
+ bytes go before the termios and the handlers are put in place here too,
480
+ raw mode or not. */
481
+ int32_t nish_set_terminal_restore(const nish_str *bytes) {
482
+ uint64_t n = bytes->len;
483
+ if (n > NISH_TERM_RESTORE_MAX) return -7;
484
+ if (n > 0 && !isatty(1)) return -25;
485
+ sigset_t old;
486
+ nish_term_hold(&old);
487
+ int cur = __atomic_load_n(&nish_term_out, __ATOMIC_ACQUIRE);
488
+ if (cur >= 0) {
489
+ int next = 0;
490
+ if (n > 0) {
491
+ nish_term_arm();
492
+ memcpy(nish_term_bytes[nish_term_next], bytes->data, n);
493
+ next = (int)(n * 2) + nish_term_next;
494
+ nish_term_next ^= 1;
495
+ __atomic_store_n(&nish_fatal_hook, nish_term_leave, __ATOMIC_RELEASE);
496
+ }
497
+ /* A writer that took the bytes meanwhile left -1, which stays. */
498
+ __atomic_compare_exchange_n(&nish_term_out, &cur, next, 0, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
499
+ if (n == 0 && !nish_term_raw) __atomic_store_n(&nish_fatal_hook, 0, __ATOMIC_RELEASE);
500
+ }
501
+ nish_term_drop(&old);
502
+ return 0;
503
+ }
504
+ #endif
@@ -115,17 +115,6 @@ void nish_array_grow(nish_array *a, uint64_t elem_size) {
115
115
  a->cap = cap;
116
116
  }
117
117
 
118
- /* ---- Math.min and Math.max of a float: llvm.minnum / llvm.maxnum, which wasm32
119
- * has no instruction for. Its `f64.min` answers NaN when either operand is NaN,
120
- * and minnum answers the other operand, so the backend lowers minnum to a libm
121
- * call, and these are that libm. They answer what minnum answers natively: a
122
- * NaN operand is dropped, and two NaNs give NaN (tests/cases/math_minmax_float,
123
- * held to these by tests/run.js). Between `0` and `-0` minnum may answer either. */
124
- double fmin(double a, double b) { return a != a ? b : b != b ? a : a < b ? a : b; }
125
- double fmax(double a, double b) { return a != a ? b : b != b ? a : a > b ? a : b; }
126
- float fminf(float a, float b) { return a != a ? b : b != b ? a : a < b ? a : b; }
127
- float fmaxf(float a, float b) { return a != a ? b : b != b ? a : a > b ? a : b; }
128
-
129
118
  /* ---- Panics: `unreachable`, which the host sees as a RuntimeError */
130
119
  void nish_panic_index(uint64_t idx, uint64_t len) { (void)idx; (void)len; __builtin_trap(); }
131
120
  void nish_panic_slice(int64_t s, int64_t e, int64_t len) { (void)s; (void)e; (void)len; __builtin_trap(); }