#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); 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); 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_get_cstr(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); 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)