From 454cb1ce1d8a35f712139e0ae5958370d6d56e81 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 24 Mar 2025 15:16:56 -0400 Subject: [PATCH] Change commands interface so it can either run or get result --- examples/commands/commands.c | 57 ++++++++++++++++++++--------------- examples/commands/commands.tm | 23 +++++++++----- examples/shell/shell.tm | 7 +++-- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/examples/commands/commands.c b/examples/commands/commands.c index d7bdeee..ec090a7 100644 --- a/examples/commands/commands.c +++ b/examples/commands/commands.c @@ -16,7 +16,7 @@ #define READ_END 0 #define WRITE_END 1 int run_command(Text_t exe, Array_t arg_array, Table_t env_table, - Array_t input_bytes, Array_t *output_bytes, Array_t *error_bytes) + OptionalArray_t input_bytes, Array_t *output_bytes, Array_t *error_bytes) { pthread_testcancel(); @@ -36,18 +36,24 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table, posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK); int child_inpipe[2], child_outpipe[2], child_errpipe[2]; - pipe(child_inpipe); - pipe(child_outpipe); - pipe(child_errpipe); + if (input_bytes.length >= 0) pipe(child_inpipe); + if (output_bytes) pipe(child_outpipe); + if (error_bytes) pipe(child_errpipe); posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions); - posix_spawn_file_actions_adddup2(&actions, child_inpipe[READ_END], STDIN_FILENO); - posix_spawn_file_actions_addclose(&actions, child_inpipe[WRITE_END]); - posix_spawn_file_actions_adddup2(&actions, child_outpipe[WRITE_END], STDOUT_FILENO); - posix_spawn_file_actions_addclose(&actions, child_outpipe[READ_END]); - posix_spawn_file_actions_adddup2(&actions, child_errpipe[WRITE_END], STDERR_FILENO); - posix_spawn_file_actions_addclose(&actions, child_errpipe[READ_END]); + if (input_bytes.length >= 0) { + posix_spawn_file_actions_adddup2(&actions, child_inpipe[READ_END], STDIN_FILENO); + posix_spawn_file_actions_addclose(&actions, child_inpipe[WRITE_END]); + } + if (output_bytes) { + posix_spawn_file_actions_adddup2(&actions, child_outpipe[WRITE_END], STDOUT_FILENO); + posix_spawn_file_actions_addclose(&actions, child_outpipe[READ_END]); + } + if (error_bytes) { + posix_spawn_file_actions_adddup2(&actions, child_errpipe[WRITE_END], STDERR_FILENO); + posix_spawn_file_actions_addclose(&actions, child_errpipe[READ_END]); + } const char *exe_str = Text$as_c_string(exe); @@ -85,15 +91,14 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table, posix_spawnattr_destroy(&attr); posix_spawn_file_actions_destroy(&actions); - close(child_inpipe[READ_END]); - close(child_outpipe[WRITE_END]); - close(child_errpipe[WRITE_END]); + if (input_bytes.length >= 0) close(child_inpipe[READ_END]); + if (output_bytes) close(child_outpipe[WRITE_END]); + if (error_bytes) close(child_errpipe[WRITE_END]); - struct pollfd pollfds[3] = { - {.fd=child_inpipe[WRITE_END], .events=POLLOUT}, - {.fd=child_outpipe[WRITE_END], .events=POLLIN}, - {.fd=child_errpipe[WRITE_END], .events=POLLIN}, - }; + struct pollfd pollfds[3] = {}; + if (input_bytes.length >= 0) pollfds[0] = (struct pollfd){.fd=child_inpipe[WRITE_END], .events=POLLOUT}; + if (output_bytes) pollfds[1] = (struct pollfd){.fd=child_outpipe[WRITE_END], .events=POLLIN}; + if (error_bytes) pollfds[2] = (struct pollfd){.fd=child_errpipe[WRITE_END], .events=POLLIN}; if (input_bytes.length > 0 && input_bytes.stride != 1) Array$compact(&input_bytes, sizeof(char)); @@ -102,10 +107,10 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table, if (error_bytes) *error_bytes = (Array_t){.atomic=1, .stride=1, .length=0}; - for (;;) { + while (input_bytes.length > 0 || output_bytes || error_bytes) { (void)poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); // Wait for data or readiness bool did_something = false; - if (pollfds[0].revents) { + if (input_bytes.length >= 0 && pollfds[0].revents) { if (input_bytes.length > 0) { ssize_t written = write(child_inpipe[WRITE_END], input_bytes.data, (size_t)input_bytes.length); if (written > 0) { @@ -123,13 +128,13 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table, } } char buf[256]; - if (pollfds[1].revents) { + if (output_bytes && pollfds[1].revents) { ssize_t n = read(child_outpipe[READ_END], buf, sizeof(buf)); did_something = did_something || (n > 0); if (n <= 0) { close(child_outpipe[READ_END]); pollfds[1].events = 0; - } else if (n > 0 && output_bytes) { + } else if (n > 0) { if (output_bytes->free < n) { output_bytes->data = GC_REALLOC(output_bytes->data, (size_t)(output_bytes->length + n)); output_bytes->free = 0; @@ -138,13 +143,13 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table, output_bytes->length += n; } } - if (pollfds[2].revents) { + if (error_bytes && pollfds[2].revents) { ssize_t n = read(child_errpipe[READ_END], buf, sizeof(buf)); did_something = did_something || (n > 0); if (n <= 0) { close(child_errpipe[READ_END]); pollfds[2].events = 0; - } else if (n > 0 && error_bytes) { + } else if (n > 0) { if (error_bytes->free < n) { error_bytes->data = GC_REALLOC(error_bytes->data, (size_t)(error_bytes->length + n)); error_bytes->free = 0; @@ -166,6 +171,10 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table, } } + if (input_bytes.length >= 0) close(child_inpipe[WRITE_END]); + if (output_bytes) close(child_outpipe[READ_END]); + if (error_bytes) close(child_errpipe[READ_END]); + sigaction(SIGINT, &oldint, NULL); sigaction(SIGQUIT, &oldquit, NULL); sigprocmask(SIG_SETMASK, &old, NULL); diff --git a/examples/commands/commands.tm b/examples/commands/commands.tm index 7d2f4a6..dd366f6 100644 --- a/examples/commands/commands.tm +++ b/examples/commands/commands.tm @@ -3,7 +3,7 @@ use ./commands.c use libunistring.so -extern run_command:func(exe:Text, args:[Text], env:{Text,Text}, input:[Byte], output:&[Byte], error:&[Byte] -> Int32) +extern run_command:func(exe:Text, args:[Text], env:{Text,Text}, input:[Byte]?, output:&[Byte]?, error:&[Byte]? -> Int32) extern command_by_line:func(exe:Text, args:[Text], env:{Text,Text} -> func(->Text?)?) enum ExitType(Exited(status:Int32), Signaled(signal:Int32), Failed) @@ -43,7 +43,7 @@ struct Command(command:Text, args=[:Text], env={:Text,Text}): func from_path(path:Path, args=[:Text], env={:Text,Text} -> Command): return Command(Text(path), args, env) - func run(command:Command, input="", input_bytes=[:Byte] -> ProgramResult): + func result(command:Command, input="", input_bytes=[:Byte] -> ProgramResult): if input.length > 0: (&input_bytes):insert_all(input:bytes()) @@ -59,11 +59,22 @@ struct Command(command:Text, args=[:Text], env={:Text,Text}): return ProgramResult(stdout, stderr, ExitType.Failed) + func run(command:Command, -> ExitType): + status := run_command(command.command, command.args, command.env, none, none, none) + + if inline C : Bool { WIFEXITED(_$status) }: + return ExitType.Exited(inline C : Int32 { WEXITSTATUS(_$status) }) + + if inline C : Bool { WIFSIGNALED(_$status) }: + return ExitType.Signaled(inline C : Int32 { WTERMSIG(_$status) }) + + return ExitType.Failed + func get_output(command:Command, input="", trim_newline=yes -> Text?): - return command:run(input=input):output_text(trim_newline=trim_newline) + return command:result(input=input):output_text(trim_newline=trim_newline) func get_output_bytes(command:Command, input="", input_bytes=[:Byte] -> [Byte]?): - result := command:run(input=input, input_bytes=input_bytes) + result := command:result(input=input, input_bytes=input_bytes) when result.exit_type is Exited(status): if status == 0: return result.stdout return none @@ -71,7 +82,3 @@ struct Command(command:Text, args=[:Text], env={:Text,Text}): func by_line(command:Command -> func(->Text?)?): return command_by_line(command.command, command.args, command.env) - -func main(command:Text, args:[Text], input=""): - cmd := Command(command, args) - say(cmd:get_output(input=input)!) diff --git a/examples/shell/shell.tm b/examples/shell/shell.tm index f1e2308..9ca9e05 100644 --- a/examples/shell/shell.tm +++ b/examples/shell/shell.tm @@ -24,8 +24,11 @@ lang Shell: func command(shell:Shell -> Command): return Command("sh", ["-c", shell.text]) - func run(shell:Shell, input="", input_bytes=[:Byte] -> ProgramResult): - return shell:command():run(input=input, input_bytes=input_bytes) + func result(shell:Shell, input="", input_bytes=[:Byte] -> ProgramResult): + return shell:command():result(input=input, input_bytes=input_bytes) + + func run(shell:Shell -> ExitType): + return shell:command():run() func get_output(shell:Shell, input="", trim_newline=yes -> Text?): return shell:command():get_output(input=input, trim_newline=trim_newline)