#include "command.h" #include #include #include #include #include #include #include #include #define OUTPUT_STREAM fx_stdtty_err static struct fx_bst command_list = {0}; FX_BST_DEFINE_SIMPLE_GET(struct fx_command, unsigned int, c_node, c_id, get_command) FX_BST_DEFINE_SIMPLE_INSERT(struct fx_command, c_node, c_id, put_command) enum item_type { ITEM_OPTION, ITEM_ARG, ITEM_SUBCOMMAND, }; static void command_list_cleanup(void) { struct fx_bst_node *node = fx_bst_first(&command_list); while (node) { struct fx_command *cmd = fx_unbox(struct fx_command, node, c_node); if (!cmd) { break; } struct fx_bst_node *next = fx_bst_next(node); fx_bst_delete(&command_list, node); fx_command_destroy(cmd); node = next; } } struct fx_command *fx_command_create(unsigned int id) { struct fx_command *out = malloc(sizeof *out); if (!out) { return NULL; } memset(out, 0x0, sizeof *out); out->c_id = id; out->c_parent_id = FX_COMMAND_INVALID_ID; return out; } static void command_usage_destroy(struct fx_command_usage *usage) { struct fx_queue_entry *entry = fx_queue_first(&usage->u_parts); while (entry) { struct fx_command_usage_entry *arg = fx_unbox(struct fx_command_usage_entry, entry, e_entry); if (!arg) { continue; } struct fx_queue_entry *next = fx_queue_next(entry); fx_queue_delete(&usage->u_parts, entry); free(arg); entry = next; } free(usage); } void fx_command_destroy(struct fx_command *cmd) { if (cmd->c_name) { free(cmd->c_name); } if (cmd->c_long_name) { free(cmd->c_long_name); } if (cmd->c_description) { free(cmd->c_description); } struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt); struct fx_queue_entry *next = NULL; while (entry) { struct fx_command_option *opt = fx_unbox(struct fx_command_option, entry, opt_entry); if (!opt) { break; } next = fx_queue_next(entry); fx_queue_delete(&cmd->c_opt, entry); fx_command_option_destroy(opt); entry = next; } entry = fx_queue_first(&cmd->c_arg); while (entry) { struct fx_command_arg *arg = fx_unbox(struct fx_command_arg, entry, arg_entry); if (!arg) { break; } next = fx_queue_next(entry); fx_queue_delete(&cmd->c_arg, entry); fx_command_arg_destroy(arg); entry = next; } #if 0 entry = fx_queue_first(&cmd->c_subcommands); while (entry) { struct fx_command *subcmd = fx_unbox(struct fx_command, entry, c_entry); if (!subcmd) { break; } next = fx_queue_next(entry); fx_command_destroy(subcmd); entry = next; } #endif entry = fx_queue_first(&cmd->c_usage); while (entry) { struct fx_command_usage *usage = fx_unbox(struct fx_command_usage, entry, u_entry); if (!usage) { break; } next = fx_queue_next(entry); fx_queue_delete(&cmd->c_usage, entry); command_usage_destroy(usage); entry = next; } free(cmd); } fx_status fx_command_register(struct fx_command *cmd) { struct fx_command *tmp = get_command(&command_list, cmd->c_id); if (tmp) { return FX_ERR_NAME_EXISTS; } if (fx_bst_empty(&command_list)) { atexit(command_list_cleanup); } put_command(&command_list, cmd); return FX_SUCCESS; } fx_status fx_command_set_name(struct fx_command *cmd, const char *name) { char *n = fx_strdup(name); if (!n) { return FX_ERR_NO_MEMORY; } if (cmd->c_name) { free(cmd->c_name); cmd->c_name = NULL; } cmd->c_name = n; return FX_SUCCESS; } fx_status fx_command_set_long_name(struct fx_command *cmd, const char *name) { char *n = fx_strdup(name); if (!n) { return FX_ERR_NO_MEMORY; } if (cmd->c_long_name) { free(cmd->c_long_name); cmd->c_long_name = NULL; } cmd->c_long_name = n; return FX_SUCCESS; } fx_status fx_command_set_short_name(struct fx_command *cmd, char name) { cmd->c_short_name = name; return FX_SUCCESS; } fx_status fx_command_set_flags(struct fx_command *cmd, fx_command_flags flags) { cmd->c_flags = flags; return FX_SUCCESS; } fx_status fx_command_set_description(struct fx_command *cmd, const char *description) { char *desc = fx_strdup(description); if (!desc) { return FX_ERR_NO_MEMORY; } if (cmd->c_description) { free(cmd->c_description); cmd->c_description = NULL; } cmd->c_description = desc; return FX_SUCCESS; } fx_status fx_command_set_callback( struct fx_command *cmd, fx_command_callback callback) { cmd->c_callback = callback; return FX_SUCCESS; } fx_status fx_command_set_parent(fx_command *cmd, unsigned int parent_id) { cmd->c_parent_id = parent_id; cmd->c_parent = NULL; return FX_SUCCESS; } struct fx_command_option *fx_command_add_option(struct fx_command *cmd, int id) { struct fx_command_option *opt = fx_command_option_create(); if (!opt) { return NULL; } opt->opt_id = id; fx_queue_push_back(&cmd->c_opt, &opt->opt_entry); return opt; } struct fx_command_arg *fx_command_add_arg(struct fx_command *cmd, int id) { struct fx_command_arg *arg = fx_command_arg_create(); if (!arg) { return NULL; } arg->arg_id = id; fx_queue_push_back(&cmd->c_arg, &arg->arg_entry); return arg; } struct fx_command_usage *fx_command_add_usage(struct fx_command *cmd) { struct fx_command_usage *usage = malloc(sizeof *usage); if (!usage) { return NULL; } memset(usage, 0x0, sizeof *usage); fx_queue_push_back(&cmd->c_usage, &usage->u_entry); return usage; } const struct fx_command_option *fx_command_get_option( const struct fx_command *cmd, int id) { struct fx_queue_entry *cur = fx_queue_first(&cmd->c_opt); while (cur) { const struct fx_command_option *opt = fx_unbox(struct fx_command_option, cur, opt_entry); if (opt->opt_id == id) { return opt; } cur = fx_queue_next(cur); } return NULL; } fx_status fx_command_usage_add_option( struct fx_command_usage *usage, struct fx_command_option *opt) { struct fx_command_usage_entry *u_opt = malloc(sizeof *u_opt); if (!u_opt) { return FX_ERR_NO_MEMORY; } memset(u_opt, 0x0, sizeof *u_opt); u_opt->e_type = CMD_USAGE_OPT; u_opt->e_opt = opt; fx_queue_push_back(&usage->u_parts, &u_opt->e_entry); return FX_SUCCESS; } fx_status fx_command_usage_add_arg( struct fx_command_usage *usage, struct fx_command_arg *arg) { struct fx_command_usage_entry *u_arg = malloc(sizeof *u_arg); if (!u_arg) { return FX_ERR_NO_MEMORY; } memset(u_arg, 0x0, sizeof *u_arg); u_arg->e_type = CMD_USAGE_ARG; u_arg->e_arg = arg; fx_queue_push_back(&usage->u_parts, &u_arg->e_entry); return FX_SUCCESS; } fx_status fx_command_usage_add_command(fx_command_usage *usage, unsigned int cmd_id) { struct fx_command_usage_entry *u_cmd = malloc(sizeof *u_cmd); if (!u_cmd) { return FX_ERR_NO_MEMORY; } memset(u_cmd, 0x0, sizeof *u_cmd); u_cmd->e_type = CMD_USAGE_COMMAND; u_cmd->e_cmd_id = cmd_id; fx_queue_push_back(&usage->u_parts, &u_cmd->e_entry); return FX_SUCCESS; } static void prepend_command_name(struct fx_command *cmd, fx_string *out) { int nr_names = 0; cmd->c_name &&nr_names++; cmd->c_long_name &&nr_names++; cmd->c_short_name &&nr_names++; if (nr_names > 1) { fx_string_prepend_cstr(out, "}"); int r = 0; if (cmd->c_name) { fx_string_prepend_cstrf(out, "%s", cmd->c_name); r++; } if (r == 1) { fx_string_prepend_cstr(out, "|"); } if (cmd->c_long_name) { fx_string_prepend_cstrf(out, "--%s", cmd->c_long_name); r++; } if (r == 2) { fx_string_prepend_cstr(out, "|"); } if (cmd->c_short_name) { fx_string_prepend_cstrf(out, "-%c", cmd->c_short_name); r++; } fx_string_prepend_cstr(out, "{"); } else { fx_string_prepend_cstr(out, cmd->c_name); } } static void get_qualified_command_name( struct fx_command *cmd, const struct fx_arglist *args, fx_string *out) { for (unsigned int i = 0; i <= args->list_argv_last_command; i++) { if (i > 0) { fx_string_append_cstr(out, " "); } fx_string_append_cstr(out, args->list_argv[i]); } #if 0 prepend_command_name(cmd, out); cmd = cmd->c_parent; while (cmd) { fx_string_prepend_cstr(out, " "); prepend_command_name(cmd, out); cmd = cmd->c_parent; } #endif } static void get_usage_string( struct fx_command *cmd, struct fx_arglist *args, struct fx_command_usage *usage, fx_string *out) { get_qualified_command_name(cmd, args, out); fx_string *cmd_name = fx_string_create(); struct fx_queue_entry *q_entry = fx_queue_first(&usage->u_parts); while (q_entry) { struct fx_command_usage_entry *entry = fx_unbox( struct fx_command_usage_entry, q_entry, e_entry); if (!entry) { break; } fx_string_clear(cmd_name); fx_string_append_cstr(out, " "); switch (entry->e_type) { case CMD_USAGE_ARG: if (entry->e_arg) { z__fx_get_arg_usage_string(entry->e_arg, false, out); } else { fx_string_append_cstr(out, "[[ARGS]"); } break; case CMD_USAGE_OPT: if (entry->e_opt) { z__fx_get_option_usage_string( entry->e_opt, CMD_STR_DIRECT_USAGE, out); } else { fx_string_append_cstr(out, "[[OPTIONS]"); } break; case CMD_USAGE_COMMAND: if (entry->e_cmd_id == FX_COMMAND_INVALID_ID) { fx_string_append_cstr(out, "[[COMMAND]"); break; } struct fx_command *subcmd_info = get_command(&command_list, entry->e_cmd_id); prepend_command_name(subcmd_info, cmd_name); fx_string_append_s(out, cmd_name); break; default: break; } q_entry = fx_queue_next(q_entry); } fx_string_unref(cmd_name); } fx_string *z__fx_command_default_usage_string( struct fx_command *cmd, struct fx_command_option *with_opt, const struct fx_arglist *args) { fx_string *str = fx_string_create(); get_qualified_command_name(cmd, args, str); if (with_opt) { fx_string_append_cstr(str, " "); z__fx_get_option_usage_string(with_opt, CMD_STR_DIRECT_USAGE, str); } else if (!fx_queue_empty(&cmd->c_opt)) { fx_string_append_cstr(str, " [OPTIONS]"); } struct fx_queue_entry *entry = fx_queue_first(&cmd->c_arg); while (entry) { struct fx_command_arg *arg = fx_unbox(struct fx_command_arg, entry, arg_entry); fx_string_append_cstr(str, " "); z__fx_get_arg_usage_string(arg, false, str); entry = fx_queue_next(entry); } if (!fx_queue_empty(&cmd->c_subcommands)) { fx_string_append_cstr(str, " [COMMAND]"); } return str; } static void get_command_string(struct fx_command *cmd, fx_string *out) { int r = 0; if (cmd->c_name) { fx_string_append_cstrf(out, F_GREEN "%s" F_RESET, cmd->c_name); r++; } if (cmd->c_short_name) { if (r > 0) { fx_string_append_cstr(out, ", "); } fx_string_append_cstrf( out, F_GREEN "-%c" F_RESET, cmd->c_short_name); r++; } if (cmd->c_long_name) { if (r > 0) { fx_string_append_cstr(out, ", "); } fx_string_append_cstrf( out, F_GREEN "--%s" F_RESET, cmd->c_long_name); r++; } } static void get_command_description(struct fx_command *cmd, fx_string *out) { fx_string_append_cstr(out, cmd->c_description); } /* criteria for printing the option description on a separate line: * 1) the length of the usage string exceeds the newline threshold; and * 2) the length of the description string exceeds the remaining line length * (once the usage string has been printed. * or, * 3) the length of the description string is more than three terminal lines. */ #define description_on_separate_line(opt_len, desc_len) \ ((opt_len >= newline_threshold \ && desc_len >= (term_width - newline_threshold)) \ || desc_len > (3 * term_width - newline_threshold)) static void print_options_list(struct fx_command *cmd) { unsigned int term_width = 0; fx_tty_get_dimensions(OUTPUT_STREAM, &term_width, NULL); unsigned int newline_threshold = 1000000; if (term_width) { newline_threshold = term_width - 12 - 35; } fx_tty_printf(OUTPUT_STREAM, 0, "\n" F_YELLOW "OPTIONS:" F_RESET "\n"); size_t desc_margin = 0; fx_string *opt_str = fx_string_create(); fx_string *desc_str = fx_string_create(); struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt); while (entry) { struct fx_command_option *opt = fx_unbox(struct fx_command_option, entry, opt_entry); fx_string_clear(opt_str); fx_string_clear(desc_str); z__fx_get_option_usage_string(opt, CMD_STR_COLOUR, opt_str); z__fx_get_option_description(opt, desc_str); size_t opt_len = fx_string_get_size( opt_str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD) + 4; size_t desc_len = fx_string_get_size( desc_str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD); if (description_on_separate_line(opt_len, desc_len)) { goto skip; } if (opt_len > desc_margin) { desc_margin = opt_len; } skip: entry = fx_queue_next(entry); } fx_paragraph_format format = {0}; format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE; format.p_left_margin = desc_margin + 4; format.p_right_margin = 4; size_t i = 0; entry = fx_queue_first(&cmd->c_opt); while (entry) { struct fx_command_option *opt = fx_unbox(struct fx_command_option, entry, opt_entry); if (!opt) { break; } fx_string_clear(opt_str); fx_string_clear(desc_str); z__fx_get_option_usage_string(opt, CMD_STR_COLOUR, opt_str); z__fx_get_option_description(opt, desc_str); size_t opt_len = fx_string_get_size( opt_str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD) + 4; size_t desc_len = fx_string_get_size( desc_str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD); bool new_paragraph = description_on_separate_line(opt_len, desc_len); if (new_paragraph) { fx_tty_putc(OUTPUT_STREAM, 0, '\n'); } fx_tty_puts(OUTPUT_STREAM, 0, " "); fx_tty_puts(OUTPUT_STREAM, 0, fx_string_ptr(opt_str)); if (new_paragraph) { format.p_flags = 0; format.p_left_margin = 8; format.p_right_margin = 4; fx_tty_putc(OUTPUT_STREAM, 0, '\n'); } else { format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE; format.p_left_margin = desc_margin + 4; format.p_right_margin = 4; } unsigned int len = opt_len; while (len < format.p_left_margin) { fx_tty_putc(OUTPUT_STREAM, 0, ' '); len++; } fx_print_paragraph(fx_string_ptr(desc_str), OUTPUT_STREAM, &format); if (new_paragraph) { fx_tty_putc(OUTPUT_STREAM, 0, '\n'); } entry = fx_queue_next(entry); } fx_string_unref(opt_str); fx_string_unref(desc_str); } static void print_args_list(struct fx_command *cmd) { fx_tty_printf(OUTPUT_STREAM, 0, "\n" F_YELLOW "ARGS:" F_RESET "\n"); size_t desc_margin = 0; fx_string *str = fx_string_create(); struct fx_queue_entry *entry = fx_queue_first(&cmd->c_arg); while (entry) { struct fx_command_arg *arg = fx_unbox(struct fx_command_arg, entry, arg_entry); fx_string_clear(str); z__fx_get_arg_usage_string(arg, true, str); size_t len = fx_string_get_size( str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD) + 4; if (len > desc_margin) { desc_margin = len; } entry = fx_queue_next(entry); } fx_paragraph_format format = {0}; format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE; format.p_left_margin = desc_margin + 4; format.p_right_margin = 4; size_t i = 0; entry = fx_queue_first(&cmd->c_arg); while (entry) { struct fx_command_arg *arg = fx_unbox(struct fx_command_arg, entry, arg_entry); fx_string_clear(str); z__fx_get_arg_usage_string(arg, true, str); fx_tty_puts(OUTPUT_STREAM, 0, " "); fx_tty_puts(OUTPUT_STREAM, 0, fx_string_ptr(str)); unsigned int len = fx_string_get_size( str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD) + 4; while (len < format.p_left_margin) { fx_tty_putc(OUTPUT_STREAM, 0, ' '); len++; } fx_string_clear(str); z__fx_get_arg_description(arg, str); fx_print_paragraph(fx_string_ptr(str), OUTPUT_STREAM, &format); entry = fx_queue_next(entry); } fx_string_unref(str); } static void print_commands_list(struct fx_command *cmd) { fx_tty_puts(OUTPUT_STREAM, 0, "\n" F_YELLOW "COMMANDS:" F_RESET "\n"); size_t desc_margin = 0; fx_string *str = fx_string_create(); struct fx_queue_entry *entry = fx_queue_first(&cmd->c_subcommands); while (entry) { struct fx_command *sub = fx_unbox(struct fx_command, entry, c_entry); fx_string_clear(str); get_command_string(sub, str); size_t len = fx_string_get_size( str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD) + 4; if (len > desc_margin) { desc_margin = len; } entry = fx_queue_next(entry); } fx_paragraph_format format = {0}; format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE; format.p_left_margin = desc_margin + 4; format.p_right_margin = 4; size_t i = 0; entry = fx_queue_first(&cmd->c_subcommands); while (entry) { struct fx_command *sub = fx_unbox(struct fx_command, entry, c_entry); fx_string_clear(str); get_command_string(sub, str); fx_tty_puts(OUTPUT_STREAM, 0, " "); fx_tty_puts(OUTPUT_STREAM, 0, fx_string_ptr(str)); unsigned int len = fx_string_get_size( str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD) + 4; while (len < format.p_left_margin) { fx_tty_putc(OUTPUT_STREAM, 0, ' '); len++; } fx_string_clear(str); get_command_description(sub, str); fx_print_paragraph(fx_string_ptr(str), OUTPUT_STREAM, &format); entry = fx_queue_next(entry); } fx_string_unref(str); } struct fx_command *fx_command_get_subcommand_with_name( struct fx_command *cmd, const char *name) { struct fx_queue_entry *entry = fx_queue_first(&cmd->c_subcommands); while (entry) { struct fx_command *subcmd = fx_unbox(struct fx_command, entry, c_entry); if (!subcmd || !subcmd->c_name) { goto skip; } if (!strcmp(subcmd->c_name, name)) { return subcmd; } skip: entry = fx_queue_next(entry); } return NULL; } struct fx_command *fx_command_get_subcommand_with_long_name( struct fx_command *cmd, const char *long_name) { struct fx_queue_entry *entry = fx_queue_first(&cmd->c_subcommands); while (entry) { struct fx_command *subcmd = fx_unbox(struct fx_command, entry, c_entry); if (!subcmd || !subcmd->c_long_name) { goto skip; } if (!strcmp(subcmd->c_name, long_name)) { return subcmd; } skip: entry = fx_queue_next(entry); } return NULL; } struct fx_command *fx_command_get_subcommand_with_short_name( struct fx_command *cmd, char short_name) { struct fx_queue_entry *entry = fx_queue_first(&cmd->c_subcommands); while (entry) { struct fx_command *subcmd = fx_unbox(struct fx_command, entry, c_entry); if (!subcmd || !subcmd->c_short_name) { goto skip; } if (subcmd->c_short_name == short_name) { return subcmd; } skip: entry = fx_queue_next(entry); } return NULL; } struct fx_command_option *fx_command_get_option_with_long_name( struct fx_command *cmd, const char *long_name) { struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt); while (entry) { struct fx_command_option *opt = fx_unbox(struct fx_command_option, entry, opt_entry); if (!opt || !opt->opt_long_name) { goto skip; } if (!strcmp(opt->opt_long_name, long_name)) { return opt; } skip: entry = fx_queue_next(entry); } return NULL; } struct fx_command_option *fx_command_get_option_with_short_name( struct fx_command *cmd, char short_name) { struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt); while (entry) { struct fx_command_option *opt = fx_unbox(struct fx_command_option, entry, opt_entry); if (!opt || !opt->opt_long_name) { goto skip; } if (opt->opt_short_name == short_name) { return opt; } skip: entry = fx_queue_next(entry); } return NULL; } struct fx_command_option *fx_command_get_option_with_id( struct fx_command *cmd, unsigned int id) { struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt); while (entry) { struct fx_command_option *opt = fx_unbox(struct fx_command_option, entry, opt_entry); if (opt->opt_id == id) { return opt; } entry = fx_queue_next(entry); } return NULL; } struct fx_command_arg *fx_command_get_arg_with_id( struct fx_command *cmd, unsigned int id) { struct fx_queue_entry *entry = fx_queue_first(&cmd->c_arg); while (entry) { struct fx_command_arg *arg = fx_unbox(struct fx_command_arg, entry, arg_entry); if (arg->arg_id == id) { return arg; } entry = fx_queue_next(entry); } return NULL; } static void print_usage(struct fx_command *cmd, struct fx_arglist *args) { fx_paragraph_format format = {0}; format.p_left_margin = format.p_right_margin = 4; fx_tty_puts(OUTPUT_STREAM, 0, F_YELLOW "USAGE:" F_RESET "\n"); if (fx_queue_empty(&cmd->c_usage)) { fx_string *usage = z__fx_command_default_usage_string(cmd, NULL, args); fx_print_paragraph(fx_string_ptr(usage), OUTPUT_STREAM, &format); fx_string_unref(usage); return; } fx_string *str = fx_string_create(); struct fx_queue_entry *entry = fx_queue_first(&cmd->c_usage); while (entry) { struct fx_command_usage *usage = fx_unbox(struct fx_command_usage, entry, u_entry); fx_string_clear(str); get_usage_string(cmd, args, usage, str); fx_print_paragraph(fx_string_ptr(str), OUTPUT_STREAM, &format); entry = fx_queue_next(entry); } fx_string_unref(str); } static void print_help(struct fx_command *cmd, struct fx_arglist *args) { fx_paragraph_format format = {0}; if (!cmd->c_parent) { fx_tty_printf( OUTPUT_STREAM, 0, F_GREEN "%s" F_RESET "\n", cmd->c_name); } if (cmd->c_description) { fx_print_paragraph(cmd->c_description, OUTPUT_STREAM, &format); } fx_tty_putc(OUTPUT_STREAM, 0, '\n'); print_usage(cmd, args); if (!fx_queue_empty(&cmd->c_opt)) { print_options_list(cmd); } if (!fx_queue_empty(&cmd->c_arg)) { print_args_list(cmd); } if (!fx_queue_empty(&cmd->c_subcommands)) { print_commands_list(cmd); } } static int execute_command(struct fx_command *cmd, struct fx_arglist *args) { if (!cmd) { return -1; } size_t nr_items = fx_arglist_get_count( args, FX_COMMAND_INVALID_ID, FX_COMMAND_INVALID_ID); size_t nr_help = fx_arglist_get_count( args, FX_COMMAND_OPTION_HELP, FX_COMMAND_INVALID_ID); if ((cmd->c_flags & FX_COMMAND_SHOW_HELP_BY_DEFAULT) && nr_items == 0) { print_help(cmd, args); return 0; } if (nr_help > 0) { print_help(cmd, args); return 0; } if (cmd->c_callback) { return cmd->c_callback(cmd, args, NULL); } return -1; } static fx_status add_subcommand(struct fx_command *parent, struct fx_command *child) { fx_queue_push_back(&parent->c_subcommands, &child->c_entry); child->c_parent = parent; return FX_SUCCESS; } static int resolve_command_parents(struct fx_bst *commands) { struct fx_bst_node *node = fx_bst_first(commands); while (node) { struct fx_command *cmd = fx_unbox(struct fx_command, node, c_node); if (cmd->c_parent_id == FX_COMMAND_INVALID_ID) { goto skip; } cmd->c_parent = get_command(commands, cmd->c_parent_id); if (!cmd->c_parent) { return -1; } add_subcommand(cmd->c_parent, cmd); skip: node = fx_bst_next(node); } return 0; } int fx_command_dispatch(unsigned int cmd_id, int argc, const char **argv) { if (resolve_command_parents(&command_list) != 0) { return -1; } struct fx_command *cmd = get_command(&command_list, cmd_id); if (!cmd) { return -1; } struct fx_arglist *args = fx_arglist_create(); args->list_argc = argc; args->list_argv = argv; args->list_command = cmd; fx_status status = fx_arglist_parse(args, &cmd, argc, argv); if (FX_ERR(status)) { fx_arglist_destroy(args); return -1; } int ret = execute_command(cmd, args); fx_arglist_destroy(args); // print_help(cmd); return ret; }