mt-lang 0.2.0 → 0.2.1

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/docs/index.html +4 -3
  3. data/lib/milk_tea/base.rb +1 -1
  4. data/std/asset_pack.mt +267 -0
  5. data/std/async/libuv_runtime.mt +539 -0
  6. data/std/async/mailbox.mt +191 -0
  7. data/std/async/runtime.mt +87 -0
  8. data/std/async.mt +87 -0
  9. data/std/base64.mt +185 -0
  10. data/std/behavior_tree.mt +396 -0
  11. data/std/binary.mt +314 -0
  12. data/std/binary_heap.mt +157 -0
  13. data/std/bitset.mt +239 -0
  14. data/std/box2d.mt +535 -0
  15. data/std/bytes.mt +46 -0
  16. data/std/c/box2d.mt +1254 -0
  17. data/std/c/cgltf.mt +647 -0
  18. data/std/c/cjson.mt +106 -0
  19. data/std/c/crypto.mt +11 -0
  20. data/std/c/crypto_support.h +51 -0
  21. data/std/c/ctype.mt +19 -0
  22. data/std/c/ctype_bindgen.h +29 -0
  23. data/std/c/curl.mt +1223 -0
  24. data/std/c/enet.mt +473 -0
  25. data/std/c/errno.mt +41 -0
  26. data/std/c/errno_bindgen.h +53 -0
  27. data/std/c/flecs.mt +2945 -0
  28. data/std/c/fs.linux.mt +43 -0
  29. data/std/c/fs.windows.mt +43 -0
  30. data/std/c/fs_support.h +954 -0
  31. data/std/c/gl.mt +2065 -0
  32. data/std/c/gl_registry_helpers.h +10748 -0
  33. data/std/c/glfw.mt +499 -0
  34. data/std/c/libc.mt +167 -0
  35. data/std/c/libuv.mt +1491 -0
  36. data/std/c/math.mt +22 -0
  37. data/std/c/math_bindgen.h +31 -0
  38. data/std/c/miniaudio.mt +3420 -0
  39. data/std/c/pcre2.mt +735 -0
  40. data/std/c/process.mt +58 -0
  41. data/std/c/process_support.h +1147 -0
  42. data/std/c/raygui.mt +1602 -0
  43. data/std/c/raylib.mt +1245 -0
  44. data/std/c/raymath.mt +163 -0
  45. data/std/c/rlgl.mt +434 -0
  46. data/std/c/rpng.mt +60 -0
  47. data/std/c/rres.mt +187 -0
  48. data/std/c/sdl3.mt +4132 -0
  49. data/std/c/sqlite3.mt +951 -0
  50. data/std/c/stb_image.mt +52 -0
  51. data/std/c/stb_image_resize2.mt +128 -0
  52. data/std/c/stb_image_write.mt +19 -0
  53. data/std/c/stb_rect_pack.mt +38 -0
  54. data/std/c/stb_truetype.mt +175 -0
  55. data/std/c/stb_vorbis.mt +31 -0
  56. data/std/c/stdio.mt +90 -0
  57. data/std/c/steamworks.h +5769 -0
  58. data/std/c/steamworks.mt +4771 -0
  59. data/std/c/string.mt +16 -0
  60. data/std/c/string_bindgen.h +36 -0
  61. data/std/c/sync.mt +33 -0
  62. data/std/c/sync_support.h +219 -0
  63. data/std/c/terminal.mt +24 -0
  64. data/std/c/terminal_support.h +408 -0
  65. data/std/c/time.mt +26 -0
  66. data/std/c/tls.mt +24 -0
  67. data/std/c/tls_support.h +626 -0
  68. data/std/c/tracy.mt +53 -0
  69. data/std/c/zlib.mt +20 -0
  70. data/std/c/zlib_support.h +239 -0
  71. data/std/c/zstd.mt +231 -0
  72. data/std/cell.mt +60 -0
  73. data/std/cgltf.mt +115 -0
  74. data/std/cjson.mt +90 -0
  75. data/std/cli.mt +768 -0
  76. data/std/cookie.mt +198 -0
  77. data/std/counter.mt +160 -0
  78. data/std/crypto.mt +74 -0
  79. data/std/cstring.mt +13 -0
  80. data/std/ctype.mt +52 -0
  81. data/std/curl/runtime.mt +261 -0
  82. data/std/curl.mt +38 -0
  83. data/std/deque.mt +348 -0
  84. data/std/encoding.mt +93 -0
  85. data/std/enet.mt +133 -0
  86. data/std/env.mt +41 -0
  87. data/std/errno.mt +47 -0
  88. data/std/flecs.mt +978 -0
  89. data/std/fmt.mt +271 -0
  90. data/std/fs.linux.mt +696 -0
  91. data/std/fs.windows.mt +711 -0
  92. data/std/fsm.mt +267 -0
  93. data/std/gl.mt +2062 -0
  94. data/std/glfw.mt +471 -0
  95. data/std/goap.mt +342 -0
  96. data/std/graph.mt +521 -0
  97. data/std/gzip.mt +77 -0
  98. data/std/hash.mt +429 -0
  99. data/std/http/server.mt +810 -0
  100. data/std/http.mt +1227 -0
  101. data/std/intern.mt +53 -0
  102. data/std/jobs.mt +288 -0
  103. data/std/json.mt +627 -0
  104. data/std/libc.mt +34 -0
  105. data/std/libuv.mt +554 -0
  106. data/std/linear_algebra.mt +243 -0
  107. data/std/linked_map.mt +372 -0
  108. data/std/linked_map_view.mt +49 -0
  109. data/std/linked_set.mt +117 -0
  110. data/std/log.mt +69 -0
  111. data/std/map.mt +418 -0
  112. data/std/math.mt +24 -0
  113. data/std/mem/arena.mt +137 -0
  114. data/std/mem/endian.mt +51 -0
  115. data/std/mem/heap.mt +298 -0
  116. data/std/mem/pool.mt +164 -0
  117. data/std/mem/stack.mt +47 -0
  118. data/std/mem/tracking.mt +119 -0
  119. data/std/miniaudio.mt +1299 -0
  120. data/std/multiset.mt +191 -0
  121. data/std/net/channel.mt +727 -0
  122. data/std/net/clock.mt +237 -0
  123. data/std/net/discovery.mt +277 -0
  124. data/std/net/lobby.mt +813 -0
  125. data/std/net/manager.mt +539 -0
  126. data/std/net/mux.mt +839 -0
  127. data/std/net/nat.mt +122 -0
  128. data/std/net/packet.mt +219 -0
  129. data/std/net/punch.mt +162 -0
  130. data/std/net/rpc.mt +119 -0
  131. data/std/net/session.mt +1157 -0
  132. data/std/net/stun.mt +252 -0
  133. data/std/net/sync.mt +183 -0
  134. data/std/net/turn.mt +459 -0
  135. data/std/net.mt +2944 -0
  136. data/std/oauth2.mt +408 -0
  137. data/std/option.mt +51 -0
  138. data/std/ordered_map.mt +497 -0
  139. data/std/ordered_set.mt +348 -0
  140. data/std/path.mt +368 -0
  141. data/std/pcre2/runtime.mt +35 -0
  142. data/std/pcre2.mt +78 -0
  143. data/std/priority_queue.mt +55 -0
  144. data/std/process.mt +837 -0
  145. data/std/queue.mt +55 -0
  146. data/std/random.mt +146 -0
  147. data/std/raygui.mt +99 -0
  148. data/std/raylib/debug_console.mt +71 -0
  149. data/std/raylib/easing.mt +388 -0
  150. data/std/raylib/packed_assets.mt +255 -0
  151. data/std/raylib/runtime.mt +30 -0
  152. data/std/raylib/tracy_gpu.mt +37 -0
  153. data/std/raylib.mt +1510 -0
  154. data/std/raymath.mt +153 -0
  155. data/std/result.mt +89 -0
  156. data/std/rlgl.mt +268 -0
  157. data/std/rpng.mt +46 -0
  158. data/std/rres.mt +36 -0
  159. data/std/sdl3/runtime.mt +54 -0
  160. data/std/sdl3.mt +1731 -0
  161. data/std/serialize.mt +68 -0
  162. data/std/set.mt +124 -0
  163. data/std/spatial.mt +176 -0
  164. data/std/sqlite3.mt +151 -0
  165. data/std/stack.mt +55 -0
  166. data/std/stb_image.mt +47 -0
  167. data/std/stb_image_resize2.mt +41 -0
  168. data/std/stb_image_write.mt +17 -0
  169. data/std/stb_rect_pack.mt +15 -0
  170. data/std/stb_truetype.mt +77 -0
  171. data/std/stb_vorbis.mt +16 -0
  172. data/std/stdio.mt +88 -0
  173. data/std/steamworks.mt +1542 -0
  174. data/std/str.mt +293 -0
  175. data/std/string.mt +234 -0
  176. data/std/sync.mt +194 -0
  177. data/std/tar.mt +704 -0
  178. data/std/terminal.mt +1002 -0
  179. data/std/testing.mt +266 -0
  180. data/std/thread.mt +120 -0
  181. data/std/time.mt +105 -0
  182. data/std/tls.mt +616 -0
  183. data/std/toml.mt +1310 -0
  184. data/std/tracy.mt +42 -0
  185. data/std/uri.mt +118 -0
  186. data/std/url.mt +372 -0
  187. data/std/vec.mt +433 -0
  188. data/std/zstd.mt +94 -0
  189. metadata +187 -2
@@ -0,0 +1,1147 @@
1
+ #ifndef MT_PROCESS_SUPPORT_H
2
+ #define MT_PROCESS_SUPPORT_H
3
+
4
+ #ifndef _GNU_SOURCE
5
+ #define _GNU_SOURCE
6
+ #endif
7
+
8
+ #include <stdbool.h>
9
+ #include <errno.h>
10
+ #include <fcntl.h>
11
+ #include <signal.h>
12
+ #include <stdint.h>
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+
16
+ #if !defined(_WIN32)
17
+ #include <poll.h>
18
+ #include <sys/ioctl.h>
19
+ #include <sys/wait.h>
20
+ #include <termios.h>
21
+ #include <unistd.h>
22
+ #endif
23
+
24
+ #include <uv.h>
25
+
26
+ typedef struct mt_process_capture_result {
27
+ char* stdout_data;
28
+ uintptr_t stdout_len;
29
+ char* stderr_data;
30
+ uintptr_t stderr_len;
31
+ int64_t exit_status;
32
+ int term_signal;
33
+ } mt_process_capture_result;
34
+
35
+ typedef struct mt_process_error {
36
+ int code;
37
+ char* message_data;
38
+ uintptr_t message_len;
39
+ } mt_process_error;
40
+
41
+ typedef struct mt_process_spawn_handle {
42
+ int pid;
43
+ int stdin_fd;
44
+ int stdout_fd;
45
+ int stderr_fd;
46
+ } mt_process_spawn_handle;
47
+
48
+ typedef struct mt_process_pty_handle {
49
+ int pid;
50
+ int master_fd;
51
+ } mt_process_pty_handle;
52
+
53
+ typedef struct mt_process_read_result {
54
+ bool ready;
55
+ bool closed;
56
+ char* data;
57
+ uintptr_t len;
58
+ } mt_process_read_result;
59
+
60
+ typedef struct mt_process_wait_result {
61
+ bool ready;
62
+ int64_t exit_status;
63
+ int term_signal;
64
+ } mt_process_wait_result;
65
+
66
+ typedef struct mt_process_buffer {
67
+ char* data;
68
+ uintptr_t len;
69
+ uintptr_t capacity;
70
+ } mt_process_buffer;
71
+
72
+ typedef enum mt_process_failure_stage {
73
+ MT_PROCESS_FAILURE_NONE = 0,
74
+ MT_PROCESS_FAILURE_LOOP_INIT = 1,
75
+ MT_PROCESS_FAILURE_PIPE_INIT = 2,
76
+ MT_PROCESS_FAILURE_SPAWN = 3,
77
+ MT_PROCESS_FAILURE_READ = 4,
78
+ MT_PROCESS_FAILURE_LOOP_CLOSE = 5,
79
+ MT_PROCESS_FAILURE_INTERNAL = 6,
80
+ } mt_process_failure_stage;
81
+
82
+ struct mt_process_capture_state;
83
+
84
+ typedef struct mt_process_pipe_state {
85
+ struct mt_process_capture_state* capture;
86
+ mt_process_buffer* buffer;
87
+ uv_pipe_t* handle;
88
+ bool close_requested;
89
+ } mt_process_pipe_state;
90
+
91
+ typedef struct mt_process_capture_state {
92
+ uv_loop_t* loop;
93
+ uv_process_t* process;
94
+ uv_pipe_t* stdout_pipe;
95
+ uv_pipe_t* stderr_pipe;
96
+ mt_process_pipe_state stdout_state;
97
+ mt_process_pipe_state stderr_state;
98
+ mt_process_buffer stdout_buffer;
99
+ mt_process_buffer stderr_buffer;
100
+ int pending_closes;
101
+ int failure_code;
102
+ mt_process_failure_stage failure_stage;
103
+ int64_t exit_status;
104
+ int term_signal;
105
+ bool exited;
106
+ } mt_process_capture_state;
107
+
108
+ static inline void mt_process_reset_capture_result(mt_process_capture_result* result) {
109
+ if (result == NULL) {
110
+ return;
111
+ }
112
+
113
+ result->stdout_data = NULL;
114
+ result->stdout_len = 0;
115
+ result->stderr_data = NULL;
116
+ result->stderr_len = 0;
117
+ result->exit_status = 0;
118
+ result->term_signal = 0;
119
+ }
120
+
121
+ static inline void mt_process_reset_error(mt_process_error* error) {
122
+ if (error == NULL) {
123
+ return;
124
+ }
125
+
126
+ error->code = 0;
127
+ error->message_data = NULL;
128
+ error->message_len = 0;
129
+ }
130
+
131
+ static inline void mt_process_buffer_release(mt_process_buffer* buffer) {
132
+ if (buffer == NULL) {
133
+ return;
134
+ }
135
+
136
+ free(buffer->data);
137
+ buffer->data = NULL;
138
+ buffer->len = 0;
139
+ buffer->capacity = 0;
140
+ }
141
+
142
+ static inline bool mt_process_buffer_append(mt_process_buffer* buffer, const char* data, uintptr_t len) {
143
+ if (len == 0) {
144
+ return true;
145
+ }
146
+ if (buffer == NULL || data == NULL) {
147
+ return false;
148
+ }
149
+ if (buffer->len > UINTPTR_MAX - len) {
150
+ return false;
151
+ }
152
+
153
+ uintptr_t required = buffer->len + len;
154
+ if (required > buffer->capacity) {
155
+ uintptr_t new_capacity = buffer->capacity == 0 ? 256 : buffer->capacity;
156
+ while (new_capacity < required) {
157
+ if (new_capacity > UINTPTR_MAX / 2) {
158
+ new_capacity = required;
159
+ break;
160
+ }
161
+ new_capacity *= 2;
162
+ }
163
+
164
+ char* resized = (char*) realloc(buffer->data, (size_t) new_capacity);
165
+ if (resized == NULL) {
166
+ return false;
167
+ }
168
+
169
+ buffer->data = resized;
170
+ buffer->capacity = new_capacity;
171
+ }
172
+
173
+ memcpy(buffer->data + buffer->len, data, (size_t) len);
174
+ buffer->len = required;
175
+ return true;
176
+ }
177
+
178
+ static inline void mt_process_note_failure(mt_process_capture_state* state, int code, mt_process_failure_stage stage) {
179
+ if (state == NULL || state->failure_code != 0) {
180
+ return;
181
+ }
182
+
183
+ state->failure_code = code;
184
+ state->failure_stage = stage;
185
+ }
186
+
187
+ static inline int mt_process_set_message(mt_process_error* error, int code, const char* prefix, const char* detail) {
188
+ if (error == NULL) {
189
+ return code == 0 ? -1 : code;
190
+ }
191
+
192
+ size_t prefix_len = prefix == NULL ? 0 : strlen(prefix);
193
+ size_t detail_len = detail == NULL ? 0 : strlen(detail);
194
+ size_t separator_len = prefix_len != 0 && detail_len != 0 ? 2 : 0;
195
+ size_t total_len = prefix_len + separator_len + detail_len;
196
+
197
+ char* message = NULL;
198
+ if (total_len != 0) {
199
+ message = (char*) malloc(total_len);
200
+ if (message != NULL) {
201
+ size_t offset = 0;
202
+ if (prefix_len != 0) {
203
+ memcpy(message + offset, prefix, prefix_len);
204
+ offset += prefix_len;
205
+ }
206
+ if (separator_len != 0) {
207
+ memcpy(message + offset, ": ", separator_len);
208
+ offset += separator_len;
209
+ }
210
+ if (detail_len != 0) {
211
+ memcpy(message + offset, detail, detail_len);
212
+ }
213
+ }
214
+ }
215
+
216
+ error->code = code;
217
+ error->message_data = message;
218
+ error->message_len = (uintptr_t) total_len;
219
+ return code == 0 ? -1 : code;
220
+ }
221
+
222
+ static inline int mt_process_set_uv_error(mt_process_error* error, int code, const char* prefix) {
223
+ return mt_process_set_message(error, code, prefix, uv_strerror(code));
224
+ }
225
+
226
+ static void mt_process_capture_pipe_close_cb(uv_handle_t* handle);
227
+ static void mt_process_capture_process_close_cb(uv_handle_t* handle);
228
+
229
+ static inline void mt_process_capture_begin_close_pipe(mt_process_pipe_state* pipe_state) {
230
+ if (pipe_state == NULL || pipe_state->handle == NULL || pipe_state->close_requested) {
231
+ return;
232
+ }
233
+
234
+ pipe_state->close_requested = true;
235
+ uv_read_stop((uv_stream_t*) pipe_state->handle);
236
+ uv_close((uv_handle_t*) pipe_state->handle, mt_process_capture_pipe_close_cb);
237
+ }
238
+
239
+ static void mt_process_capture_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
240
+ (void) handle;
241
+
242
+ size_t buffer_size = suggested_size == 0 ? 1 : suggested_size;
243
+ char* memory = (char*) malloc(buffer_size);
244
+ if (memory == NULL) {
245
+ *buf = uv_buf_init(NULL, 0);
246
+ return;
247
+ }
248
+
249
+ *buf = uv_buf_init(memory, (unsigned int) buffer_size);
250
+ }
251
+
252
+ static void mt_process_capture_pipe_close_cb(uv_handle_t* handle) {
253
+ mt_process_pipe_state* pipe_state = (mt_process_pipe_state*) handle->data;
254
+ if (pipe_state != NULL && pipe_state->capture != NULL) {
255
+ if (pipe_state->capture->pending_closes > 0) {
256
+ pipe_state->capture->pending_closes -= 1;
257
+ }
258
+ pipe_state->handle = NULL;
259
+ }
260
+
261
+ free(handle);
262
+ }
263
+
264
+ static void mt_process_capture_process_close_cb(uv_handle_t* handle) {
265
+ mt_process_capture_state* state = (mt_process_capture_state*) handle->data;
266
+ if (state != NULL) {
267
+ if (state->pending_closes > 0) {
268
+ state->pending_closes -= 1;
269
+ }
270
+ state->process = NULL;
271
+ }
272
+
273
+ free(handle);
274
+ }
275
+
276
+ static void mt_process_capture_read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
277
+ mt_process_pipe_state* pipe_state = (mt_process_pipe_state*) stream->data;
278
+ if (pipe_state == NULL || pipe_state->capture == NULL) {
279
+ if (buf != NULL && buf->base != NULL) {
280
+ free(buf->base);
281
+ }
282
+ return;
283
+ }
284
+
285
+ if (nread > 0) {
286
+ if (!mt_process_buffer_append(pipe_state->buffer, buf->base, (uintptr_t) nread)) {
287
+ mt_process_note_failure(pipe_state->capture, UV_ENOMEM, MT_PROCESS_FAILURE_READ);
288
+ mt_process_capture_begin_close_pipe(pipe_state);
289
+ }
290
+ } else if (nread < 0) {
291
+ if (nread != UV_EOF) {
292
+ mt_process_note_failure(pipe_state->capture, (int) nread, MT_PROCESS_FAILURE_READ);
293
+ }
294
+ mt_process_capture_begin_close_pipe(pipe_state);
295
+ }
296
+
297
+ if (buf != NULL && buf->base != NULL) {
298
+ free(buf->base);
299
+ }
300
+ }
301
+
302
+ static void mt_process_capture_exit_cb(uv_process_t* process, int64_t exit_status, int term_signal) {
303
+ mt_process_capture_state* state = (mt_process_capture_state*) process->data;
304
+ if (state == NULL) {
305
+ return;
306
+ }
307
+
308
+ state->exit_status = exit_status;
309
+ state->term_signal = term_signal;
310
+ state->exited = true;
311
+ uv_close((uv_handle_t*) process, mt_process_capture_process_close_cb);
312
+ }
313
+
314
+ static inline void mt_process_capture_cleanup_before_spawn_failure(mt_process_capture_state* state) {
315
+ if (state == NULL || state->loop == NULL) {
316
+ return;
317
+ }
318
+
319
+ mt_process_capture_begin_close_pipe(&state->stdout_state);
320
+ mt_process_capture_begin_close_pipe(&state->stderr_state);
321
+
322
+ if (state->pending_closes > 0) {
323
+ uv_run(state->loop, UV_RUN_DEFAULT);
324
+ }
325
+
326
+ if (state->process != NULL) {
327
+ free(state->process);
328
+ state->process = NULL;
329
+ }
330
+ }
331
+
332
+ static inline int mt_process_capture_finish(mt_process_capture_state* state, mt_process_capture_result* out_result, mt_process_error* out_error) {
333
+ int close_status = uv_loop_close(state->loop);
334
+ free(state->loop);
335
+ state->loop = NULL;
336
+
337
+ if (close_status != 0) {
338
+ mt_process_buffer_release(&state->stdout_buffer);
339
+ mt_process_buffer_release(&state->stderr_buffer);
340
+ return mt_process_set_uv_error(out_error, close_status, "process loop close failed");
341
+ }
342
+
343
+ if (!state->exited) {
344
+ mt_process_buffer_release(&state->stdout_buffer);
345
+ mt_process_buffer_release(&state->stderr_buffer);
346
+ return mt_process_set_message(out_error, -1, "process exited early", NULL);
347
+ }
348
+
349
+ if (state->failure_code != 0) {
350
+ const char* prefix = "process read failed";
351
+ if (state->failure_stage == MT_PROCESS_FAILURE_INTERNAL) {
352
+ prefix = "process failed";
353
+ }
354
+ mt_process_buffer_release(&state->stdout_buffer);
355
+ mt_process_buffer_release(&state->stderr_buffer);
356
+ return mt_process_set_uv_error(out_error, state->failure_code, prefix);
357
+ }
358
+
359
+ out_result->stdout_data = state->stdout_buffer.data;
360
+ out_result->stdout_len = state->stdout_buffer.len;
361
+ out_result->stderr_data = state->stderr_buffer.data;
362
+ out_result->stderr_len = state->stderr_buffer.len;
363
+ out_result->exit_status = state->exit_status;
364
+ out_result->term_signal = state->term_signal;
365
+
366
+ state->stdout_buffer.data = NULL;
367
+ state->stdout_buffer.len = 0;
368
+ state->stdout_buffer.capacity = 0;
369
+ state->stderr_buffer.data = NULL;
370
+ state->stderr_buffer.len = 0;
371
+ state->stderr_buffer.capacity = 0;
372
+ return 0;
373
+ }
374
+
375
+ static inline int mt_process_capture(const char* file, char** args, char** env, const char* cwd, mt_process_capture_result* out_result, mt_process_error* out_error) {
376
+ mt_process_capture_state state;
377
+ memset(&state, 0, sizeof(state));
378
+ mt_process_reset_capture_result(out_result);
379
+ mt_process_reset_error(out_error);
380
+
381
+ if (file == NULL || args == NULL || args[0] == NULL) {
382
+ return mt_process_set_message(out_error, -1, "process command cannot be empty", NULL);
383
+ }
384
+
385
+ state.loop = (uv_loop_t*) calloc(1, uv_loop_size());
386
+ if (state.loop == NULL) {
387
+ return mt_process_set_message(out_error, UV_ENOMEM, "process loop allocation failed", uv_strerror(UV_ENOMEM));
388
+ }
389
+
390
+ int loop_status = uv_loop_init(state.loop);
391
+ if (loop_status != 0) {
392
+ free(state.loop);
393
+ state.loop = NULL;
394
+ return mt_process_set_uv_error(out_error, loop_status, "process loop init failed");
395
+ }
396
+
397
+ state.stdout_pipe = (uv_pipe_t*) calloc(1, uv_handle_size(UV_NAMED_PIPE));
398
+ state.stderr_pipe = (uv_pipe_t*) calloc(1, uv_handle_size(UV_NAMED_PIPE));
399
+ state.process = (uv_process_t*) calloc(1, uv_handle_size(UV_PROCESS));
400
+ if (state.stdout_pipe == NULL || state.stderr_pipe == NULL || state.process == NULL) {
401
+ mt_process_buffer_release(&state.stdout_buffer);
402
+ mt_process_buffer_release(&state.stderr_buffer);
403
+ mt_process_capture_cleanup_before_spawn_failure(&state);
404
+ return mt_process_set_message(out_error, UV_ENOMEM, "process handle allocation failed", uv_strerror(UV_ENOMEM));
405
+ }
406
+
407
+ state.stdout_state.capture = &state;
408
+ state.stdout_state.buffer = &state.stdout_buffer;
409
+ state.stdout_state.handle = state.stdout_pipe;
410
+ state.stderr_state.capture = &state;
411
+ state.stderr_state.buffer = &state.stderr_buffer;
412
+ state.stderr_state.handle = state.stderr_pipe;
413
+
414
+ int stdout_init = uv_pipe_init(state.loop, state.stdout_pipe, 0);
415
+ if (stdout_init != 0) {
416
+ mt_process_capture_cleanup_before_spawn_failure(&state);
417
+ return mt_process_set_uv_error(out_error, stdout_init, "process stdout pipe init failed");
418
+ }
419
+ state.pending_closes += 1;
420
+ state.stdout_pipe->data = &state.stdout_state;
421
+
422
+ int stderr_init = uv_pipe_init(state.loop, state.stderr_pipe, 0);
423
+ if (stderr_init != 0) {
424
+ mt_process_capture_cleanup_before_spawn_failure(&state);
425
+ return mt_process_set_uv_error(out_error, stderr_init, "process stderr pipe init failed");
426
+ }
427
+ state.pending_closes += 1;
428
+ state.stderr_pipe->data = &state.stderr_state;
429
+
430
+ uv_stdio_container_t stdio[3];
431
+ memset(stdio, 0, sizeof(stdio));
432
+ stdio[0].flags = UV_IGNORE;
433
+ stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
434
+ stdio[1].data.stream = (uv_stream_t*) state.stdout_pipe;
435
+ stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
436
+ stdio[2].data.stream = (uv_stream_t*) state.stderr_pipe;
437
+
438
+ uv_process_options_t options;
439
+ memset(&options, 0, sizeof(options));
440
+ options.exit_cb = mt_process_capture_exit_cb;
441
+ options.file = file;
442
+ options.args = args;
443
+ options.env = env;
444
+ options.cwd = cwd;
445
+ options.stdio_count = 3;
446
+ options.stdio = stdio;
447
+
448
+ state.process->data = &state;
449
+ int spawn_status = uv_spawn(state.loop, state.process, &options);
450
+ if (spawn_status != 0) {
451
+ mt_process_capture_cleanup_before_spawn_failure(&state);
452
+ return mt_process_set_uv_error(out_error, spawn_status, "process spawn failed");
453
+ }
454
+ state.pending_closes += 1;
455
+
456
+ int stdout_read_status = uv_read_start((uv_stream_t*) state.stdout_pipe, mt_process_capture_alloc_cb, mt_process_capture_read_cb);
457
+ if (stdout_read_status != 0) {
458
+ mt_process_note_failure(&state, stdout_read_status, MT_PROCESS_FAILURE_READ);
459
+ mt_process_capture_begin_close_pipe(&state.stdout_state);
460
+ }
461
+
462
+ int stderr_read_status = uv_read_start((uv_stream_t*) state.stderr_pipe, mt_process_capture_alloc_cb, mt_process_capture_read_cb);
463
+ if (stderr_read_status != 0) {
464
+ mt_process_note_failure(&state, stderr_read_status, MT_PROCESS_FAILURE_READ);
465
+ mt_process_capture_begin_close_pipe(&state.stderr_state);
466
+ }
467
+
468
+ uv_run(state.loop, UV_RUN_DEFAULT);
469
+ return mt_process_capture_finish(&state, out_result, out_error);
470
+ }
471
+
472
+ static void mt_process_detached_close_cb(uv_handle_t* handle) {
473
+ free(handle);
474
+ }
475
+
476
+ static inline int mt_process_spawn_detached(const char* file, char** args, char** env, const char* cwd, int* out_pid, mt_process_error* out_error) {
477
+ mt_process_reset_error(out_error);
478
+ if (out_pid != NULL) {
479
+ *out_pid = 0;
480
+ }
481
+
482
+ if (file == NULL || args == NULL || args[0] == NULL) {
483
+ return mt_process_set_message(out_error, -1, "process command cannot be empty", NULL);
484
+ }
485
+
486
+ uv_loop_t* loop = (uv_loop_t*) calloc(1, uv_loop_size());
487
+ if (loop == NULL) {
488
+ return mt_process_set_message(out_error, UV_ENOMEM, "process loop allocation failed", uv_strerror(UV_ENOMEM));
489
+ }
490
+
491
+ int loop_status = uv_loop_init(loop);
492
+ if (loop_status != 0) {
493
+ free(loop);
494
+ return mt_process_set_uv_error(out_error, loop_status, "process loop init failed");
495
+ }
496
+
497
+ uv_process_t* process = (uv_process_t*) calloc(1, uv_handle_size(UV_PROCESS));
498
+ if (process == NULL) {
499
+ int close_status = uv_loop_close(loop);
500
+ free(loop);
501
+ if (close_status != 0) {
502
+ return mt_process_set_uv_error(out_error, close_status, "process loop close failed");
503
+ }
504
+ return mt_process_set_message(out_error, UV_ENOMEM, "process handle allocation failed", uv_strerror(UV_ENOMEM));
505
+ }
506
+
507
+ uv_stdio_container_t stdio[3];
508
+ memset(stdio, 0, sizeof(stdio));
509
+ stdio[0].flags = UV_IGNORE;
510
+ stdio[1].flags = UV_IGNORE;
511
+ stdio[2].flags = UV_IGNORE;
512
+
513
+ uv_process_options_t options;
514
+ memset(&options, 0, sizeof(options));
515
+ options.file = file;
516
+ options.args = args;
517
+ options.env = env;
518
+ options.cwd = cwd;
519
+ options.flags = UV_PROCESS_DETACHED;
520
+ options.stdio_count = 3;
521
+ options.stdio = stdio;
522
+
523
+ int spawn_status = uv_spawn(loop, process, &options);
524
+ if (spawn_status != 0) {
525
+ free(process);
526
+ int close_status = uv_loop_close(loop);
527
+ free(loop);
528
+ if (close_status != 0) {
529
+ return mt_process_set_uv_error(out_error, close_status, "process loop close failed");
530
+ }
531
+ return mt_process_set_uv_error(out_error, spawn_status, "process spawn failed");
532
+ }
533
+
534
+ if (out_pid != NULL) {
535
+ *out_pid = (int) uv_process_get_pid(process);
536
+ }
537
+
538
+ uv_unref((uv_handle_t*) process);
539
+ uv_close((uv_handle_t*) process, mt_process_detached_close_cb);
540
+ uv_run(loop, UV_RUN_DEFAULT);
541
+
542
+ int close_status = uv_loop_close(loop);
543
+ free(loop);
544
+ if (close_status != 0) {
545
+ return mt_process_set_uv_error(out_error, close_status, "process loop close failed");
546
+ }
547
+
548
+ return 0;
549
+ }
550
+
551
+ static inline void mt_process_reset_spawn_handle(mt_process_spawn_handle* handle) {
552
+ if (handle == NULL) {
553
+ return;
554
+ }
555
+
556
+ handle->pid = 0;
557
+ handle->stdin_fd = -1;
558
+ handle->stdout_fd = -1;
559
+ handle->stderr_fd = -1;
560
+ }
561
+
562
+ static inline void mt_process_reset_pty_handle(mt_process_pty_handle* handle) {
563
+ if (handle == NULL) {
564
+ return;
565
+ }
566
+
567
+ handle->pid = 0;
568
+ handle->master_fd = -1;
569
+ }
570
+
571
+ static inline void mt_process_reset_read_result(mt_process_read_result* result) {
572
+ if (result == NULL) {
573
+ return;
574
+ }
575
+
576
+ result->ready = false;
577
+ result->closed = false;
578
+ result->data = NULL;
579
+ result->len = 0;
580
+ }
581
+
582
+ static inline void mt_process_reset_wait_result(mt_process_wait_result* result) {
583
+ if (result == NULL) {
584
+ return;
585
+ }
586
+
587
+ result->ready = false;
588
+ result->exit_status = 0;
589
+ result->term_signal = 0;
590
+ }
591
+
592
+ #if defined(_WIN32)
593
+
594
+ static inline char*** mt_process_environ_accessor(void) {
595
+ return NULL;
596
+ }
597
+
598
+ static inline uintptr_t mt_process_environ_length(void) {
599
+ return 0;
600
+ }
601
+
602
+ static inline int mt_process_spawn_interactive(const char* file, char** args, char** env, const char* cwd, mt_process_spawn_handle* out_handle, mt_process_error* out_error) {
603
+ (void) file;
604
+ (void) args;
605
+ (void) env;
606
+ (void) cwd;
607
+ mt_process_reset_spawn_handle(out_handle);
608
+ mt_process_reset_error(out_error);
609
+ return mt_process_set_message(out_error, -1, "interactive process is not supported on Windows yet", NULL);
610
+ }
611
+
612
+ static inline int mt_process_spawn_pty(const char* file, char** args, char** env, const char* cwd, int columns, int rows, mt_process_pty_handle* out_handle, mt_process_error* out_error) {
613
+ (void) file;
614
+ (void) args;
615
+ (void) env;
616
+ (void) cwd;
617
+ (void) columns;
618
+ (void) rows;
619
+ mt_process_reset_pty_handle(out_handle);
620
+ mt_process_reset_error(out_error);
621
+ return mt_process_set_message(out_error, -1, "pty process is not supported on Windows yet", NULL);
622
+ }
623
+
624
+ static inline int mt_process_read_fd(int fd, int timeout_ms, mt_process_read_result* out_result, mt_process_error* out_error) {
625
+ (void) fd;
626
+ (void) timeout_ms;
627
+ mt_process_reset_read_result(out_result);
628
+ mt_process_reset_error(out_error);
629
+ return mt_process_set_message(out_error, -1, "interactive process I/O is not supported on Windows yet", NULL);
630
+ }
631
+
632
+ static inline int mt_process_write_fd(int fd, const char* data, uintptr_t len, uintptr_t* out_written, mt_process_error* out_error) {
633
+ (void) fd;
634
+ (void) data;
635
+ (void) len;
636
+ mt_process_reset_error(out_error);
637
+ if (out_written != NULL) {
638
+ *out_written = 0;
639
+ }
640
+ return mt_process_set_message(out_error, -1, "interactive process I/O is not supported on Windows yet", NULL);
641
+ }
642
+
643
+ static inline int mt_process_close_fd(int fd, mt_process_error* out_error) {
644
+ (void) fd;
645
+ mt_process_reset_error(out_error);
646
+ return mt_process_set_message(out_error, -1, "interactive process I/O is not supported on Windows yet", NULL);
647
+ }
648
+
649
+ static inline int mt_process_wait(int pid, mt_process_wait_result* out_result, mt_process_error* out_error) {
650
+ (void) pid;
651
+ mt_process_reset_wait_result(out_result);
652
+ mt_process_reset_error(out_error);
653
+ return mt_process_set_message(out_error, -1, "interactive process waiting is not supported on Windows yet", NULL);
654
+ }
655
+
656
+ static inline int mt_process_try_wait(int pid, mt_process_wait_result* out_result, mt_process_error* out_error) {
657
+ (void) pid;
658
+ mt_process_reset_wait_result(out_result);
659
+ mt_process_reset_error(out_error);
660
+ return mt_process_set_message(out_error, -1, "interactive process waiting is not supported on Windows yet", NULL);
661
+ }
662
+
663
+ static inline int mt_process_kill(int pid, int signal_number, mt_process_error* out_error) {
664
+ (void) pid;
665
+ (void) signal_number;
666
+ mt_process_reset_error(out_error);
667
+ return mt_process_set_message(out_error, -1, "interactive process signals are not supported on Windows yet", NULL);
668
+ }
669
+
670
+ static inline int mt_process_pty_resize(int fd, int columns, int rows, mt_process_error* out_error) {
671
+ (void) fd;
672
+ (void) columns;
673
+ (void) rows;
674
+ mt_process_reset_error(out_error);
675
+ return mt_process_set_message(out_error, -1, "pty resize is not supported on Windows yet", NULL);
676
+ }
677
+
678
+ #else
679
+
680
+ static inline void mt_process_close_fd_quiet(int* fd) {
681
+ if (fd == NULL || *fd < 0) {
682
+ return;
683
+ }
684
+
685
+ close(*fd);
686
+ *fd = -1;
687
+ }
688
+
689
+ static inline int mt_process_set_fd_cloexec(int fd, mt_process_error* out_error, const char* prefix) {
690
+ int flags = fcntl(fd, F_GETFD);
691
+ if (flags < 0) {
692
+ return mt_process_set_message(out_error, errno, prefix, strerror(errno));
693
+ }
694
+
695
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
696
+ return mt_process_set_message(out_error, errno, prefix, strerror(errno));
697
+ }
698
+
699
+ return 0;
700
+ }
701
+
702
+ static inline int mt_process_read_exec_error(int error_fd, mt_process_error* out_error, const char* prefix) {
703
+ int error_code = 0;
704
+ ssize_t read_count;
705
+ do {
706
+ read_count = read(error_fd, &error_code, sizeof(error_code));
707
+ } while (read_count < 0 && errno == EINTR);
708
+
709
+ if (read_count > 0) {
710
+ return mt_process_set_message(out_error, error_code, prefix, strerror(error_code));
711
+ }
712
+
713
+ return 0;
714
+ }
715
+
716
+ static inline int mt_process_execve_with_env(const char* file, char** args, char** env) {
717
+ if (env != NULL) {
718
+ return execvpe(file, args, env);
719
+ }
720
+
721
+ return execvp(file, args);
722
+ }
723
+
724
+ static inline void mt_process_child_fail(int error_fd, int error_code) {
725
+ if (error_fd >= 0) {
726
+ ssize_t written_count;
727
+ do {
728
+ written_count = write(error_fd, &error_code, sizeof(error_code));
729
+ } while (written_count < 0 && errno == EINTR);
730
+ }
731
+
732
+ _exit(127);
733
+ }
734
+
735
+ static inline int mt_process_spawn_interactive(const char* file, char** args, char** env, const char* cwd, mt_process_spawn_handle* out_handle, mt_process_error* out_error) {
736
+ int stdin_pipe[2] = { -1, -1 };
737
+ int stdout_pipe[2] = { -1, -1 };
738
+ int stderr_pipe[2] = { -1, -1 };
739
+ int error_pipe[2] = { -1, -1 };
740
+ mt_process_reset_spawn_handle(out_handle);
741
+ mt_process_reset_error(out_error);
742
+
743
+ if (file == NULL || args == NULL || args[0] == NULL) {
744
+ return mt_process_set_message(out_error, -1, "process command cannot be empty", NULL);
745
+ }
746
+
747
+ if (pipe(stdin_pipe) < 0 || pipe(stdout_pipe) < 0 || pipe(stderr_pipe) < 0 || pipe(error_pipe) < 0) {
748
+ mt_process_close_fd_quiet(&stdin_pipe[0]);
749
+ mt_process_close_fd_quiet(&stdin_pipe[1]);
750
+ mt_process_close_fd_quiet(&stdout_pipe[0]);
751
+ mt_process_close_fd_quiet(&stdout_pipe[1]);
752
+ mt_process_close_fd_quiet(&stderr_pipe[0]);
753
+ mt_process_close_fd_quiet(&stderr_pipe[1]);
754
+ mt_process_close_fd_quiet(&error_pipe[0]);
755
+ mt_process_close_fd_quiet(&error_pipe[1]);
756
+ return mt_process_set_message(out_error, errno, "process pipe creation failed", strerror(errno));
757
+ }
758
+
759
+ int cloexec_status = mt_process_set_fd_cloexec(error_pipe[1], out_error, "process exec error pipe setup failed");
760
+ if (cloexec_status != 0) {
761
+ mt_process_close_fd_quiet(&stdin_pipe[0]);
762
+ mt_process_close_fd_quiet(&stdin_pipe[1]);
763
+ mt_process_close_fd_quiet(&stdout_pipe[0]);
764
+ mt_process_close_fd_quiet(&stdout_pipe[1]);
765
+ mt_process_close_fd_quiet(&stderr_pipe[0]);
766
+ mt_process_close_fd_quiet(&stderr_pipe[1]);
767
+ mt_process_close_fd_quiet(&error_pipe[0]);
768
+ mt_process_close_fd_quiet(&error_pipe[1]);
769
+ return cloexec_status;
770
+ }
771
+
772
+ pid_t pid = fork();
773
+ if (pid < 0) {
774
+ mt_process_close_fd_quiet(&stdin_pipe[0]);
775
+ mt_process_close_fd_quiet(&stdin_pipe[1]);
776
+ mt_process_close_fd_quiet(&stdout_pipe[0]);
777
+ mt_process_close_fd_quiet(&stdout_pipe[1]);
778
+ mt_process_close_fd_quiet(&stderr_pipe[0]);
779
+ mt_process_close_fd_quiet(&stderr_pipe[1]);
780
+ mt_process_close_fd_quiet(&error_pipe[0]);
781
+ mt_process_close_fd_quiet(&error_pipe[1]);
782
+ return mt_process_set_message(out_error, errno, "process fork failed", strerror(errno));
783
+ }
784
+
785
+ if (pid == 0) {
786
+ mt_process_close_fd_quiet(&error_pipe[0]);
787
+ mt_process_close_fd_quiet(&stdin_pipe[1]);
788
+ mt_process_close_fd_quiet(&stdout_pipe[0]);
789
+ mt_process_close_fd_quiet(&stderr_pipe[0]);
790
+
791
+ if (cwd != NULL && chdir(cwd) != 0) {
792
+ mt_process_child_fail(error_pipe[1], errno);
793
+ }
794
+
795
+ if (dup2(stdin_pipe[0], STDIN_FILENO) < 0 || dup2(stdout_pipe[1], STDOUT_FILENO) < 0 || dup2(stderr_pipe[1], STDERR_FILENO) < 0) {
796
+ mt_process_child_fail(error_pipe[1], errno);
797
+ }
798
+
799
+ mt_process_close_fd_quiet(&stdin_pipe[0]);
800
+ mt_process_close_fd_quiet(&stdout_pipe[1]);
801
+ mt_process_close_fd_quiet(&stderr_pipe[1]);
802
+
803
+ mt_process_execve_with_env(file, args, env);
804
+ mt_process_child_fail(error_pipe[1], errno);
805
+ }
806
+
807
+ mt_process_close_fd_quiet(&error_pipe[1]);
808
+ mt_process_close_fd_quiet(&stdin_pipe[0]);
809
+ mt_process_close_fd_quiet(&stdout_pipe[1]);
810
+ mt_process_close_fd_quiet(&stderr_pipe[1]);
811
+
812
+ int exec_status = mt_process_read_exec_error(error_pipe[0], out_error, "process exec failed");
813
+ mt_process_close_fd_quiet(&error_pipe[0]);
814
+ if (exec_status != 0) {
815
+ int wait_status;
816
+ waitpid(pid, &wait_status, 0);
817
+ mt_process_close_fd_quiet(&stdin_pipe[1]);
818
+ mt_process_close_fd_quiet(&stdout_pipe[0]);
819
+ mt_process_close_fd_quiet(&stderr_pipe[0]);
820
+ return exec_status;
821
+ }
822
+
823
+ out_handle->pid = (int) pid;
824
+ out_handle->stdin_fd = stdin_pipe[1];
825
+ out_handle->stdout_fd = stdout_pipe[0];
826
+ out_handle->stderr_fd = stderr_pipe[0];
827
+ return 0;
828
+ }
829
+
830
+ static inline int mt_process_spawn_pty(const char* file, char** args, char** env, const char* cwd, int columns, int rows, mt_process_pty_handle* out_handle, mt_process_error* out_error) {
831
+ int master_fd = -1;
832
+ int error_pipe[2] = { -1, -1 };
833
+ mt_process_reset_pty_handle(out_handle);
834
+ mt_process_reset_error(out_error);
835
+
836
+ if (file == NULL || args == NULL || args[0] == NULL) {
837
+ return mt_process_set_message(out_error, -1, "process command cannot be empty", NULL);
838
+ }
839
+
840
+ master_fd = posix_openpt(O_RDWR | O_NOCTTY);
841
+ if (master_fd < 0) {
842
+ return mt_process_set_message(out_error, errno, "pty master open failed", strerror(errno));
843
+ }
844
+
845
+ if (grantpt(master_fd) != 0 || unlockpt(master_fd) != 0) {
846
+ mt_process_close_fd_quiet(&master_fd);
847
+ return mt_process_set_message(out_error, errno, "pty master setup failed", strerror(errno));
848
+ }
849
+
850
+ if (pipe(error_pipe) < 0) {
851
+ mt_process_close_fd_quiet(&master_fd);
852
+ return mt_process_set_message(out_error, errno, "pty error pipe creation failed", strerror(errno));
853
+ }
854
+
855
+ int cloexec_status = mt_process_set_fd_cloexec(error_pipe[1], out_error, "pty exec error pipe setup failed");
856
+ if (cloexec_status != 0) {
857
+ mt_process_close_fd_quiet(&master_fd);
858
+ mt_process_close_fd_quiet(&error_pipe[0]);
859
+ mt_process_close_fd_quiet(&error_pipe[1]);
860
+ return cloexec_status;
861
+ }
862
+
863
+ char* slave_name = ptsname(master_fd);
864
+ if (slave_name == NULL) {
865
+ int error_code = errno;
866
+ mt_process_close_fd_quiet(&master_fd);
867
+ mt_process_close_fd_quiet(&error_pipe[0]);
868
+ mt_process_close_fd_quiet(&error_pipe[1]);
869
+ return mt_process_set_message(out_error, error_code, "pty slave lookup failed", strerror(error_code));
870
+ }
871
+
872
+ pid_t pid = fork();
873
+ if (pid < 0) {
874
+ mt_process_close_fd_quiet(&master_fd);
875
+ mt_process_close_fd_quiet(&error_pipe[0]);
876
+ mt_process_close_fd_quiet(&error_pipe[1]);
877
+ return mt_process_set_message(out_error, errno, "pty fork failed", strerror(errno));
878
+ }
879
+
880
+ if (pid == 0) {
881
+ mt_process_close_fd_quiet(&error_pipe[0]);
882
+
883
+ if (setsid() < 0) {
884
+ mt_process_child_fail(error_pipe[1], errno);
885
+ }
886
+
887
+ int slave_fd = open(slave_name, O_RDWR);
888
+ if (slave_fd < 0) {
889
+ mt_process_child_fail(error_pipe[1], errno);
890
+ }
891
+
892
+ if (ioctl(slave_fd, TIOCSCTTY, 0) < 0) {
893
+ mt_process_close_fd_quiet(&slave_fd);
894
+ mt_process_child_fail(error_pipe[1], errno);
895
+ }
896
+
897
+ if (columns > 0 && rows > 0) {
898
+ struct winsize window_size;
899
+ memset(&window_size, 0, sizeof(window_size));
900
+ window_size.ws_col = (unsigned short) columns;
901
+ window_size.ws_row = (unsigned short) rows;
902
+ if (ioctl(slave_fd, TIOCSWINSZ, &window_size) < 0) {
903
+ mt_process_close_fd_quiet(&slave_fd);
904
+ mt_process_child_fail(error_pipe[1], errno);
905
+ }
906
+ }
907
+
908
+ if (cwd != NULL && chdir(cwd) != 0) {
909
+ mt_process_close_fd_quiet(&slave_fd);
910
+ mt_process_child_fail(error_pipe[1], errno);
911
+ }
912
+
913
+ if (dup2(slave_fd, STDIN_FILENO) < 0 || dup2(slave_fd, STDOUT_FILENO) < 0 || dup2(slave_fd, STDERR_FILENO) < 0) {
914
+ mt_process_close_fd_quiet(&slave_fd);
915
+ mt_process_child_fail(error_pipe[1], errno);
916
+ }
917
+
918
+ mt_process_close_fd_quiet(&master_fd);
919
+ mt_process_close_fd_quiet(&slave_fd);
920
+
921
+ mt_process_execve_with_env(file, args, env);
922
+ mt_process_child_fail(error_pipe[1], errno);
923
+ }
924
+
925
+ mt_process_close_fd_quiet(&error_pipe[1]);
926
+ int exec_status = mt_process_read_exec_error(error_pipe[0], out_error, "pty exec failed");
927
+ mt_process_close_fd_quiet(&error_pipe[0]);
928
+ if (exec_status != 0) {
929
+ int wait_status;
930
+ waitpid(pid, &wait_status, 0);
931
+ mt_process_close_fd_quiet(&master_fd);
932
+ return exec_status;
933
+ }
934
+
935
+ out_handle->pid = (int) pid;
936
+ out_handle->master_fd = master_fd;
937
+ return 0;
938
+ }
939
+
940
+ static inline int mt_process_read_fd(int fd, int timeout_ms, mt_process_read_result* out_result, mt_process_error* out_error) {
941
+ mt_process_reset_read_result(out_result);
942
+ mt_process_reset_error(out_error);
943
+
944
+ if (fd < 0) {
945
+ return mt_process_set_message(out_error, -1, "process stream is closed", NULL);
946
+ }
947
+
948
+ struct pollfd poll_state;
949
+ memset(&poll_state, 0, sizeof(poll_state));
950
+ poll_state.fd = fd;
951
+ poll_state.events = POLLIN | POLLHUP;
952
+
953
+ int poll_status;
954
+ do {
955
+ poll_status = poll(&poll_state, 1, timeout_ms);
956
+ } while (poll_status < 0 && errno == EINTR);
957
+
958
+ if (poll_status < 0) {
959
+ return mt_process_set_message(out_error, errno, "process stream poll failed", strerror(errno));
960
+ }
961
+
962
+ if (poll_status == 0) {
963
+ return 0;
964
+ }
965
+
966
+ char* buffer = (char*) malloc(4096);
967
+ if (buffer == NULL) {
968
+ return mt_process_set_message(out_error, UV_ENOMEM, "process stream read allocation failed", uv_strerror(UV_ENOMEM));
969
+ }
970
+
971
+ ssize_t read_count;
972
+ do {
973
+ read_count = read(fd, buffer, 4096);
974
+ } while (read_count < 0 && errno == EINTR);
975
+
976
+ if (read_count < 0) {
977
+ int error_code = errno;
978
+ free(buffer);
979
+ if (error_code == EAGAIN || error_code == EWOULDBLOCK) {
980
+ return 0;
981
+ }
982
+ if (error_code == EIO) {
983
+ out_result->ready = true;
984
+ out_result->closed = true;
985
+ return 0;
986
+ }
987
+ return mt_process_set_message(out_error, error_code, "process stream read failed", strerror(error_code));
988
+ }
989
+
990
+ out_result->ready = true;
991
+ if (read_count == 0) {
992
+ out_result->closed = true;
993
+ free(buffer);
994
+ return 0;
995
+ }
996
+
997
+ out_result->data = buffer;
998
+ out_result->len = (uintptr_t) read_count;
999
+ return 0;
1000
+ }
1001
+
1002
+ static inline int mt_process_write_fd(int fd, const char* data, uintptr_t len, uintptr_t* out_written, mt_process_error* out_error) {
1003
+ uintptr_t written_total = 0;
1004
+ mt_process_reset_error(out_error);
1005
+ if (out_written != NULL) {
1006
+ *out_written = 0;
1007
+ }
1008
+
1009
+ if (fd < 0) {
1010
+ return mt_process_set_message(out_error, -1, "process stream is closed", NULL);
1011
+ }
1012
+
1013
+ while (written_total < len) {
1014
+ ssize_t write_count;
1015
+ do {
1016
+ write_count = write(fd, data + written_total, len - written_total);
1017
+ } while (write_count < 0 && errno == EINTR);
1018
+
1019
+ if (write_count < 0) {
1020
+ return mt_process_set_message(out_error, errno, "process stream write failed", strerror(errno));
1021
+ }
1022
+
1023
+ written_total += (uintptr_t) write_count;
1024
+ }
1025
+
1026
+ if (out_written != NULL) {
1027
+ *out_written = written_total;
1028
+ }
1029
+ return 0;
1030
+ }
1031
+
1032
+ static inline int mt_process_close_fd(int fd, mt_process_error* out_error) {
1033
+ mt_process_reset_error(out_error);
1034
+ if (fd < 0) {
1035
+ return 0;
1036
+ }
1037
+
1038
+ if (close(fd) != 0) {
1039
+ return mt_process_set_message(out_error, errno, "process stream close failed", strerror(errno));
1040
+ }
1041
+
1042
+ return 0;
1043
+ }
1044
+
1045
+ static inline int mt_process_wait_internal(int pid, int options, mt_process_wait_result* out_result, mt_process_error* out_error) {
1046
+ int status = 0;
1047
+ mt_process_reset_wait_result(out_result);
1048
+ mt_process_reset_error(out_error);
1049
+
1050
+ if (pid <= 0) {
1051
+ return mt_process_set_message(out_error, -1, "process pid is invalid", NULL);
1052
+ }
1053
+
1054
+ pid_t wait_status;
1055
+ do {
1056
+ wait_status = waitpid((pid_t) pid, &status, options);
1057
+ } while (wait_status < 0 && errno == EINTR);
1058
+
1059
+ if (wait_status < 0) {
1060
+ return mt_process_set_message(out_error, errno, "process wait failed", strerror(errno));
1061
+ }
1062
+
1063
+ if (wait_status == 0) {
1064
+ return 0;
1065
+ }
1066
+
1067
+ out_result->ready = true;
1068
+ if (WIFEXITED(status)) {
1069
+ out_result->exit_status = (int64_t) WEXITSTATUS(status);
1070
+ out_result->term_signal = 0;
1071
+ return 0;
1072
+ }
1073
+
1074
+ if (WIFSIGNALED(status)) {
1075
+ out_result->exit_status = 0;
1076
+ out_result->term_signal = WTERMSIG(status);
1077
+ return 0;
1078
+ }
1079
+
1080
+ out_result->exit_status = 0;
1081
+ out_result->term_signal = 0;
1082
+ return 0;
1083
+ }
1084
+
1085
+ static inline int mt_process_wait(int pid, mt_process_wait_result* out_result, mt_process_error* out_error) {
1086
+ return mt_process_wait_internal(pid, 0, out_result, out_error);
1087
+ }
1088
+
1089
+ static inline int mt_process_try_wait(int pid, mt_process_wait_result* out_result, mt_process_error* out_error) {
1090
+ return mt_process_wait_internal(pid, WNOHANG, out_result, out_error);
1091
+ }
1092
+
1093
+ static inline int mt_process_kill(int pid, int signal_number, mt_process_error* out_error) {
1094
+ mt_process_reset_error(out_error);
1095
+ if (pid <= 0) {
1096
+ return mt_process_set_message(out_error, -1, "process pid is invalid", NULL);
1097
+ }
1098
+
1099
+ if (kill((pid_t) pid, signal_number) != 0) {
1100
+ return mt_process_set_message(out_error, errno, "process signal failed", strerror(errno));
1101
+ }
1102
+
1103
+ return 0;
1104
+ }
1105
+
1106
+ static inline int mt_process_pty_resize(int fd, int columns, int rows, mt_process_error* out_error) {
1107
+ struct winsize window_size;
1108
+ mt_process_reset_error(out_error);
1109
+
1110
+ if (fd < 0) {
1111
+ return mt_process_set_message(out_error, -1, "process stream is closed", NULL);
1112
+ }
1113
+
1114
+ if (columns <= 0 || rows <= 0) {
1115
+ return mt_process_set_message(out_error, -1, "pty size must be positive", NULL);
1116
+ }
1117
+
1118
+ memset(&window_size, 0, sizeof(window_size));
1119
+ window_size.ws_col = (unsigned short) columns;
1120
+ window_size.ws_row = (unsigned short) rows;
1121
+ if (ioctl(fd, TIOCSWINSZ, &window_size) != 0) {
1122
+ return mt_process_set_message(out_error, errno, "pty resize failed", strerror(errno));
1123
+ }
1124
+
1125
+ return 0;
1126
+ }
1127
+
1128
+ static inline char*** mt_process_environ_accessor(void) {
1129
+ return &environ;
1130
+ }
1131
+
1132
+ static inline uintptr_t mt_process_environ_length(void) {
1133
+ if (environ == NULL) {
1134
+ return 0;
1135
+ }
1136
+ uintptr_t count = 0;
1137
+ char** cursor = environ;
1138
+ while (*cursor != NULL) {
1139
+ count += 1;
1140
+ cursor += 1;
1141
+ }
1142
+ return count;
1143
+ }
1144
+
1145
+ #endif
1146
+
1147
+ #endif