#include "command.h" #include #include #include #include #include #include #include #include #include FX_BST_DEFINE_SIMPLE_INSERT( struct fx_arglist_option, opt_node, opt_id, put_arglist_option) FX_BST_DEFINE_SIMPLE_GET( struct fx_arglist_option, unsigned int, opt_node, opt_id, get_arglist_option) FX_BST_DEFINE_SIMPLE_INSERT( struct fx_arglist_value, val_node, val_id, put_arglist_value) FX_BST_DEFINE_SIMPLE_GET( struct fx_arglist_value, unsigned int, val_node, val_id, get_arglist_value) struct argv_parser { struct fx_command *cmd; struct fx_arglist *arglist; fx_queue_entry *arg_it; int nr_values_cur_arg; int argc; const char **argv; int index; }; static const char *peek(struct argv_parser *parser) { if (parser->index >= parser->argc) { return NULL; } return parser->argv[parser->index]; } static const char *advance(struct argv_parser *parser) { if (parser->index >= parser->argc) { return NULL; } return parser->argv[parser->index++]; } struct fx_arglist *fx_arglist_create(void) { struct fx_arglist *out = malloc(sizeof *out); if (!out) { return NULL; } memset(out, 0x0, sizeof *out); return out; } static void move_to_subcommand(struct argv_parser *parser, struct fx_command *cmd) { parser->cmd = cmd; parser->arglist->list_command = cmd; parser->arg_it = fx_queue_first(&cmd->c_arg); parser->nr_values_cur_arg = 0; } static fx_status set_opt(struct fx_arglist *args, struct fx_command_option *opt) { struct fx_arglist_option *arg_opt = malloc(sizeof *arg_opt); if (!arg_opt) { return FX_ERR_NO_MEMORY; } memset(arg_opt, 0x0, sizeof *arg_opt); arg_opt->opt_id = opt->opt_id; put_arglist_option(&args->list_options, arg_opt); return FX_SUCCESS; } static fx_status put_arg( struct fx_arglist *args, struct fx_command_arg *arg, const char *value) { struct fx_arglist_option *arglist_opt = get_arglist_option(&args->list_options, FX_COMMAND_INVALID_ID); if (!arglist_opt) { arglist_opt = malloc(sizeof *arglist_opt); if (!arglist_opt) { return FX_ERR_NO_MEMORY; } memset(arglist_opt, 0x0, sizeof *arglist_opt); arglist_opt->opt_id = FX_COMMAND_INVALID_ID; put_arglist_option(&args->list_options, arglist_opt); } if (arg->arg_allowed_values) { bool value_ok = false; for (int i = 0; arg->arg_allowed_values[i]; i++) { if (!strcmp(value, arg->arg_allowed_values[i])) { value_ok = true; break; } } if (!value_ok) { return FX_ERR_INVALID_ARGUMENT; } } struct fx_arglist_value *val = malloc(sizeof *val); if (!val) { return FX_ERR_NO_MEMORY; } memset(val, 0x0, sizeof *val); val->val_id = arg->arg_id; val->val_type = FX_COMMAND_ARG_STRING; val->val_str = fx_strdup(value); put_arglist_value(&arglist_opt->opt_values, val); return FX_SUCCESS; } static fx_status put_opt_arg( struct fx_arglist_option *arglist_opt, struct fx_command_option *opt, struct fx_command_arg *arg, const char *value) { if (arg->arg_allowed_values) { bool value_ok = false; for (int i = 0; arg->arg_allowed_values[i]; i++) { if (!strcmp(value, arg->arg_allowed_values[i])) { value_ok = true; break; } } if (!value_ok) { return FX_ERR_INVALID_ARGUMENT; } } struct fx_arglist_value *val = malloc(sizeof *val); if (!val) { return FX_ERR_NO_MEMORY; } memset(val, 0x0, sizeof *val); val->val_id = arg->arg_id; val->val_type = FX_COMMAND_ARG_STRING; val->val_str = fx_strdup(value); put_arglist_value(&arglist_opt->opt_values, val); return FX_SUCCESS; } #if 0 static void report_missing_args( struct argv_parser *parser, struct fx_command_option *opt, struct fx_command_arg *arg, int args_supplied) { } static void report_unexpected_arg(struct argv_parser *parser, const char *value) { } #endif static fx_status check_required_args(struct argv_parser *parser) { if (!parser->arg_it) { return FX_SUCCESS; } struct fx_command_arg *arg = fx_unbox(struct fx_command_arg, parser->arg_it, arg_entry); if (!arg) { return FX_SUCCESS; } if (arg->arg_nr_values == FX_ARG_0_OR_MORE_VALUES) { return FX_SUCCESS; } if (arg->arg_nr_values == FX_ARG_1_OR_MORE_VALUES) { if (parser->nr_values_cur_arg > 0) { return FX_SUCCESS; } fx_arglist_report_missing_args( parser->arglist, FX_COMMAND_INVALID_ID, arg->arg_id, parser->nr_values_cur_arg); return FX_ERR_BAD_FORMAT; } if (parser->nr_values_cur_arg != arg->arg_nr_values) { fx_arglist_report_missing_args( parser->arglist, FX_COMMAND_INVALID_ID, arg->arg_id, parser->nr_values_cur_arg); return FX_ERR_BAD_FORMAT; } return FX_SUCCESS; } static fx_status parse_arg(struct argv_parser *parser) { while (1) { const char *value = peek(parser); if (!value || value[0] == '-') { break; } struct fx_command *subcmd = fx_command_get_subcommand_with_name(parser->cmd, value); if (subcmd) { move_to_subcommand(parser, subcmd); parser->arglist->list_argv_last_command = parser->index + 1; advance(parser); continue; } struct fx_command_arg *arg = fx_unbox( struct fx_command_arg, parser->arg_it, arg_entry); if (!arg) { fx_arglist_report_unexpected_arg(parser->arglist, value); return FX_ERR_BAD_FORMAT; } fx_status status = put_arg(parser->arglist, arg, value); parser->nr_values_cur_arg++; advance(parser); if (status == FX_ERR_INVALID_ARGUMENT) { fx_arglist_report_invalid_arg_value( parser->arglist, FX_COMMAND_INVALID_ID, arg->arg_id, value); } if (FX_ERR(status)) { return status; } if (arg->arg_nr_values == FX_ARG_0_OR_1_VALUES) { parser->arg_it = fx_queue_next(parser->arg_it); parser->nr_values_cur_arg = 0; continue; } if (parser->nr_values_cur_arg == arg->arg_nr_values) { parser->arg_it = fx_queue_next(parser->arg_it); parser->nr_values_cur_arg = 0; continue; } } return FX_SUCCESS; } static struct fx_command_arg *get_first_arg(struct fx_command *cmd) { struct fx_command_arg *arg = NULL; struct fx_queue_entry *first_arg = fx_queue_first(&cmd->c_arg); if (first_arg) { arg = fx_unbox(struct fx_command_arg, first_arg, arg_entry); } return arg; } static fx_status parse_short_opt(struct argv_parser *parser) { const char *flags = peek(parser); flags += 1; struct fx_command_option *opt = NULL; struct fx_command_arg *arg = NULL; struct fx_command *subcmd = NULL; int nr_args_cur_opt = 0; while (1) { char flag = *flags; if (!flag) { break; } subcmd = NULL; opt = fx_command_get_option_with_short_name(parser->cmd, flag); if (!opt) { subcmd = fx_command_get_subcommand_with_short_name( parser->cmd, flag); } if (subcmd) { move_to_subcommand(parser, subcmd); parser->arglist->list_argv_last_command = parser->index + 1; flags++; continue; } if (!opt) { fx_string *usage = z__fx_command_default_usage_string( parser->cmd, NULL, parser->arglist); fx_err("unrecognised argument '" F_YELLOW "-%c" F_RESET "'\n\n", flag, fx_string_ptr(usage)); fx_i("usage: %s", fx_string_ptr(usage)); fx_i("for more information, use '" F_YELLOW "--help" F_RESET "'\n"); fx_string_unref(usage); return FX_ERR_NO_ENTRY; } flags++; if (fx_queue_empty(&opt->opt_args)) { set_opt(parser->arglist, opt); continue; } break; } if (!opt || fx_queue_empty(&opt->opt_args)) { advance(parser); return FX_SUCCESS; } struct fx_arglist_option *arglist_opt = malloc(sizeof *arglist_opt); if (!arglist_opt) { return FX_ERR_NO_MEMORY; } memset(arglist_opt, 0x0, sizeof *arglist_opt); arglist_opt->opt_id = opt->opt_id; put_arglist_option(&parser->arglist->list_options, arglist_opt); struct fx_queue_entry *entry = fx_queue_first(&opt->opt_args); const char *value = flags; if (*value == '\0') { advance(parser); } while (entry) { value = peek(parser); if (!value || *value == '-') { value = NULL; } arg = fx_unbox(struct fx_command_arg, entry, arg_entry); if (!value || *value == '\0' || *value == '-') { value = NULL; } if (value) { fx_status status = put_opt_arg(arglist_opt, opt, arg, value); nr_args_cur_opt++; if (status == FX_ERR_INVALID_ARGUMENT) { fx_arglist_report_invalid_arg_value( parser->arglist, FX_COMMAND_INVALID_ID, arg->arg_id, value); } if (FX_ERR(status)) { return status; } } if (arg->arg_nr_values == FX_ARG_0_OR_1_VALUES) { nr_args_cur_opt = 0; goto next_value; } if (arg->arg_nr_values == FX_ARG_0_OR_MORE_VALUES && !value) { nr_args_cur_opt = 0; goto next_value; } if (arg->arg_nr_values == FX_ARG_1_OR_MORE_VALUES && !value) { if (nr_args_cur_opt > 0) { nr_args_cur_opt = 0; goto next_value; } fx_arglist_report_missing_args( parser->arglist, opt->opt_id, arg->arg_id, nr_args_cur_opt); return FX_ERR_BAD_FORMAT; } if (nr_args_cur_opt == arg->arg_nr_values) { nr_args_cur_opt = 0; goto next_value; } if (!value) { fx_arglist_report_missing_args( parser->arglist, opt->opt_id, arg->arg_id, nr_args_cur_opt); return FX_ERR_BAD_FORMAT; } next_value: entry = fx_queue_next(entry); value = advance(parser); } return FX_SUCCESS; } static fx_status parse_long_opt(struct argv_parser *parser) { const char *opt_name = peek(parser); opt_name += 2; struct fx_command_option *opt = NULL; struct fx_command *subcmd = NULL; opt = fx_command_get_option_with_long_name(parser->cmd, opt_name); if (!opt) { subcmd = fx_command_get_subcommand_with_long_name( parser->cmd, opt_name); } if (!opt && !subcmd) { fx_string *usage = z__fx_command_default_usage_string( parser->cmd, NULL, parser->arglist); fx_err("unrecognised argument '" F_YELLOW "--%s" F_RESET "'\n\nusage: %s\n\nfor more information, use '" F_YELLOW "--help" F_RESET "'\n", opt_name, fx_string_ptr(usage)); fx_string_unref(usage); return FX_ERR_NO_ENTRY; } advance(parser); if (subcmd) { move_to_subcommand(parser, subcmd); parser->arglist->list_argv_last_command = parser->index + 1; return FX_SUCCESS; } if (fx_queue_empty(&opt->opt_args)) { return set_opt(parser->arglist, opt); } int nr_args_total = 0; int nr_args_cur_opt = 0; struct fx_arglist_option *arglist_opt = malloc(sizeof *arglist_opt); if (!arglist_opt) { return FX_ERR_NO_MEMORY; } memset(arglist_opt, 0x0, sizeof *arglist_opt); arglist_opt->opt_id = opt->opt_id; put_arglist_option(&parser->arglist->list_options, arglist_opt); struct fx_queue_entry *entry = fx_queue_first(&opt->opt_args); struct fx_command_arg *arg = NULL; while (entry) { arg = fx_unbox(struct fx_command_arg, entry, arg_entry); const char *value = peek(parser); if (!value || value[0] == '-') { value = NULL; } if (value) { fx_status status = put_opt_arg(arglist_opt, opt, arg, value); nr_args_total++; nr_args_cur_opt++; advance(parser); if (status == FX_ERR_INVALID_ARGUMENT) { fx_arglist_report_invalid_arg_value( parser->arglist, FX_COMMAND_INVALID_ID, arg->arg_id, value); } if (FX_ERR(status)) { return status; } } if (arg->arg_nr_values == FX_ARG_0_OR_1_VALUES) { entry = fx_queue_next(entry); nr_args_cur_opt = 0; continue; } if (arg->arg_nr_values == FX_ARG_0_OR_MORE_VALUES && !value) { entry = fx_queue_next(entry); nr_args_cur_opt = 0; continue; } if (arg->arg_nr_values == FX_ARG_1_OR_MORE_VALUES && !value) { if (nr_args_cur_opt > 0) { entry = fx_queue_next(entry); nr_args_cur_opt = 0; continue; } fx_arglist_report_missing_args( parser->arglist, opt->opt_id, arg->arg_id, nr_args_cur_opt); return FX_ERR_BAD_FORMAT; } if (nr_args_cur_opt == arg->arg_nr_values) { entry = fx_queue_next(entry); nr_args_cur_opt = 0; continue; } if (!value) { fx_arglist_report_missing_args( parser->arglist, opt->opt_id, arg->arg_id, nr_args_cur_opt); return FX_ERR_BAD_FORMAT; } } return FX_SUCCESS; } static bool should_show_help(struct fx_command *cmd, struct fx_arglist *args) { bool show_help = false; size_t count = 0; fx_arglist_iterator it; fx_arglist_foreach(&it, args) { if (it.opt_id == FX_COMMAND_OPTION_HELP) { show_help = true; break; } count++; } if (count == 0 && (cmd->c_flags & FX_COMMAND_SHOW_HELP_BY_DEFAULT)) { show_help = true; } return show_help; } fx_status fx_arglist_parse( struct fx_arglist *args, struct fx_command **cmd, int argc, const char **argv) { struct argv_parser parser = { .arglist = args, .argc = argc - 1, .argv = argv + 1, }; move_to_subcommand(&parser, *cmd); fx_status status = FX_SUCCESS; bool arg_only = false; while (1) { const char *arg = peek(&parser); if (!arg) { break; } size_t len = strlen(arg); status = FX_SUCCESS; if (len < 2 || arg_only) { status = parse_arg(&parser); } else if (arg[0] == '-' && arg[1] != '-') { status = parse_short_opt(&parser); } else if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') { status = FX_SUCCESS; arg_only = true; advance(&parser); continue; } else if (arg[0] == '-' && arg[1] == '-' && arg[2] != '-') { status = parse_long_opt(&parser); } else { status = parse_arg(&parser); } if (FX_ERR(status)) { return status; } } bool show_help = should_show_help(parser.cmd, args); if (!show_help) { status = check_required_args(&parser); } if (FX_ERR(status)) { return status; } *cmd = parser.cmd; return FX_SUCCESS; } static void arglist_option_destroy(struct fx_arglist_option *opt) { free(opt); } static void arglist_value_destroy(struct fx_arglist_value *val) { if (val->val_type == FX_COMMAND_ARG_STRING) { free(val->val_str); } free(val); } void fx_arglist_destroy(struct fx_arglist *args) { fx_bst_node *opt_it, *args_it; fx_bst_node *opt_next, *args_next; opt_it = fx_bst_first(&args->list_options); while (opt_it) { struct fx_arglist_option *opt = fx_unbox(struct fx_arglist_option, opt_it, opt_node); opt_next = fx_bst_next(opt_it); args_it = fx_bst_first(&opt->opt_values); while (args_it) { struct fx_arglist_value *val = fx_unbox( struct fx_arglist_value, args_it, val_node); args_next = fx_bst_next(args_it); if (val) { fx_bst_delete(&opt->opt_values, args_it); arglist_value_destroy(val); } args_it = args_next; } fx_bst_delete(&args->list_options, opt_it); arglist_option_destroy(opt); opt_it = opt_next; } free(args); } fx_status fx_arglist_get_string( const fx_arglist *args, unsigned int opt_id, unsigned int arg_id, unsigned int index, const char **out) { fx_arglist_iterator it = {0}; fx_arglist_iterator_begin(args, opt_id, arg_id, &it); while (fx_arglist_iterator_is_valid(&it)) { if (index > 0) { index--; fx_arglist_iterator_next(&it); continue; } if (it.value && it.value->val_str) { *out = it.value->val_str; return FX_SUCCESS; } fx_arglist_iterator_next(&it); } return FX_ERR_NO_ENTRY; } fx_status fx_arglist_get_int( const fx_arglist *args, unsigned int opt_id, unsigned int arg_id, unsigned int index, long long *out) { fx_arglist_iterator it = {0}; fx_arglist_iterator_begin(args, opt_id, arg_id, &it); while (fx_arglist_iterator_is_valid(&it)) { if (index > 0) { index--; fx_arglist_iterator_next(&it); continue; } if (it.value) { *out = it.value->val_int; return FX_SUCCESS; } fx_arglist_iterator_next(&it); } return FX_ERR_NO_ENTRY; } fx_status fx_arglist_get_uint( const fx_arglist *args, unsigned int opt_id, unsigned int arg_id, unsigned int index, unsigned long long *out) { fx_arglist_iterator it = {0}; fx_arglist_iterator_begin(args, opt_id, arg_id, &it); while (fx_arglist_iterator_is_valid(&it)) { if (index > 0) { index--; fx_arglist_iterator_next(&it); continue; } if (it.value && it.value->val_uint) { *out = it.value->val_uint; return FX_SUCCESS; } fx_arglist_iterator_next(&it); } return FX_ERR_NO_ENTRY; } fx_status fx_arglist_get_option( const fx_arglist *args, unsigned int opt_id, unsigned int index, fx_arglist_option **out) { struct fx_bst_node *node = fx_bst_first(&args->list_options); while (node) { fx_arglist_option *cur = fx_unbox(fx_arglist_option, node, opt_node); if (cur->opt_id != opt_id) { node = fx_bst_next(node); continue; } if (index == 0) { *out = cur; return FX_SUCCESS; } node = fx_bst_next(node); index--; } return FX_ERR_NO_ENTRY; } size_t fx_arglist_get_count( const fx_arglist *args, unsigned int opt_id, unsigned int arg_id) { size_t count = 0; fx_arglist_iterator it; fx_arglist_iterator_begin(args, opt_id, arg_id, &it); while (fx_arglist_iterator_is_valid(&it)) { count++; fx_arglist_iterator_next(&it); } return count; } fx_status fx_arglist_option_get_value( const fx_arglist_option *opt, unsigned int arg_id, unsigned int index, fx_arglist_value **out) { struct fx_bst_node *node = fx_bst_first(&opt->opt_values); while (node) { fx_arglist_value *cur = fx_unbox(fx_arglist_value, node, val_node); if (cur->val_id != arg_id) { node = fx_bst_next(node); continue; } if (index == 0) { *out = cur; return FX_SUCCESS; } node = fx_bst_next(node); index--; } return FX_ERR_NO_ENTRY; } /************************ arglist iterator functions **************************/ static struct fx_arglist_option *advance_to_next_opt(struct fx_arglist_iterator *it) { struct fx_arglist_option *opt; // fx_bst_iterator_next(&it->_opt_it); while (it->_opt_it) { opt = fx_unbox(struct fx_arglist_option, it->_opt_it, opt_node); if (opt && (opt->opt_id == it->_opt_filter || it->_opt_filter == FX_COMMAND_INVALID_ID)) { it->opt_id = opt->opt_id; return opt; } it->_opt_it = fx_bst_next(it->_opt_it); } return NULL; } static struct fx_arglist_value *advance_to_next_arg(struct fx_arglist_iterator *it) { struct fx_arglist_value *val; // fx_bst_iterator_next(&it->_arg_it); while (it->_arg_it) { val = fx_unbox(struct fx_arglist_value, it->_arg_it, val_node); if (val && (val->val_id == it->_arg_filter || it->_arg_filter == FX_COMMAND_INVALID_ID)) { it->value = val; return val; } it->_arg_it = fx_bst_next(it->_arg_it); } return NULL; } int fx_arglist_iterator_begin( const struct fx_arglist *args, unsigned int opt_filter, unsigned int arg_filter, struct fx_arglist_iterator *it) { memset(it, 0x0, sizeof *it); it->opt_id = FX_COMMAND_INVALID_ID; it->_opt_filter = opt_filter; it->_arg_filter = arg_filter; it->_opt_it = fx_bst_first(&args->list_options); struct fx_arglist_option *opt = NULL; struct fx_arglist_value *val = NULL; while (1) { if (!it->_opt_it) { opt = NULL; return -1; } opt = fx_unbox(struct fx_arglist_option, it->_opt_it, opt_node); if (!opt || (opt_filter != opt->opt_id && opt_filter != FX_COMMAND_INVALID_ID)) { it->_opt_it = fx_bst_next(it->_opt_it); continue; } it->_arg_it = fx_bst_first(&opt->opt_values); bool done = false; while (1) { if (!it->_arg_it) { if (arg_filter == FX_COMMAND_INVALID_ID) { done = true; } break; } val = fx_unbox( struct fx_arglist_value, it->_arg_it, val_node); if (!val || (arg_filter != val->val_id && arg_filter != FX_COMMAND_INVALID_ID)) { it->_arg_it = fx_bst_next(it->_arg_it); continue; } done = true; break; } if (done) { break; } it->_opt_it = fx_bst_next(it->_opt_it); } it->opt_id = opt->opt_id; it->value = val; return 0; } bool fx_arglist_iterator_next(struct fx_arglist_iterator *it) { struct fx_arglist_option *opt; struct fx_arglist_value *val; it->_arg_it = fx_bst_next(it->_arg_it); while (1) { val = advance_to_next_arg(it); if (val) { it->value = val; it->i++; return true; } it->_opt_it = fx_bst_next(it->_opt_it); opt = advance_to_next_opt(it); if (!opt) { it->value = NULL; it->opt_id = FX_COMMAND_INVALID_ID; return false; } it->_arg_it = fx_bst_first(&opt->opt_values); if (it->_arg_filter == FX_COMMAND_INVALID_ID) { return true; } } } bool fx_arglist_iterator_is_valid(const struct fx_arglist_iterator *it) { return it->opt_id != FX_COMMAND_INVALID_ID || it->value != NULL; } /********************* arglist option iterator functions **********************/ static struct fx_arglist_option *advance_to_next_opt2( struct fx_arglist_option_iterator *it) { struct fx_arglist_option *opt; while (it->_opt_it) { opt = fx_unbox(struct fx_arglist_option, it->_opt_it, opt_node); if (opt && (opt->opt_id == it->_opt_filter || it->_opt_filter == FX_COMMAND_INVALID_ID)) { it->opt_id = opt->opt_id; return opt; } it->_opt_it = fx_bst_next(it->_opt_it); } return NULL; } int fx_arglist_option_iterator_begin( const struct fx_arglist *args, unsigned int opt_filter, struct fx_arglist_option_iterator *it) { memset(it, 0x0, sizeof *it); it->opt_id = FX_COMMAND_INVALID_ID; it->_opt_filter = opt_filter; it->_opt_it = fx_bst_first(&args->list_options); struct fx_arglist_option *opt = NULL; struct fx_arglist_value *val = NULL; while (1) { if (!it->_opt_it) { opt = NULL; return -1; } opt = fx_unbox(struct fx_arglist_option, it->_opt_it, opt_node); if (opt && (opt_filter == opt->opt_id || opt_filter == FX_COMMAND_INVALID_ID)) { break; } it->_opt_it = fx_bst_next(it->_opt_it); } it->opt_id = opt->opt_id; it->opt = opt; return 0; } bool fx_arglist_option_iterator_next(struct fx_arglist_option_iterator *it) { struct fx_arglist_option *opt; struct fx_arglist_value *val; it->_opt_it = fx_bst_next(it->_opt_it); opt = advance_to_next_opt2(it); if (!opt) { it->opt = NULL; it->opt_id = FX_COMMAND_INVALID_ID; return false; } it->opt = opt; it->opt_id = opt->opt_id; return true; } bool fx_arglist_option_iterator_is_valid(const struct fx_arglist_option_iterator *it) { return it->opt_id != FX_COMMAND_INVALID_ID || it->opt != NULL; }