@@ -41,38 +41,30 @@ constdef SpawnOptions
4141 ASYNC_OUTPUT = 1 << 2 ,
4242 NO_WINDOW = 1 << 3 ,
4343 NO_PATH_SEARCH = 1 << 4 ,
44- INHERIT_STDIO @deprecated = 1 << 5
44+ INHERIT_STDIO = 1 << 5
4545}
4646
47- const INHERIT_STDIO_MASK @local = 1 << 5 ;
4847
4948alias RawFd = env ::WIN32 ? Win32_HANDLE : CInt ;
5049
5150enum ProcessStdioAction
5251{
52+ DEFAULT ,
5353 CAPTURE ,
5454 INHERIT ,
5555 BIND
5656}
5757
5858struct ProcessStdio
5959{
60- ProcessStdioAction kind ;
60+ ProcessStdioAction action ;
6161 RawFd fd ;
6262}
6363
64- const ProcessStdio CAPTURE = { .kind = CAPTURE };
65- const ProcessStdio INHERIT = { .kind = INHERIT };
66- macro ProcessStdio bind (RawFd fd ) => { .kind = BIND , .fd = fd };
67-
68- struct ProcessStdioSet
69- {
70- ProcessStdio stdin ;
71- ProcessStdio stdout ;
72- ProcessStdio stderr ;
73- }
74-
75- const ProcessStdioSet INHERIT_STDIO = { .stdin .kind = INHERIT , .stdout .kind = INHERIT , .stderr .kind = INHERIT };
64+ const ProcessStdio CAPTURE = { .action = CAPTURE };
65+ const ProcessStdio INHERIT = { .action = INHERIT };
66+ const ProcessStdio DEFAULT = { .action = DEFAULT };
67+ macro ProcessStdio bind (RawFd fd ) => { .action = BIND , .fd = fd };
7668
7769fn String ? run_capture_stdout (char [] buffer , String [] command_line , SpawnOptions options = {}, String [] environment = {})
7870{
@@ -84,18 +76,24 @@ fn String? run_capture_stdout(char[] buffer, String[] command_line, SpawnOptions
8476 return (String )buffer [: len - 1 ];
8577}
8678
87- fn int ? run (String [] command_line , SpawnOptions options = {}, String [] environment = {}, ProcessStdioSet stdio = {})
79+ fn int ? run (String [] command_line , SpawnOptions options = {}, String [] environment = {},
80+ ProcessStdio in = DEFAULT , ProcessStdio out = DEFAULT , ProcessStdio err = DEFAULT )
8881{
89- Process process = process ::spawn (command_line , options , environment , stdio )! ;
82+ Process process = process ::spawn (command_line , options , environment , in , out , err )! ;
9083 defer (void )process .destroy ();
9184 return process .join ()! ;
9285}
9386
87+
9488<*
9589 @require ! environment || ! (options & INHERIT_ENV )
9690*>
97- fn Process ? spawn (String [] command_line , SpawnOptions options = {}, String [] environment = {}, ProcessStdioSet stdio = {}) @if (env ::WIN32 )
91+ fn Process ? spawn (String [] command_line , SpawnOptions options = {}, String [] environment = {},
92+ ProcessStdio in = DEFAULT , ProcessStdio out = DEFAULT , ProcessStdio err = DEFAULT ) @if (env ::WIN32 || env ::POSIX )
9893{
94+ update_default (& in , & out , & err , !! (options & INHERIT_STDIO ));
95+
96+ $if env ::WIN32 :
9997 Win32_DWORD flags = win32 ::CREATE_UNICODE_ENVIRONMENT ;
10098 Win32_PROCESS_INFORMATION process_info ;
10199 Win32_SECURITY_ATTRIBUTES sa_attr = { Win32_SECURITY_ATTRIBUTES ::size , null , 1 };
@@ -106,15 +104,15 @@ fn Process? spawn(String[] command_line, SpawnOptions options = {}, String[] env
106104
107105 if (options & NO_WINDOW ) flags |= win32 ::CREATE_NO_WINDOW ;
108106
109- // Backwards compatibility
110- if (options & INHERIT_STDIO_MASK ) stdio = { INHERIT , INHERIT , INHERIT };
111107
112108 CFile stdin , stdout , stderr ;
113109 void * rd , wr ;
114110 void * event_output , event_error ;
115111
116- switch (stdio . stdin . kind )
112+ switch (in . action )
117113 {
114+ case DEFAULT :
115+ unreachable ();
118116 case CAPTURE :
119117 if (! win32 ::createPipe (& rd , & wr , & sa_attr , 0 )) return FAILED_TO_CREATE_PIPE ~ ;
120118 if (! win32 ::setHandleInformation (wr , win32 ::HANDLE_FLAG_INHERIT , 0 )) return FAILED_TO_CREATE_PIPE ~ ;
@@ -127,13 +125,15 @@ fn Process? spawn(String[] command_line, SpawnOptions options = {}, String[] env
127125 }
128126 start_info .hStdInput = rd ;
129127 case BIND :
130- start_info .hStdInput = stdio . stdin .fd ;
128+ start_info .hStdInput = in .fd ;
131129 case INHERIT :
132130 start_info .hStdInput = win32 ::getStdHandle (win32 ::STD_INPUT_HANDLE );
133131 }
134132
135- switch (stdio . stdout . kind )
133+ switch (out . action )
136134 {
135+ case DEFAULT :
136+ unreachable ();
137137 case CAPTURE :
138138 if (options & ASYNC_OUTPUT )
139139 {
@@ -155,7 +155,7 @@ fn Process? spawn(String[] command_line, SpawnOptions options = {}, String[] env
155155
156156 start_info .hStdOutput = wr ;
157157 case BIND :
158- start_info .hStdOutput = stdio . stdout .fd ;
158+ start_info .hStdOutput = out .fd ;
159159 case INHERIT :
160160 start_info .hStdOutput = win32 ::getStdHandle (win32 ::STD_OUTPUT_HANDLE );
161161 }
@@ -167,8 +167,10 @@ fn Process? spawn(String[] command_line, SpawnOptions options = {}, String[] env
167167 }
168168 else
169169 {
170- switch (stdio . stderr . kind )
170+ switch (err . action )
171171 {
172+ case DEFAULT :
173+ unreachable ();
172174 case CAPTURE :
173175 if (options & ASYNC_OUTPUT )
174176 {
@@ -189,7 +191,7 @@ fn Process? spawn(String[] command_line, SpawnOptions options = {}, String[] env
189191 }
190192 start_info .hStdError = wr ;
191193 case BIND :
192- start_info .hStdError = stdio . stderr .fd ;
194+ start_info .hStdError = err .fd ;
193195 case INHERIT :
194196 start_info .hStdError = win32 ::getStdHandle (win32 ::STD_ERROR_HANDLE );
195197 }
@@ -230,69 +232,56 @@ fn Process? spawn(String[] command_line, SpawnOptions options = {}, String[] env
230232 // We don't need the handle of the primary thread in the called process.
231233 win32 ::closeHandle (process_info .hThread );
232234
233- if (stdio . stdout . kind == CAPTURE ) win32 ::closeHandle (start_info .hStdOutput );
234- if (stdio . stderr . kind == CAPTURE && start_info .hStdOutput != start_info .hStdError )
235+ if (out . action == CAPTURE ) win32 ::closeHandle (start_info .hStdOutput );
236+ if (err . action == CAPTURE && start_info .hStdOutput != start_info .hStdError )
235237 {
236238 win32 ::closeHandle (start_info .hStdError );
237239 }
238240
239241
240242 return {
241243 .hProcess = process_info .hProcess ,
242- .hStdInput = stdio . stdin . kind == CAPTURE ? start_info .hStdInput : null ,
244+ .hStdInput = in . action == CAPTURE ? start_info .hStdInput : null ,
243245 .hEventOutput = event_output ,
244246 .hEventError = event_error ,
245247 .stdin_file = stdin ,
246248 .stdout_file = stdout ,
247249 .stderr_file = stderr ,
248250 .is_alive = true ,
249251 };
250- }
251-
252-
253-
254-
255- <*
256- @require ! environment || ! (options & INHERIT_ENV )
257- *>
258- fn Process ? spawn (String [] command_line , SpawnOptions options = { }, String [] environment = {}, ProcessStdioSet stdio = {}) @if (env ::POSIX )
259- {
260- CInt [2 ] stdinfd ;
261- CInt [2 ] stdoutfd ;
262- CInt [2 ] stderrfd ;
263- CFile stdin = null ;
264- CFile stdout = null ;
265- CFile stderr = null ;
266-
252+ $else
267253 Posix_spawn_file_actions_t actions ;
268254 if (posix ::spawn_file_actions_init (& actions )) return FAILED_TO_INITIALIZE_ACTIONS ~ ;
269255 defer posix ::spawn_file_actions_destroy (& actions );
270256
271- // Backwards compatibility
272- if (options & INHERIT_STDIO_MASK ) stdio = { INHERIT , INHERIT , INHERIT };
273-
274-
275257 // We should set FD_CLOEXEC to prevent leaking pipe handles between concurrent spawns
276258 const int F_SETFD = 2 ;
277259 const int FD_CLOEXEC = 1 ;
278260
279- switch (stdio .stdin .kind )
261+ CInt [2 ] stdinfd , stdoutfd , stderrfd ;
262+ CFile stdin , stdout , stderr ;
263+
264+ switch (in .action )
280265 {
281- case CAPTURE :
266+ case DEFAULT :
267+ unreachable ();
268+ case CAPTURE :
282269 if (posix ::pipe (& stdinfd )) return FAILED_TO_OPEN_STDIN ~ ;
283270 os ::fcntl ((NativeSocket )stdinfd [0 ], F_SETFD , FD_CLOEXEC );
284271 os ::fcntl ((NativeSocket )stdinfd [1 ], F_SETFD , FD_CLOEXEC );
285272 if (posix ::spawn_file_actions_adddup2 (& actions , stdinfd [0 ], libc ::STDIN_FD )) return FAILED_TO_OPEN_STDIN ~ ;
286273 case BIND :
287- if (posix ::spawn_file_actions_adddup2 (& actions , stdio . stdin .fd , libc ::STDIN_FD )) return FAILED_TO_BIND_STDIN ~ ;
274+ if (posix ::spawn_file_actions_adddup2 (& actions , in .fd , libc ::STDIN_FD )) return FAILED_TO_BIND_STDIN ~ ;
288275 case INHERIT :
289276 break ;
290277 }
291278
292- switch (stdio . stdout . kind )
279+ switch (out . action )
293280 {
281+ case DEFAULT :
282+ unreachable ();
294283 case CAPTURE :
295- if (posix ::pipe (& stdoutfd )) return FAILED_TO_OPEN_STDOUT ~ ;
284+ if (posix ::pipe (& stdoutfd )) return FAILED_TO_OPEN_STDOUT ~ ;
296285 os ::fcntl ((NativeSocket )stdoutfd [0 ], F_SETFD , FD_CLOEXEC );
297286 os ::fcntl ((NativeSocket )stdoutfd [1 ], F_SETFD , FD_CLOEXEC );
298287
@@ -303,7 +292,7 @@ fn Process? spawn(String[] command_line, SpawnOptions options = { }, String[] en
303292 }
304293 if (posix ::spawn_file_actions_adddup2 (& actions , stdoutfd [1 ], libc ::STDOUT_FD )) return FAILED_TO_OPEN_STDOUT ~ ;
305294 case BIND :
306- if (posix ::spawn_file_actions_adddup2 (& actions , stdio . stdout .fd , libc ::STDOUT_FD )) return FAILED_TO_BIND_STDOUT ~ ;
295+ if (posix ::spawn_file_actions_adddup2 (& actions , out .fd , libc ::STDOUT_FD )) return FAILED_TO_BIND_STDOUT ~ ;
307296 case INHERIT :
308297 break ;
309298 }
@@ -314,21 +303,23 @@ fn Process? spawn(String[] command_line, SpawnOptions options = { }, String[] en
314303 }
315304 else
316305 {
317- switch (stdio . stderr . kind )
306+ switch (err . action )
318307 {
308+ case DEFAULT :
309+ unreachable ();
319310 case CAPTURE :
320311 if (posix ::pipe (& stderrfd )) return FAILED_TO_OPEN_STDERR ~ ;
321312 os ::fcntl ((NativeSocket )stderrfd [0 ], F_SETFD , FD_CLOEXEC );
322313 os ::fcntl ((NativeSocket )stderrfd [1 ], F_SETFD , FD_CLOEXEC );
323-
314+
324315 if (options & ASYNC_OUTPUT )
325316 {
326317 ((NativeSocket )stderrfd [0 ]).set_non_blocking (true )! ;
327318 ((NativeSocket )stderrfd [1 ]).set_non_blocking (true )! ;
328319 }
329320 if (posix ::spawn_file_actions_adddup2 (& actions , stderrfd [1 ], libc ::STDERR_FD )) return FAILED_TO_OPEN_STDERR ~ ;
330321 case BIND :
331- if (posix ::spawn_file_actions_adddup2 (& actions , stdio . stderr .fd , libc ::STDERR_FD )) return FAILED_TO_BIND_STDERR ~ ;
322+ if (posix ::spawn_file_actions_adddup2 (& actions , err .fd , libc ::STDERR_FD )) return FAILED_TO_BIND_STDERR ~ ;
332323 case INHERIT :
333324 break ;
334325 }
@@ -350,17 +341,17 @@ fn Process? spawn(String[] command_line, SpawnOptions options = { }, String[] en
350341 };
351342
352343 // Only set up file handles for when stdio is captured
353- if (stdio . stdin . kind == CAPTURE )
344+ if (in . action == CAPTURE )
354345 {
355346 libc ::close (stdinfd [0 ]);
356347 stdin = libc ::fdopen (stdinfd [1 ], " wb" );
357348 }
358- if (stdio . stdout . kind == CAPTURE )
349+ if (out . action == CAPTURE )
359350 {
360351 libc ::close (stdoutfd [1 ]);
361352 stdout = libc ::fdopen (stdoutfd [0 ], " rb" );
362353 }
363- if (stdio . stderr . kind == CAPTURE )
354+ if (err . action == CAPTURE )
364355 {
365356 if (options & STDERR_TO_STDOUT )
366357 {
@@ -381,8 +372,13 @@ fn Process? spawn(String[] command_line, SpawnOptions options = { }, String[] en
381372 .child = child ,
382373 .is_alive = true ,
383374 };
375+ $endif
384376}
385377
378+
379+
380+
381+
386382fn CInt ? Process .join (& self ) @if (env ::POSIX )
387383{
388384 if (self .stdin_file )
@@ -434,7 +430,7 @@ fn bool Process.destroy(&self)
434430{
435431 if (self .stdin_file ) libc ::fclose (self .stdin_file );
436432 if (self .stdout_file ) libc ::fclose (self .stdout_file );
437- if (self .stderr_file && self .stderr_file != self .stdout_file ) libc ::fclose (self .stderr_file );
433+ if (self .stderr_file && self .stderr_file != self .stdout_file ) libc ::fclose (self .stderr_file );
438434 self .stdin_file = self .stdout_file = self .stderr_file = null ;
439435 $if env ::WIN32 :
440436 if (self .hProcess ) win32 ::closeHandle (self .hProcess );
@@ -630,3 +626,11 @@ fn ZString* copy_env(Allocator mem, String[] environment) @local @inline @if(env
630626 }
631627 return copy ;
632628}
629+
630+ macro update_default (ProcessStdio * in , ProcessStdio * out , ProcessStdio * err , bool inherit_by_default ) @local
631+ {
632+ ProcessStdioAction action = inherit_by_default ? INHERIT : CAPTURE ;
633+ if (in .action == DEFAULT ) in .action = action ;
634+ if (out .action == DEFAULT ) out .action = action ;
635+ if (err .action == DEFAULT ) err .action = action ;
636+ }
0 commit comments