370 lines
8.5 KiB
C
370 lines
8.5 KiB
C
#include <bshell/command/alias.h>
|
|
#include <bshell/command/command.h>
|
|
#include <bshell/command/native-command.h>
|
|
#include <bshell/runtime/cmdcall.h>
|
|
#include <bshell/runtime/pipeline.h>
|
|
#include <bshell/runtime/runtime.h>
|
|
#include <bshell/status.h>
|
|
#include <fx/collections/array.h>
|
|
#include <fx/collections/hashtable.h>
|
|
#include <fx/io/path.h>
|
|
#include <fx/reflection/assembly.h>
|
|
#include <fx/reflection/function.h>
|
|
#include <fx/reflection/type.h>
|
|
#include <fx/string.h>
|
|
#include <fx/vector.h>
|
|
|
|
#if BSHELL_ENABLE_NATIVE_COMMANDS == 1
|
|
#include <fx/diagnostics/process.h>
|
|
#endif
|
|
|
|
#if BSHELL_INTERACTIVE == 1
|
|
#include <fx/term/print.h>
|
|
#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)
|