#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if BSHELL_ENABLE_NATIVE_COMMANDS == 1 #include #endif #if BSHELL_INTERACTIVE == 1 #include #endif enum cmdcall_type { CMDCALL_NONE = 0, CMDCALL_CMDLET, CMDCALL_NATIVE, }; struct bshell_cmdcall_p { enum cmdcall_type cmd_type; bshell_command *cmd_target; char *cmd_native_path; bool cmd_initialised; FX_VECTOR_DECLARE(fx_value, cmd_args); fx_array *cmd_args_processed; }; static void cmdcall_fini(fx_object *obj, void *priv) { struct bshell_cmdcall_p *cmdcall = priv; if (cmdcall->cmd_target) { bshell_command_end_processing(cmdcall->cmd_target); bshell_command_unref(cmdcall->cmd_target); } if (cmdcall->cmd_native_path) { free(cmdcall->cmd_native_path); } if (cmdcall->cmd_args_processed) { fx_array_unref(cmdcall->cmd_args_processed); } for (size_t i = 0; i < cmdcall->cmd_args.count; i++) { fx_value_unset(&cmdcall->cmd_args.items[i]); } fx_vector_destroy(cmdcall->cmd_args, NULL); } static enum bshell_status cmdcall_push_arg( struct bshell_cmdcall_p *cmdcall, fx_value arg) { fx_value *slot = fx_vector_emplace_back(cmdcall->cmd_args, NULL); fx_value_copy(slot, &arg); return BSHELL_SUCCESS; } static const fx_value *get_arg(struct bshell_cmdcall_p *cmdcall, size_t index) { if (index >= cmdcall->cmd_args.count) { return NULL; } return &cmdcall->cmd_args.items[cmdcall->cmd_args.count - index - 1]; } static bshell_command *resolve_cmdlet( struct bshell_cmdcall_p *cmdcall, struct bshell_runtime *rt, fx_string *cmd_name) { while (1) { const char *cmd_name_cstr = fx_string_get_cstr(cmd_name); bshell_command *cmd = bshell_runtime_find_command(rt, cmd_name_cstr); if (!cmd) { break; } bshell_alias_class *alias = fx_object_get_interface(cmd, BSHELL_TYPE_ALIAS); if (alias) { fx_string_clear(cmd_name); fx_string_append_cstr(cmd_name, alias->a_target_name); bshell_command_unref(cmd); continue; } return cmd; } return NULL; } static const fx_string *get_path(void) { #if BSHELL_ENABLE_NATIVE_COMMANDS == 1 fx_process *self = fx_process_get_self(); if (!self) { return NULL; } const fx_hashtable *env = fx_process_get_environment(self); if (!env) { return NULL; } const fx_value *path_v = fx_hashtable_get(env, &FX_CSTR("PATH")); if (!path_v) { return NULL; } const fx_string *path = NULL; fx_value_get_object(path_v, (fx_object **)&path); return path; #else return NULL; #endif } #if BSHELL_ENABLE_NATIVE_COMMANDS == 1 static bshell_command *resolve_native_command_local( struct bshell_cmdcall_p *cmdcall, struct bshell_runtime *rt, fx_string *cmd_name) { const char *cmd_name_cstr = fx_string_get_cstr(cmd_name); fx_path *cmd_path = fx_path_create_from_cstr(cmd_name_cstr); if (!fx_path_is_file(cmd_path)) { fx_path_unref(cmd_path); return NULL; } bshell_command *result = bshell_native_command_create(fx_path_get_cstr(cmd_path)); fx_path_unref(cmd_path); return result; } static bshell_command *resolve_native_command( struct bshell_cmdcall_p *cmdcall, struct bshell_runtime *rt, fx_string *cmd_name) { const char *cmd_name_cstr = fx_string_get_cstr(cmd_name); if (cmd_name_cstr[0] == '/' || (cmd_name_cstr[0] == '.' && cmd_name_cstr[1] == '/') || (cmd_name_cstr[0] == '.' && cmd_name_cstr[1] == '.' && cmd_name_cstr[2] == '/')) { return resolve_native_command_local(cmdcall, rt, cmd_name); } fx_path *cmd_name_path = fx_path_create_from_cstr(cmd_name_cstr); const char *delim[] = {":"}; const fx_string *path = get_path(); const fx_iterator *it = fx_string_tokenise( path, delim, sizeof delim / sizeof delim[0], FX_STRING_TOK_F_NORMAL); bshell_command *result = NULL; fx_foreach(v, it) { const char *tok = NULL; fx_path *full_path = fx_path_join_list(2, v, &FX_VALUE_OBJECT(cmd_name)); if (!fx_path_is_file(full_path)) { fx_path_unref(full_path); continue; } result = bshell_native_command_create( fx_path_get_cstr(full_path)); fx_path_unref(full_path); break; } fx_iterator_unref(it); return result; } #endif static enum bshell_status resolve_call_target( struct bshell_cmdcall_p *cmdcall, struct bshell_runtime *rt) { fx_value *cmd_name_v = fx_array_get_ref(cmdcall->cmd_args_processed, 0); if (!cmd_name_v) { return BSHELL_ERR_BAD_STATE; } fx_string *cmd_name_s = NULL; fx_value_get_object(cmd_name_v, &cmd_name_s); if ((cmdcall->cmd_target = resolve_cmdlet(cmdcall, rt, cmd_name_s))) { cmdcall->cmd_type = CMDCALL_CMDLET; return BSHELL_SUCCESS; } #if BSHELL_ENABLE_NATIVE_COMMANDS == 1 if ((cmdcall->cmd_target = resolve_native_command(cmdcall, rt, cmd_name_s))) { cmdcall->cmd_type = CMDCALL_NATIVE; return BSHELL_SUCCESS; } #endif const char *cmd_name_cstr = fx_string_get_cstr(cmd_name_s); #if BSHELL_INTERACTIVE == 1 fx_printf("[bold,bright_red]"); #endif printf("%s: The term '%s' is not recognised as a name of a cmdlet, " "function, script file, or executable program.\n", cmd_name_cstr, cmd_name_cstr); #if BSHELL_INTERACTIVE == 1 fx_printf("[reset]"); #endif printf("\n"); return BSHELL_ERR_NO_ENTRY; } static enum bshell_status process_args(struct bshell_cmdcall_p *cmdcall) { if (cmdcall->cmd_args_processed) { return BSHELL_SUCCESS; } fx_stringstream *str = fx_stringstream_create(); if (!str) { return BSHELL_ERR_NO_MEMORY; } cmdcall->cmd_args_processed = fx_array_create(); if (!cmdcall->cmd_args_processed) { fx_stringstream_unref(str); return BSHELL_ERR_NO_MEMORY; } for (i32 i = cmdcall->cmd_args.count - 1; i >= 0; i--) { fx_stringstream_reset(str); fx_value_to_string(&cmdcall->cmd_args.items[i], str, NULL); fx_string *arg_str = fx_string_create_from_cstr( fx_stringstream_get_cstr(str)); fx_array_push_back( cmdcall->cmd_args_processed, FX_VALUE_OBJECT(arg_str)); fx_string_unref(arg_str); } fx_stringstream_unref(str); return BSHELL_SUCCESS; } static enum bshell_status cmdcall_resolve( struct bshell_cmdcall_p *cmdcall, struct bshell_runtime *rt) { enum bshell_status status = process_args(cmdcall); if (status != BSHELL_SUCCESS) { return status; } status = resolve_call_target(cmdcall, rt); if (status != BSHELL_SUCCESS) { return status; } bshell_command_set_args_reverse( cmdcall->cmd_target, cmdcall->cmd_args.items, cmdcall->cmd_args.count); return BSHELL_SUCCESS; } static enum bshell_status cmdcall_execute(struct bshell_cmdcall_p *cmdcall) { return BSHELL_SUCCESS; } static enum bshell_status cmdcall_process_record( struct bshell_cmdcall_p *cmdcall, bshell_pipeline *pipeline, struct bshell_runtime *rt, enum bshell_command_position position) { if (!cmdcall->cmd_target) { return BSHELL_ERR_NOT_SUPPORTED; } if (!cmdcall->cmd_initialised) { bshell_command_begin_processing(cmdcall->cmd_target, position); cmdcall->cmd_initialised = true; } return bshell_command_process_record(cmdcall->cmd_target, pipeline, rt); } enum bshell_status bshell_cmdcall_push_arg( bshell_cmdcall *cmdcall, fx_value arg) { FX_CLASS_DISPATCH_STATIC( BSHELL_TYPE_CMDCALL, cmdcall_push_arg, cmdcall, arg); } enum bshell_status bshell_cmdcall_resolve( bshell_cmdcall *cmdcall, struct bshell_runtime *rt) { FX_CLASS_DISPATCH_STATIC( BSHELL_TYPE_CMDCALL, cmdcall_resolve, cmdcall, rt); } enum bshell_status bshell_cmdcall_process_record( bshell_cmdcall *cmdcall, bshell_pipeline *pipeline, struct bshell_runtime *rt, enum bshell_command_position position) { FX_CLASS_DISPATCH_STATIC( BSHELL_TYPE_CMDCALL, cmdcall_process_record, cmdcall, pipeline, rt, position); } FX_TYPE_CLASS_BEGIN(bshell_cmdcall) 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_CONSTRUCTOR("create", bshell_cmdcall_create, 0, FX_TYPE_VOID); FX_TYPE_CLASS_END(bshell_cmdcall) FX_TYPE_DEFINITION_BEGIN(bshell_cmdcall) FX_TYPE_ID(0x08aeb275, 0xc09b, 0x4c3a, 0xa8df, 0xd9fba0bb4571); FX_TYPE_NAME("bshell.cmdcall"); FX_TYPE_CLASS(bshell_cmdcall_class); FX_TYPE_INSTANCE_PRIVATE(struct bshell_cmdcall_p); FX_TYPE_INSTANCE_FINI(cmdcall_fini); FX_TYPE_DEFINITION_END(bshell_cmdcall)