From fba7987d782e8515d035841ccae712e01fdefb22 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sat, 13 Jun 2026 13:47:34 +0100 Subject: [PATCH] runtime: implement a bshell_command sub-class for calling native commands --- bshell.runtime/command/native-command.c | 232 ++++++++++++++++++ .../include/bshell/command/native-command.h | 23 ++ .../include/bshell/runtime/cmdcall.h | 9 + bshell.runtime/runtime/cmdcall.c | 4 + 4 files changed, 268 insertions(+) create mode 100644 bshell.runtime/command/native-command.c create mode 100644 bshell.runtime/include/bshell/command/native-command.h diff --git a/bshell.runtime/command/native-command.c b/bshell.runtime/command/native-command.c new file mode 100644 index 0000000..631d0d0 --- /dev/null +++ b/bshell.runtime/command/native-command.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + F_REDIRECT_STDIN = 0x01u, + F_REDIRECT_STDOUT = 0x02u, +}; + +struct bshell_native_command_p { + int cmd_flags; + fx_string *cmd_path; + fx_stringstream *cmd_tmp; + fx_process *cmd_proc; + struct bshell_value_writer cmd_writer; + bool cmd_writer_init; +}; + +static void init(fx_object *obj, void *priv) +{ + struct bshell_native_command_p *cmd_p = priv; + cmd_p->cmd_tmp = fx_stringstream_create(); +} + +static void fini(fx_object *obj, void *priv) +{ + struct bshell_native_command_p *cmd_p = priv; + fx_stringstream_unref(cmd_p->cmd_tmp); + fx_string_unref(cmd_p->cmd_path); +} + +static enum bshell_status start_process(bshell_native_command *cmd, int flags) +{ + struct bshell_native_command_p *cmd_p + = fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND); + fx_stringstream *tmp = fx_stringstream_create(); + size_t argc = bshell_command_get_arg_count(cmd); + char **argv = calloc(argc, sizeof *argv); + + for (size_t i = 0; i < argc; i++) { + const fx_value *arg = bshell_command_get_arg(cmd, i); + + fx_stringstream_reset(tmp); + fx_value_to_string(arg, tmp, NULL); + argv[i] = fx_stringstream_steal(tmp); + } + fx_stringstream_unref(tmp); + + fx_process *self = fx_process_get_self(); + + fx_process_start_info info = { + .proc_args = (const char **)argv, + .proc_args_count = argc, + .proc_file_name = fx_string_get_cstr(cmd_p->cmd_path), + .proc_environment = fx_process_get_environment(self), + }; + + if (flags & F_REDIRECT_STDIN) { + info.proc_redirect_stdin = true; + } + + if (flags & F_REDIRECT_STDOUT) { + info.proc_redirect_stdout = true; + } + + cmd_p->cmd_flags = flags; + cmd_p->cmd_proc = fx_process_create(&info); + + fx_status status = fx_process_start(cmd_p->cmd_proc); + for (size_t i = 0; i < argc; i++) { + free(argv[i]); + } + free(argv); + + if (!FX_OK(status)) { + return BSHELL_ERR_NO_MEMORY; + } + + return BSHELL_SUCCESS; +} + +static enum bshell_status begin_processing( + bshell_command *cmd, + enum bshell_command_position position) +{ + struct bshell_native_command_p *cmd_p + = fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND); + printf("begin processing '%s'\n", fx_string_get_cstr(cmd_p->cmd_path)); + int flags = 0; + switch (position) { + case BSHELL_COMMAND_POSITION_BEGINNING: + flags = F_REDIRECT_STDOUT; + break; + case BSHELL_COMMAND_POSITION_MIDDLE: + flags = F_REDIRECT_STDIN | F_REDIRECT_STDOUT; + break; + case BSHELL_COMMAND_POSITION_END: + flags = F_REDIRECT_STDIN; + break; + default: + break; + } + + return start_process(cmd, flags); +} + +static enum bshell_status process_record( + bshell_command *cmd, + bshell_pipeline *pipeline, + struct bshell_runtime *rt) +{ + struct bshell_native_command_p *cmd_p + = fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND); + printf("process record '%s'\n", fx_string_get_cstr(cmd_p->cmd_path)); + + if (cmd_p->cmd_flags & F_REDIRECT_STDIN) { + fx_value in = bshell_pipeline_read_value(pipeline); + if (!fx_value_is_set(&in)) { + return BSHELL_SUCCESS; + } + + fx_stream *in_stream = fx_process_get_stdin(cmd_p->cmd_proc); + if (!cmd_p->cmd_writer_init) { + bshell_value_writer_init(&cmd_p->cmd_writer, in_stream); + cmd_p->cmd_writer_init = true; + } + + bshell_value_writer_write(&cmd_p->cmd_writer, &in); + fx_value_unset(&in); + } + + if (cmd_p->cmd_flags & F_REDIRECT_STDOUT) { + fx_stream *out_stream = fx_process_get_stdout(cmd_p->cmd_proc); + fx_stringstream_reset(cmd_p->cmd_tmp); + fx_status status + = fx_stream_read_line_s(out_stream, cmd_p->cmd_tmp); + if (!FX_OK(status)) { + /* no more data available */ + return BSHELL_SUCCESS; + } + + fx_string *record = fx_string_create_from_cstr( + fx_stringstream_ptr(cmd_p->cmd_tmp)); + bshell_pipeline_write_value( + pipeline, + FX_VALUE_OBJECT(record), + false); + fx_string_unref(record); + } + + return BSHELL_SUCCESS; +} + +static enum bshell_status end_processing(bshell_command *cmd) +{ + struct bshell_native_command_p *cmd_p + = fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND); + printf("end processing '%s'\n", fx_string_get_cstr(cmd_p->cmd_path)); + if (cmd_p->cmd_writer_init) { + bshell_value_writer_cleanup(&cmd_p->cmd_writer); + cmd_p->cmd_writer_init = false; + } + + if (cmd_p->cmd_proc) { + fx_process_close_stdin(cmd_p->cmd_proc); + fx_process_close_stdout(cmd_p->cmd_proc); + fx_process_close_stderr(cmd_p->cmd_proc); + + int result = 0; + fx_process_wait(cmd_p->cmd_proc, &result); + fx_process_unref(cmd_p->cmd_proc); + cmd_p->cmd_proc = NULL; + } + + return BSHELL_SUCCESS; +} + +static enum bshell_status get_callable_name( + const bshell_native_command *cmd, + fx_string *out) +{ + struct bshell_native_command_p *cmd_p + = fx_object_get_private(cmd, BSHELL_TYPE_NATIVE_COMMAND); + fx_string_append_s(out, cmd_p->cmd_path); + return BSHELL_SUCCESS; +} + +bshell_native_command *bshell_native_command_create(const char *path) +{ + bshell_native_command *out + = fx_object_create(BSHELL_TYPE_NATIVE_COMMAND); + if (!out) { + return NULL; + } + + struct bshell_native_command_p *cmd + = fx_object_get_private(out, BSHELL_TYPE_NATIVE_COMMAND); + cmd->cmd_path = fx_string_create_from_cstr(path); + + return out; +} + +FX_TYPE_CLASS_BEGIN(bshell_native_command) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND) + FX_INTERFACE_ENTRY(c_command_type) = "Native-Command"; + FX_INTERFACE_ENTRY(c_get_callable_name) = get_callable_name; + FX_INTERFACE_ENTRY(c_begin_processing) = begin_processing; + FX_INTERFACE_ENTRY(c_process_record) = process_record; + FX_INTERFACE_ENTRY(c_end_processing) = end_processing; + FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND) +FX_TYPE_CLASS_END(bshell_native_command) + +FX_TYPE_DEFINITION_BEGIN(bshell_native_command) + FX_TYPE_ID(0x720ede87, 0xe586, 0x495a, 0xa229, 0x1505c01b5714); + FX_TYPE_FLAGS(FX_TYPE_F_ABSTRACT); + FX_TYPE_NAME("bshell.runtime.native_command"); + FX_TYPE_EXTENDS(BSHELL_TYPE_COMMAND); + FX_TYPE_CLASS(bshell_native_command_class); + FX_TYPE_INSTANCE_INIT(init); + FX_TYPE_INSTANCE_FINI(fini); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_native_command_p); +FX_TYPE_DEFINITION_END(bshell_native_command) diff --git a/bshell.runtime/include/bshell/command/native-command.h b/bshell.runtime/include/bshell/command/native-command.h new file mode 100644 index 0000000..be19863 --- /dev/null +++ b/bshell.runtime/include/bshell/command/native-command.h @@ -0,0 +1,23 @@ +#ifndef BSHELL_COMMAND_NATIVE_COMMAND_H_ +#define BSHELL_COMMAND_NATIVE_COMMAND_H_ + +#include + +FX_DECLS_BEGIN; + +struct bshell_runtime; + +#define BSHELL_TYPE_NATIVE_COMMAND (bshell_native_command_get_type()) + +FX_DECLARE_TYPE(bshell_native_command); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_native_command) +FX_TYPE_CLASS_DECLARATION_END(bshell_native_command) + +extern fx_type_id bshell_native_command_get_type(void); + +extern bshell_native_command *bshell_native_command_create(const char *path); + +FX_DECLS_END; + +#endif diff --git a/bshell.runtime/include/bshell/runtime/cmdcall.h b/bshell.runtime/include/bshell/runtime/cmdcall.h index 7be301f..8ed3167 100644 --- a/bshell.runtime/include/bshell/runtime/cmdcall.h +++ b/bshell.runtime/include/bshell/runtime/cmdcall.h @@ -10,6 +10,12 @@ struct bshell_runtime; enum bshell_command_position; +enum bshell_cmdcall_type { + BSHELL_CMDCALL_NONE = 0, + BSHELL_CMDCALL_CMDLET, + BSHELL_CMDCALL_NATIVE, +}; + #define BSHELL_TYPE_CMDCALL (bshell_cmdcall_get_type()) FX_DECLARE_TYPE(bshell_cmdcall); @@ -21,6 +27,9 @@ extern fx_type_id bshell_cmdcall_get_type(void); FX_TYPE_DEFAULT_CONSTRUCTOR(bshell_cmdcall, BSHELL_TYPE_CMDCALL); +extern enum bshell_cmdcall_type bshell_cmdcall_get_target_type( + const bshell_cmdcall *cmdcall); + extern enum bshell_status bshell_cmdcall_push_arg( bshell_cmdcall *cmdcall, fx_value arg); diff --git a/bshell.runtime/runtime/cmdcall.c b/bshell.runtime/runtime/cmdcall.c index 905415b..efa8b74 100644 --- a/bshell.runtime/runtime/cmdcall.c +++ b/bshell.runtime/runtime/cmdcall.c @@ -1,10 +1,14 @@ #include #include +#include #include #include #include #include #include +#include +#include +#include #include #include #include