From 0ee1a3575d205cf7cec228d54b444ae7a8fc17d3 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sun, 24 May 2026 20:22:19 +0100 Subject: [PATCH] cmdlet: implement a set of builtin cmdlets --- bshell/cmdlets/foreach-object.c | 102 ++++++++++++++++++++++ bshell/cmdlets/format-table.c | 9 ++ bshell/cmdlets/get-command.c | 143 +++++++++++++++++++++++++++++++ bshell/cmdlets/get-verb.c | 114 +++++++++++++++++++++++++ bshell/cmdlets/write-output.c | 144 ++++++++++++++++++++++++++++++++ 5 files changed, 512 insertions(+) create mode 100644 bshell/cmdlets/foreach-object.c create mode 100644 bshell/cmdlets/format-table.c create mode 100644 bshell/cmdlets/get-command.c create mode 100644 bshell/cmdlets/get-verb.c create mode 100644 bshell/cmdlets/write-output.c diff --git a/bshell/cmdlets/foreach-object.c b/bshell/cmdlets/foreach-object.c new file mode 100644 index 0000000..dff3a27 --- /dev/null +++ b/bshell/cmdlets/foreach-object.c @@ -0,0 +1,102 @@ +#include "../command/cmdlet.h" +#include "../command/command.h" +#include "../runtime/pipeline.h" +#include "../runtime/runtime.h" +#include "../script-block.h" +#include "../status.h" + +#include +#include +#include + +#define BSHELL_TYPE_FOREACH_OBJECT (bshell_foreach_object_get_type()) + +fx_type_id bshell_foreach_object_get_type(void); + +FX_DECLARE_TYPE(bshell_foreach_object); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_foreach_object) +FX_TYPE_CLASS_DECLARATION_END(bshell_foreach_object) + +struct bshell_foreach_object_p { + bshell_scriptblock *f_block; +}; + +static enum bshell_status begin_processing(bshell_cmdlet *cmdlet) +{ + struct bshell_foreach_object_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_FOREACH_OBJECT); + + const fx_value *block_v = bshell_command_get_arg(cmdlet, 1); + if (!block_v) { + return BSHELL_ERR_INVALID_ARGUMENT; + } + + fx_value_get_object(block_v, &p->f_block); + return BSHELL_SUCCESS; +} + +static enum bshell_status process_record( + bshell_cmdlet *cmdlet, + bshell_pipeline *pipeline, + struct bshell_runtime *rt) +{ + struct bshell_foreach_object_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_FOREACH_OBJECT); + + fx_value in = bshell_pipeline_read_value(pipeline); + if (!fx_value_is_set(&in)) { + return BSHELL_SUCCESS; + } + + bshell_runtime_push_scope(rt, p->f_block); + bshell_variable *item = bshell_runtime_define_var(rt, "_"); + bshell_variable_set_value(item, &in); + fx_value out = bshell_runtime_eval(rt); + bshell_runtime_pop_scope(rt); + fx_value_unset(&in); + bshell_pipeline_write_value(pipeline, out, false); + fx_value_unset(&out); + + return BSHELL_SUCCESS; +} + +static enum bshell_status end_processing(bshell_cmdlet *cmdlet) +{ + struct bshell_foreach_object_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_FOREACH_OBJECT); + return BSHELL_SUCCESS; +} + +static void init(fx_object *obj, void *priv) +{ +} + +FX_TYPE_CLASS_BEGIN(bshell_foreach_object) + 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_cmdlet, BSHELL_TYPE_CMDLET) + FX_INTERFACE_ENTRY(c_verb) = BSHELL_VERB_FOREACH; + FX_INTERFACE_ENTRY(c_noun) = "Object"; + FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND) + 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_foreach_object) + +FX_TYPE_DEFINITION_BEGIN(bshell_foreach_object) + FX_TYPE_ID(0x44ae3223, 0x14b3, 0x4c89, 0x9753, 0x6c7901ae00e9); + FX_TYPE_NAME("bshell.core.foreach_object"); + FX_TYPE_EXTENDS(BSHELL_TYPE_CMDLET); + FX_TYPE_CLASS(bshell_foreach_object_class); + FX_TYPE_INSTANCE_INIT(init); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_foreach_object_p); +FX_TYPE_DEFINITION_END(bshell_foreach_object) diff --git a/bshell/cmdlets/format-table.c b/bshell/cmdlets/format-table.c new file mode 100644 index 0000000..8c78e40 --- /dev/null +++ b/bshell/cmdlets/format-table.c @@ -0,0 +1,9 @@ +#include "../status.h" + +#include +#include + +enum bshell_status do_format_table(fx_object *object, fx_stream *dest) +{ + return BSHELL_SUCCESS; +} diff --git a/bshell/cmdlets/get-command.c b/bshell/cmdlets/get-command.c new file mode 100644 index 0000000..ae4df48 --- /dev/null +++ b/bshell/cmdlets/get-command.c @@ -0,0 +1,143 @@ +#include "../command/cmdlet.h" +#include "../command/command.h" +#include "../runtime/pipeline.h" +#include "../runtime/runtime.h" +#include "../runtime/scope.h" +#include "../status.h" + +#include +#include +#include +#include + +#define BSHELL_TYPE_GET_COMMAND (bshell_get_command_get_type()) + +fx_type_id bshell_get_command_get_type(void); + +FX_DECLARE_TYPE(bshell_get_command); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_get_command) +FX_TYPE_CLASS_DECLARATION_END(bshell_get_command) + +struct bshell_get_command_p { + bool c_eof; +}; + +extern const fx_assembly *bshell_assembly_get(void); + +static enum bshell_status begin_processing(bshell_cmdlet *cmdlet) +{ + struct bshell_get_command_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_GET_COMMAND); + return BSHELL_SUCCESS; +} + +static enum bshell_status process_record( + bshell_cmdlet *cmdlet, + bshell_pipeline *pipeline, + struct bshell_runtime *rt) +{ + struct bshell_get_command_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_GET_COMMAND); + if (p->c_eof) { + return BSHELL_SUCCESS; + } + + fx_iterator *it = fx_iterator_begin(rt->rt_aliases); + fx_foreach(item_v, it) + { + fx_hashtable_item *item; + fx_value_get_object(&item_v, &item); + fx_value alias_v = fx_hashtable_item_get_value(item); + bshell_pipeline_write_value(pipeline, alias_v, false); + } + fx_iterator_unref(it); + + fx_queue_entry *cur = fx_queue_last(&rt->rt_scope); + while (cur) { + struct runtime_scope *scope = fx_unbox( + struct runtime_scope, + cur, + s_entry); + + it = fx_iterator_begin(scope->s_functions); + fx_foreach(item_v, it) + { + fx_hashtable_item *item; + fx_value_get_object(&item_v, &item); + fx_value func_v = fx_hashtable_item_get_value(item); + bshell_pipeline_write_value(pipeline, func_v, false); + } + fx_iterator_unref(it); + + cur = fx_queue_prev(cur); + } + + const fx_assembly *self = bshell_assembly_get(); + it = fx_assembly_get_types(self); + fx_foreach(v, it) + { + fx_type *ty = NULL; + fx_value_get_object(&v, &ty); + if (fx_type_id_compare(fx_type_get_id(ty), BSHELL_TYPE_CMDLET) + == 0) { + continue; + } + bshell_command_class *cmd = fx_type_get_interface( + ty, + BSHELL_TYPE_COMMAND); + if (!cmd) { + continue; + } + + fx_value callable = FX_VALUE_OBJECT( + fx_object_create(fx_type_get_id(ty))); + bshell_pipeline_write_value(pipeline, callable, false); + fx_value_unset(&callable); + } + fx_iterator_unref(it); + p->c_eof = true; + + return BSHELL_SUCCESS; +} + +static enum bshell_status end_processing(bshell_cmdlet *cmdlet) +{ + struct bshell_get_command_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_GET_COMMAND); + + return BSHELL_SUCCESS; +} + +static void init(fx_object *obj, void *priv) +{ +} + +FX_TYPE_CLASS_BEGIN(bshell_get_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_cmdlet, BSHELL_TYPE_CMDLET) + FX_INTERFACE_ENTRY(c_verb) = BSHELL_VERB_GET; + FX_INTERFACE_ENTRY(c_noun) = "Command"; + FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND) + 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_cmdlet, BSHELL_TYPE_CMDLET) +FX_TYPE_CLASS_END(bshell_get_command) + +FX_TYPE_DEFINITION_BEGIN(bshell_get_command) + FX_TYPE_ID(0x4f862cc3, 0xf3d3, 0x4f04, 0xb68e, 0x8764079f03c4); + FX_TYPE_NAME("bshell.core.get_command"); + FX_TYPE_EXTENDS(BSHELL_TYPE_CMDLET); + FX_TYPE_CLASS(bshell_get_command_class); + FX_TYPE_INSTANCE_INIT(init); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_get_command_p); +FX_TYPE_DEFINITION_END(bshell_get_command) diff --git a/bshell/cmdlets/get-verb.c b/bshell/cmdlets/get-verb.c new file mode 100644 index 0000000..a8f6760 --- /dev/null +++ b/bshell/cmdlets/get-verb.c @@ -0,0 +1,114 @@ +#include "../command/cmdlet.h" +#include "../command/command.h" +#include "../runtime/pipeline.h" +#include "../status.h" + +#include +#include +#include + +#define BSHELL_TYPE_GET_VERB (bshell_get_verb_get_type()) + +fx_type_id bshell_get_verb_get_type(void); + +FX_DECLARE_TYPE(bshell_get_verb); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_get_verb) +FX_TYPE_CLASS_DECLARATION_END(bshell_get_verb) + +struct bshell_get_verb_p { + fx_hashtable *c_verbs; + fx_iterator *c_it; +}; + +static enum bshell_status begin_processing(bshell_cmdlet *cmdlet) +{ + struct bshell_get_verb_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_GET_VERB); + p->c_verbs = bshell_get_all_verbs(); + p->c_it = fx_iterator_begin(p->c_verbs); + return BSHELL_SUCCESS; +} + +static enum bshell_status process_record( + bshell_cmdlet *cmdlet, + bshell_pipeline *pipeline, + struct bshell_runtime *rt) +{ + struct bshell_get_verb_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_GET_VERB); + + do { + fx_value v = fx_iterator_get_value(p->c_it); + if (!v.v_type) { + return BSHELL_SUCCESS; + } + + fx_hashtable_item *item = NULL; + fx_value_get_object(&v, &item); + + v = fx_hashtable_item_get_value(item); + + bshell_verb *verb = NULL; + fx_value_get_object(&v, &verb); + fx_iterator_move_next(p->c_it); + + if (bshell_verb_is_reserved(verb)) { + continue; + } + + bshell_verb_ref(verb); + bshell_pipeline_write_value( + pipeline, + fx_value_copy_return(fx_hashtable_item_get_value(item)), + false); + break; + } while (1); + + return BSHELL_SUCCESS; +} + +static enum bshell_status end_processing(bshell_cmdlet *cmdlet) +{ + struct bshell_get_verb_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_GET_VERB); + if (p->c_it) { + fx_iterator_unref(p->c_it); + p->c_it = NULL; + } + + return BSHELL_SUCCESS; +} + +static void init(fx_object *obj, void *priv) +{ +} + +FX_TYPE_CLASS_BEGIN(bshell_get_verb) + 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_cmdlet, BSHELL_TYPE_CMDLET) + FX_INTERFACE_ENTRY(c_verb) = BSHELL_VERB_GET; + FX_INTERFACE_ENTRY(c_noun) = "Verb"; + FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND) + 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_cmdlet, BSHELL_TYPE_CMDLET) +FX_TYPE_CLASS_END(bshell_get_verb) + +FX_TYPE_DEFINITION_BEGIN(bshell_get_verb) + FX_TYPE_ID(0xa47976a6, 0xf7d8, 0x447f, 0x887b, 0x6c5d78b7bd2e); + FX_TYPE_NAME("bshell.core.get_verb"); + FX_TYPE_EXTENDS(BSHELL_TYPE_CMDLET); + FX_TYPE_CLASS(bshell_get_verb_class); + FX_TYPE_INSTANCE_INIT(init); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_get_verb_p); +FX_TYPE_DEFINITION_END(bshell_get_verb) diff --git a/bshell/cmdlets/write-output.c b/bshell/cmdlets/write-output.c new file mode 100644 index 0000000..ff05f06 --- /dev/null +++ b/bshell/cmdlets/write-output.c @@ -0,0 +1,144 @@ +#include "../command/cmdlet.h" +#include "../command/command.h" +#include "../format/format.h" +#include "../runtime/pipeline.h" +#include "../status.h" + +#include +#include +#include +#include +#include + +#define BSHELL_TYPE_WRITE_OUTPUT (bshell_write_output_get_type()) + +fx_type_id bshell_write_output_get_type(void); + +FX_DECLARE_TYPE(bshell_write_output); + +FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_write_output) +FX_TYPE_CLASS_DECLARATION_END(bshell_write_output) + +struct bshell_write_output_p { + struct table_format_ctx w_table_ctx; + bool w_table_initialised; +}; + +static void initialise_table( + struct bshell_write_output_p *cmd, + const fx_value *first_record) +{ + table_format_ctx_init(&cmd->w_table_ctx, fx_stdout); + const fx_type *ty = fx_type_get_by_id(first_record->v_type); + table_format_ctx_prepare_columns_from_format( + &cmd->w_table_ctx, + ty, + NULL); + table_format_ctx_print_headers(&cmd->w_table_ctx); + cmd->w_table_initialised = true; +} + +static bool value_requires_table(const fx_value *value) +{ + if (fx_type_is_value_type(value->v_type)) { + return false; + } + + if (fx_value_is_type(value, FX_TYPE_STRING) + || fx_value_is_type(value, FX_TYPE_ARRAY)) { + return false; + } + + return true; +} + +static void write_value( + struct bshell_write_output_p *cmd, + const fx_value *value) +{ + if (!value_requires_table(value)) { + fx_value_to_string(value, fx_stdout, NULL); + fx_stream_write_char(fx_stdout, '\n'); + return; + } + + if (!cmd->w_table_initialised) { + initialise_table(cmd, value); + } + + table_format_ctx_print_record(&cmd->w_table_ctx, value); +} + +static enum bshell_status begin_processing(bshell_cmdlet *cmdlet) +{ + struct bshell_write_output_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_WRITE_OUTPUT); + for (size_t i = 1;; i++) { + const fx_value *v = bshell_command_get_arg(cmdlet, i); + if (!v) { + break; + } + + write_value(p, v); + } + + return BSHELL_SUCCESS; +} + +static enum bshell_status process_record( + bshell_cmdlet *cmdlet, + bshell_pipeline *pipeline, + struct bshell_runtime *rt) +{ + struct bshell_write_output_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_WRITE_OUTPUT); + + fx_value in = bshell_pipeline_read_value(pipeline); + if (fx_value_is_set(&in)) { + write_value(p, &in); + fx_value_unset(&in); + } + + return BSHELL_SUCCESS; +} + +static enum bshell_status end_processing(bshell_cmdlet *cmdlet) +{ + struct bshell_write_output_p *p = fx_object_get_private( + cmdlet, + BSHELL_TYPE_WRITE_OUTPUT); + + return BSHELL_SUCCESS; +} + +static void init(fx_object *obj, void *priv) +{ +} + +FX_TYPE_CLASS_BEGIN(bshell_write_output) + 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_cmdlet, BSHELL_TYPE_CMDLET) + FX_INTERFACE_ENTRY(c_verb) = BSHELL_VERB_WRITE; + FX_INTERFACE_ENTRY(c_noun) = "Output"; + FX_TYPE_VTABLE_INTERFACE_END(bshell_cmdlet, BSHELL_TYPE_CMDLET) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(bshell_command, BSHELL_TYPE_COMMAND) + 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_cmdlet, BSHELL_TYPE_CMDLET) +FX_TYPE_CLASS_END(bshell_write_output) + +FX_TYPE_DEFINITION_BEGIN(bshell_write_output) + FX_TYPE_ID(0xf7f9c42d, 0xc053, 0x4ffc, 0xa8ec, 0x78fef4844ac0); + FX_TYPE_NAME("bshell.core.write_output"); + FX_TYPE_EXTENDS(BSHELL_TYPE_CMDLET); + FX_TYPE_CLASS(bshell_write_output_class); + FX_TYPE_INSTANCE_INIT(init); + FX_TYPE_INSTANCE_PRIVATE(struct bshell_write_output_p); +FX_TYPE_DEFINITION_END(bshell_write_output)