Files
honey/std/process/process.hon
T

67 lines
2.2 KiB
Plaintext

import "@std/io"
import "@ffi/c"
import "@std/mem"
Init :: struct {
io io.Io
args Args
}
#! borrows argument slices and their zero-terminated strings, valid until main returns.
Args :: struct {
values [][;0]u8
}
RunError :: enum {
invalid_arguments
spawn_failed
wait_failed
}
#! run a program synchronously without a shell, searching PATH for `arguments[0]`.
#! arguments and zero-terminated strings are borrowed until this call returns.
#! an empty argument vector, empty executable name, or embedded NUL is invalid.
#! the child inherits the current environment and stdin/stdout/stderr.
#! temporary argument descriptors use `allocator` and are freed on every return.
#! return the exit code, or 128 plus the terminating signal number.
run proc(allocator mem.Allocator, arguments [][;0]u8) i32 ! (mem.AllocError | RunError) {
if (arguments.len == 0) return .invalid_arguments
if (arguments[0].len == 0) return .invalid_arguments
for arguments |argument| {
for (argument) |byte| if (byte == 0) return .invalid_arguments
}
if (arguments.len == maxval!(usize)) return .out_of_memory
descriptors :: try mem.alloc(?*mut c_char, allocator, arguments.len + 1)
defer mem.free(allocator, descriptors)
for (0..arguments.len) |index| {
descriptors[index] = ptrcast!(constcast!(arguments[index]).ptr)
}
descriptors[arguments.len] = null
pid [1]mut c_int := [0]
result :: c.posix_spawnp(
pid[..].ptr,
ptrcast!(arguments[0].ptr),
null,
null,
descriptors.ptr,
c._NSGetEnviron()?^,
)
if (result != 0) return .spawn_failed
status [1]mut c_int := [0]
while true {
waited :: c.waitpid(pid[0], status[..].ptr, 0)
if (waited < 0) {
if (c.__error()?^ == c.EINTR) continue
return .wait_failed
}
# Darwin wait status: low seven bits hold the signal, next byte the exit code.
signal :: status[0] & 127
if (signal == 0) return i32(divtrunc!(status[0], 256) & 255)
if (signal != 127) return i32(128 + signal)
# A traced child can report a stop even without WUNTRACED; wait for termination.
}
}