From 5d6423057a6e4c205f9ca186e38a32e88131a28f Mon Sep 17 00:00:00 2001 From: Max Wash Date: Thu, 17 Jul 2025 17:56:00 +0100 Subject: [PATCH] cmd: add functions to report option/arg errors to the public API --- cmd/arglist.c | 240 +++++++++++++++++++++++------------------ cmd/command.c | 155 ++++++++++++++------------ cmd/command.h | 39 ++++--- cmd/include/blue/cmd.h | 47 +++++++- cmd/option.c | 19 ++++ cmd/report.c | 193 +++++++++++++++++++++++++++++++++ 6 files changed, 498 insertions(+), 195 deletions(-) create mode 100644 cmd/report.c diff --git a/cmd/arglist.c b/cmd/arglist.c index 9327284..6fc9e58 100644 --- a/cmd/arglist.c +++ b/cmd/arglist.c @@ -65,6 +65,7 @@ struct b_arglist *b_arglist_create(void) static void move_to_subcommand(struct argv_parser *parser, struct b_command *cmd) { parser->cmd = cmd; + parser->arglist->list_command = cmd; b_queue_iterator_begin(&cmd->b_arg, &parser->arg_it); parser->nr_values_cur_arg = 0; } @@ -84,48 +85,6 @@ static b_status set_opt(struct b_arglist *args, struct b_command_option *opt) return B_SUCCESS; } -static void report_invalid_arg_value( - struct argv_parser *parser, struct b_command_option *opt, - struct b_command_arg *arg, const char *value) -{ - struct b_string *usage - = z__b_command_default_usage_string(parser->cmd, opt); - struct b_string *opt_string = b_string_create(); - - if (opt) { - z__b_get_option_usage_string(opt, 0, opt_string); - } else { - z__b_get_arg_usage_string(arg, 0, opt_string); - } - - b_err("invalid value '" F_YELLOW "%s" F_RESET "' for '" F_YELLOW - "%s" F_RESET "'.", - value, b_string_ptr(opt_string)); - - if (opt) { - b_i("'" F_YELLOW "%s" F_RESET - "' accepts the following values for '" F_YELLOW "%s" F_RESET - "':", - b_string_ptr(opt_string), arg->arg_name); - } else { - b_i("'" F_YELLOW "%s" F_RESET "' accepts the following values:", - b_string_ptr(opt_string)); - } - - for (int i = 0; arg->arg_allowed_values[i]; i++) { - b_printf( - " * " F_GREEN "%s" F_RESET "\n", - arg->arg_allowed_values[i]); - } - - b_printf("\n"); - b_i("usage: %s", b_string_ptr(usage)); - b_i("for more information, use '" F_YELLOW "--help" F_RESET); - - b_string_release(usage); - b_string_release(opt_string); -} - static b_status put_arg( struct b_arglist *args, struct b_command_arg *arg, const char *value) { @@ -209,70 +168,17 @@ static b_status put_opt_arg( return B_SUCCESS; } +#if 0 static void report_missing_args( struct argv_parser *parser, struct b_command_option *opt, struct b_command_arg *arg, int args_supplied) { - struct b_string *usage - = z__b_command_default_usage_string(parser->cmd, opt); - struct b_string *opt_string = b_string_create(); - - if (opt) { - z__b_get_option_usage_string(opt, 0, opt_string); - } else { - z__b_get_arg_usage_string(arg, 0, opt_string); - } - - char supplied_arg_str[64]; - if (args_supplied == 0) { - snprintf( - supplied_arg_str, sizeof supplied_arg_str, - F_RED_BOLD "none" F_RESET " were provided"); - } else if (args_supplied == 1) { - snprintf( - supplied_arg_str, sizeof supplied_arg_str, - "only " F_YELLOW_BOLD "%d" F_RESET " was provided", - args_supplied); - } else { - snprintf( - supplied_arg_str, sizeof supplied_arg_str, - "only " F_YELLOW_BOLD "%d" F_RESET " were provided", - args_supplied); - } - - char required_arg_count[64]; - switch (arg->arg_nr_values) { - case B_ARG_1_OR_MORE_VALUES: - snprintf( - required_arg_count, sizeof required_arg_count, - "one or more"); - break; - default: - snprintf( - required_arg_count, sizeof required_arg_count, "%d", - arg->arg_nr_values); - } - - b_err("argument '" F_YELLOW "%s" F_RESET "' requires " F_GREEN_BOLD - "%s" F_RESET " `" F_YELLOW "%s" F_RESET "` values, but %s.\n\n", - b_string_ptr(opt_string), required_arg_count, arg->arg_name, - supplied_arg_str); - b_i("usage: %s", b_string_ptr(usage)); - b_i("for more information, use '" F_YELLOW "--help" F_RESET "'"); - - b_string_release(usage); - b_string_release(opt_string); } static void report_unexpected_arg(struct argv_parser *parser, const char *value) { - struct b_string *usage - = z__b_command_default_usage_string(parser->cmd, NULL); - - b_err("unexpected argument '" F_YELLOW "%s" F_RESET "' found.", value); - b_i("usage: %s", b_string_ptr(usage)); - b_i("for more information, use '" F_YELLOW "--help" F_RESET "'"); } +#endif static b_status check_required_args(struct argv_parser *parser) { @@ -295,12 +201,16 @@ static b_status check_required_args(struct argv_parser *parser) return B_SUCCESS; } - report_missing_args(parser, NULL, arg, parser->nr_values_cur_arg); + b_arglist_report_missing_args( + parser->arglist, B_COMMAND_INVALID_ID, arg->arg_id, + parser->nr_values_cur_arg); return B_ERR_BAD_FORMAT; } if (parser->nr_values_cur_arg != arg->arg_nr_values) { - report_missing_args(parser, NULL, arg, parser->nr_values_cur_arg); + b_arglist_report_missing_args( + parser->arglist, B_COMMAND_INVALID_ID, arg->arg_id, + parser->nr_values_cur_arg); return B_ERR_BAD_FORMAT; } @@ -327,7 +237,7 @@ static b_status parse_arg(struct argv_parser *parser) struct b_command_arg, parser->arg_it.entry, arg_entry); if (!arg) { - report_unexpected_arg(parser, value); + b_arglist_report_unexpected_arg(parser->arglist, value); return B_ERR_BAD_FORMAT; } @@ -336,7 +246,9 @@ static b_status parse_arg(struct argv_parser *parser) advance(parser); if (status == B_ERR_INVALID_ARGUMENT) { - report_invalid_arg_value(parser, NULL, arg, value); + b_arglist_report_invalid_arg_value( + parser->arglist, B_COMMAND_INVALID_ID, + arg->arg_id, value); } if (B_ERR(status)) { @@ -471,7 +383,9 @@ static b_status parse_short_opt(struct argv_parser *parser) nr_args_cur_opt++; if (status == B_ERR_INVALID_ARGUMENT) { - report_invalid_arg_value(parser, NULL, arg, value); + b_arglist_report_invalid_arg_value( + parser->arglist, B_COMMAND_INVALID_ID, + arg->arg_id, value); } if (B_ERR(status)) { @@ -498,7 +412,9 @@ static b_status parse_short_opt(struct argv_parser *parser) goto next_value; } - report_missing_args(parser, opt, arg, nr_args_cur_opt); + b_arglist_report_missing_args( + parser->arglist, opt->opt_id, arg->arg_id, + nr_args_cur_opt); return B_ERR_BAD_FORMAT; } @@ -509,7 +425,9 @@ static b_status parse_short_opt(struct argv_parser *parser) } if (!value) { - report_missing_args(parser, opt, arg, nr_args_cur_opt); + b_arglist_report_missing_args( + parser->arglist, opt->opt_id, arg->arg_id, + nr_args_cur_opt); return B_ERR_BAD_FORMAT; } next_value: @@ -594,7 +512,9 @@ static b_status parse_long_opt(struct argv_parser *parser) advance(parser); if (status == B_ERR_INVALID_ARGUMENT) { - report_invalid_arg_value(parser, NULL, arg, value); + b_arglist_report_invalid_arg_value( + parser->arglist, B_COMMAND_INVALID_ID, + arg->arg_id, value); } if (B_ERR(status)) { @@ -621,7 +541,9 @@ static b_status parse_long_opt(struct argv_parser *parser) continue; } - report_missing_args(parser, opt, arg, nr_args_cur_opt); + b_arglist_report_missing_args( + parser->arglist, opt->opt_id, arg->arg_id, + nr_args_cur_opt); return B_ERR_BAD_FORMAT; } @@ -632,7 +554,9 @@ static b_status parse_long_opt(struct argv_parser *parser) } if (!value) { - report_missing_args(parser, opt, arg, nr_args_cur_opt); + b_arglist_report_missing_args( + parser->arglist, opt->opt_id, arg->arg_id, + nr_args_cur_opt); return B_ERR_BAD_FORMAT; } } @@ -882,6 +806,8 @@ b_status b_arglist_option_get_value( return B_ERR_NO_ENTRY; } +/************************ arglist iterator functions **************************/ + static bool arglist_iterator_next(struct b_iterator *it) { return b_arglist_iterator_next((struct b_arglist_iterator *)it); @@ -1043,3 +969,103 @@ bool b_arglist_iterator_is_valid(const struct b_arglist_iterator *it) { return it->opt_id != B_COMMAND_INVALID_ID || it->value != NULL; } + +/********************* arglist option iterator functions **********************/ + +static bool arglist_option_iterator_next(struct b_iterator *it) +{ + return b_arglist_option_iterator_next( + (struct b_arglist_option_iterator *)it); +} + +static bool arglist_option_iterator_is_valid(const struct b_iterator *it) +{ + return b_arglist_option_iterator_is_valid( + (const struct b_arglist_option_iterator *)it); +} + +static struct b_arglist_option *advance_to_next_opt2( + struct b_arglist_option_iterator *it) +{ + struct b_arglist_option *opt; + + // b_btree_iterator_next(&it->_opt_it); + + while (b_btree_iterator_is_valid(&it->_opt_it)) { + opt = b_unbox(struct b_arglist_option, it->_opt_it.node, opt_node); + if (opt + && (opt->opt_id == it->_opt_filter + || it->_opt_filter == B_COMMAND_INVALID_ID)) { + it->opt_id = opt->opt_id; + return opt; + } + + b_btree_iterator_next(&it->_opt_it); + } + + return NULL; +} + +static b_iterator_ops option_it_ops = { + .it_next = arglist_option_iterator_next, + .it_is_valid = arglist_option_iterator_is_valid, +}; + +int b_arglist_option_iterator_begin( + const struct b_arglist *args, unsigned int opt_filter, + struct b_arglist_option_iterator *it) +{ + memset(it, 0x0, sizeof *it); + + it->opt_id = B_COMMAND_INVALID_ID; + + it->_base.it_ops = &it_ops; + it->_opt_filter = opt_filter; + + b_btree_iterator_begin(&args->list_options, &it->_opt_it); + struct b_arglist_option *opt = NULL; + struct b_arglist_value *val = NULL; + + while (1) { + if (!b_btree_iterator_is_valid(&it->_opt_it)) { + opt = NULL; + return -1; + } + + opt = b_unbox(struct b_arglist_option, it->_opt_it.node, opt_node); + if (opt + && (opt_filter == opt->opt_id + || opt_filter == B_COMMAND_INVALID_ID)) { + break; + } + b_btree_iterator_next(&it->_opt_it); + } + + it->opt_id = opt->opt_id; + it->opt = opt; + + return 0; +} + +bool b_arglist_option_iterator_next(struct b_arglist_option_iterator *it) +{ + struct b_arglist_option *opt; + struct b_arglist_value *val; + + b_btree_iterator_next(&it->_opt_it); + opt = advance_to_next_opt2(it); + if (!opt) { + it->opt = NULL; + it->opt_id = B_COMMAND_INVALID_ID; + return false; + } + + it->opt = opt; + it->opt_id = opt->opt_id; + return true; +} + +bool b_arglist_option_iterator_is_valid(const struct b_arglist_option_iterator *it) +{ + return it->opt_id != B_COMMAND_INVALID_ID || it->opt != NULL; +} diff --git a/cmd/command.c b/cmd/command.c index 3269835..60bf16e 100644 --- a/cmd/command.c +++ b/cmd/command.c @@ -55,10 +55,10 @@ static void command_usage_destroy(struct b_command_usage *usage) { struct b_queue_iterator it = {0}; - b_queue_iterator_begin(&usage->u_arg, &it); + b_queue_iterator_begin(&usage->u_parts, &it); while (b_queue_iterator_is_valid(&it)) { - struct b_command_usage_arg *arg = b_unbox( - struct b_command_usage_arg, it.entry, arg_entry); + struct b_command_usage_entry *arg = b_unbox( + struct b_command_usage_entry, it.entry, e_entry); if (!arg) { continue; } @@ -67,18 +67,6 @@ static void command_usage_destroy(struct b_command_usage *usage) free(arg); } - b_queue_iterator_begin(&usage->u_opt, &it); - while (b_queue_iterator_is_valid(&it)) { - struct b_command_usage_opt *opt = b_unbox( - struct b_command_usage_opt, it.entry, opt_entry); - if (!opt) { - continue; - } - - b_queue_iterator_erase(&it); - free(opt); - } - free(usage); } @@ -277,47 +265,50 @@ struct b_command_usage *b_command_add_usage(struct b_command *cmd) b_status b_command_usage_add_option( struct b_command_usage *usage, struct b_command_option *opt) { - struct b_command_usage_opt *u_opt = malloc(sizeof *u_opt); + struct b_command_usage_entry *u_opt = malloc(sizeof *u_opt); if (!u_opt) { return B_ERR_NO_MEMORY; } memset(u_opt, 0x0, sizeof *u_opt); - u_opt->opt = opt; + u_opt->e_type = CMD_USAGE_OPT; + u_opt->e_opt = opt; - b_queue_push_back(&usage->u_opt, &u_opt->opt_entry); + b_queue_push_back(&usage->u_parts, &u_opt->e_entry); return B_SUCCESS; } b_status b_command_usage_add_arg( struct b_command_usage *usage, struct b_command_arg *arg) { - struct b_command_usage_arg *u_arg = malloc(sizeof *u_arg); + struct b_command_usage_entry *u_arg = malloc(sizeof *u_arg); if (!u_arg) { return B_ERR_NO_MEMORY; } memset(u_arg, 0x0, sizeof *u_arg); - u_arg->arg = arg; + u_arg->e_type = CMD_USAGE_ARG; + u_arg->e_arg = arg; - b_queue_push_back(&usage->u_arg, &u_arg->arg_entry); + b_queue_push_back(&usage->u_parts, &u_arg->e_entry); return B_SUCCESS; } b_status b_command_usage_add_command(b_command_usage *usage, unsigned int cmd_id) { - struct b_command_usage_command *u_cmd = malloc(sizeof *u_cmd); + struct b_command_usage_entry *u_cmd = malloc(sizeof *u_cmd); if (!u_cmd) { return B_ERR_NO_MEMORY; } memset(u_cmd, 0x0, sizeof *u_cmd); - u_cmd->cmd_id = cmd_id; + u_cmd->e_type = CMD_USAGE_COMMAND; + u_cmd->e_cmd_id = cmd_id; - b_queue_push_back(&usage->u_cmd, &u_cmd->cmd_entry); + b_queue_push_back(&usage->u_parts, &u_cmd->e_entry); return B_SUCCESS; } @@ -381,66 +372,52 @@ static void get_usage_string( struct b_string *cmd_name = b_string_create(); b_queue_iterator it; - b_queue_foreach (&it, &usage->u_cmd) { - struct b_command_usage_command *subcmd = b_unbox( - struct b_command_usage_command, it.entry, cmd_entry); + b_queue_foreach (&it, &usage->u_parts) { + struct b_command_usage_entry *entry = b_unbox( + struct b_command_usage_entry, it.entry, e_entry); - if (!subcmd) { + if (!entry) { break; } b_string_clear(cmd_name); b_string_append_cstr(out, " "); - if (subcmd->cmd_id == B_COMMAND_INVALID_ID) { - b_string_append_cstr(out, "[COMMAND]"); - } else { + switch (entry->e_type) { + case CMD_USAGE_ARG: + if (entry->e_arg) { + z__b_get_arg_usage_string(entry->e_arg, false, out); + } else { + b_string_append_cstr(out, "[[ARGS]"); + } + break; + case CMD_USAGE_OPT: + if (entry->e_opt) { + z__b_get_option_usage_string( + entry->e_opt, CMD_STR_DIRECT_USAGE, out); + } else { + b_string_append_cstr(out, "[[OPTIONS]"); + } + + break; + case CMD_USAGE_COMMAND: + if (entry->e_cmd_id == B_COMMAND_INVALID_ID) { + b_string_append_cstr(out, "[[COMMAND]"); + break; + } + struct b_command *subcmd_info - = get_command(&command_list, subcmd->cmd_id); + = get_command(&command_list, entry->e_cmd_id); prepend_command_name(subcmd_info, cmd_name); b_string_append_s(out, cmd_name); + break; + default: + break; } } b_string_release(cmd_name); - - b_queue_foreach (&it, &usage->u_opt) { - struct b_command_usage_opt *opt = b_unbox( - struct b_command_usage_opt, it.entry, opt_entry); - - if (!opt) { - break; - } - - b_string_append_cstr(out, " "); - - if (opt->opt - == (struct b_command_option *)(uintptr_t)B_COMMAND_INVALID_ID) { - b_string_append_cstr(out, "[OPTIONS]"); - } else { - z__b_get_option_usage_string( - opt->opt, CMD_STR_DIRECT_USAGE, out); - } - } - - b_queue_foreach (&it, &usage->u_arg) { - struct b_command_usage_arg *arg = b_unbox( - struct b_command_usage_arg, it.entry, arg_entry); - - if (!arg) { - break; - } - - b_string_append_cstr(out, " "); - - if (arg->arg - == (struct b_command_arg *)(uintptr_t)B_COMMAND_INVALID_ID) { - b_string_append_cstr(out, "[ARGS]"); - } else { - z__b_get_arg_usage_string(arg->arg, false, out); - } - } } b_string *z__b_command_default_usage_string( @@ -833,6 +810,44 @@ struct b_command_option *b_command_get_option_with_short_name( return NULL; } +struct b_command_option *b_command_get_option_with_id( + struct b_command *cmd, unsigned int id) +{ + b_queue_iterator it; + b_queue_foreach (&it, &cmd->b_opt) { + struct b_command_option *opt + = b_unbox(struct b_command_option, it.entry, opt_entry); + if (!opt) { + continue; + } + + if (opt->opt_id == id) { + return opt; + } + } + + return NULL; +} + +struct b_command_arg *b_command_get_arg_with_id( + struct b_command *cmd, unsigned int id) +{ + b_queue_iterator it; + b_queue_foreach (&it, &cmd->b_arg) { + struct b_command_arg *arg + = b_unbox(struct b_command_arg, it.entry, arg_entry); + if (!arg) { + continue; + } + + if (arg->arg_id == id) { + return arg; + } + } + + return NULL; +} + static void print_usage(struct b_command *cmd) { b_paragraph_format format = {0}; @@ -963,6 +978,8 @@ int b_command_dispatch(unsigned int cmd_id, int argc, const char **argv) } struct b_arglist *args = b_arglist_create(); + args->list_command = cmd; + b_status status = b_arglist_parse(args, &cmd, argc, argv); if (B_ERR(status)) { b_arglist_destroy(args); diff --git a/cmd/command.h b/cmd/command.h index 3b10bd9..694f56b 100644 --- a/cmd/command.h +++ b/cmd/command.h @@ -19,6 +19,13 @@ enum cmd_string_flags { CMD_STR_DIRECT_USAGE = 0x02u, }; +enum b_command_usage_entry_type { + CMD_USAGE_NONE = 0, + CMD_USAGE_ARG, + CMD_USAGE_OPT, + CMD_USAGE_COMMAND, +}; + struct b_string; struct b_command { @@ -41,28 +48,22 @@ struct b_command { struct b_btree_node b_node; }; -struct b_command_usage_opt { - struct b_queue_entry opt_entry; - struct b_command_option *opt; -}; +struct b_command_usage_entry { + struct b_queue_entry e_entry; + enum b_command_usage_entry_type e_type; -struct b_command_usage_arg { - struct b_queue_entry arg_entry; - struct b_command_arg *arg; -}; - -struct b_command_usage_command { - struct b_queue_entry cmd_entry; - unsigned int cmd_id; + union { + struct b_command_option *e_opt; + struct b_command_arg *e_arg; + unsigned int e_cmd_id; + }; }; struct b_command_usage { b_command_usage_flags u_flags; struct b_queue_entry u_entry; - struct b_queue u_cmd; - struct b_queue u_opt; - struct b_queue u_arg; + struct b_queue u_parts; }; struct b_command_option { @@ -97,6 +98,7 @@ struct b_arglist_option { }; struct b_arglist { + struct b_command *list_command; struct b_btree list_options; }; @@ -111,6 +113,13 @@ BLUE_API struct b_command_option *b_command_get_option_with_long_name( struct b_command *cmd, const char *long_name); BLUE_API struct b_command_option *b_command_get_option_with_short_name( struct b_command *cmd, char short_name); +BLUE_API struct b_command_option *b_command_get_option_with_id( + struct b_command *cmd, unsigned int id); +BLUE_API struct b_command_arg *b_command_get_arg_with_id( + struct b_command *cmd, unsigned int id); + +BLUE_API struct b_command_arg *b_command_option_get_arg_with_id( + struct b_command_option *opt, unsigned int id); BLUE_API struct b_command_option *b_command_option_create(void); BLUE_API void b_command_option_destroy(struct b_command_option *opt); diff --git a/cmd/include/blue/cmd.h b/cmd/include/blue/cmd.h index efd7f81..445fa18 100644 --- a/cmd/include/blue/cmd.h +++ b/cmd/include/blue/cmd.h @@ -19,6 +19,18 @@ = b_arglist_iterator_begin(q, opt_id, arg_id, (it)); \ b_arglist_iterator_is_valid(it); b_arglist_iterator_next(it)) +#define b_arglist_option_foreach(it, q) \ + for (int z__b_unique_name() \ + = b_arglist_option_iterator_begin(q, B_COMMAND_INVALID_ID, (it)); \ + b_arglist_option_iterator_is_valid(it); \ + b_arglist_option_iterator_next(it)) + +#define b_arglist_option_foreach_filtered(it, q, opt_id) \ + for (int z__b_unique_name() \ + = b_arglist_option_iterator_begin(q, opt_id, (it)); \ + b_arglist_option_iterator_is_valid(it); \ + b_arglist_option_iterator_next(it)) + #define B_COMMAND(id, parent_id) \ static b_command *command_##id = NULL; \ static void __init_##id( \ @@ -105,15 +117,13 @@ b_command_usage_add_option(this_usage, opt_##opt_id) #define B_COMMAND_USAGE_OPT_PLACEHOLDER() \ - b_command_usage_add_option( \ - this_usage, (struct b_command_option *)B_COMMAND_INVALID_ID) + b_command_usage_add_option(this_usage, NULL) #define B_COMMAND_USAGE_ARG(opt_id) \ b_command_usage_add_arg(this_usage, arg_##opt_id) #define B_COMMAND_USAGE_ARG_PLACEHOLDER() \ - b_command_usage_add_arg( \ - this_usage, (struct b_command_arg *)B_COMMAND_INVALID_ID) + b_command_usage_add_arg(this_usage, NULL) #define B_COMMAND_OPTION_HELP ((uintptr_t)0xF0000001) #define B_COMMAND_INVALID_ID ((uintptr_t)0xFFFFFFFF) @@ -164,6 +174,17 @@ typedef struct b_arglist_iterator { unsigned int _opt_filter, _arg_filter; } b_arglist_iterator; +typedef struct b_arglist_option_iterator { + b_iterator _base; + size_t i; + + unsigned int opt_id; + struct b_arglist_option *opt; + + b_btree_iterator _opt_it; + unsigned int _opt_filter; +} b_arglist_option_iterator; + typedef struct b_command b_command; typedef struct b_command_option b_command_option; typedef struct b_command_arg b_command_arg; @@ -228,6 +249,17 @@ BLUE_API b_status b_arglist_get_option( BLUE_API size_t b_arglist_get_count( const b_arglist *args, unsigned int opt_id, unsigned int arg_id); +BLUE_API b_status b_arglist_report_missing_option( + const b_arglist *args, unsigned int opt_id); +BLUE_API b_status b_arglist_report_unexpected_arg( + const b_arglist *args, const char *value); +BLUE_API b_status b_arglist_report_invalid_arg_value( + const b_arglist *args, unsigned int opt_id, unsigned int arg_id, + const char *value); +BLUE_API b_status b_arglist_report_missing_args( + const b_arglist *args, unsigned int opt_id, unsigned int arg_id, + size_t nr_supplied); + BLUE_API b_status b_arglist_option_get_value( const b_arglist_option *opt, unsigned int arg_id, unsigned int index, b_arglist_value **out); @@ -238,4 +270,11 @@ BLUE_API int b_arglist_iterator_begin( BLUE_API bool b_arglist_iterator_next(b_arglist_iterator *it); BLUE_API bool b_arglist_iterator_is_valid(const b_arglist_iterator *it); +BLUE_API int b_arglist_option_iterator_begin( + const b_arglist *args, unsigned int opt_filter, + b_arglist_option_iterator *it); +BLUE_API bool b_arglist_option_iterator_next(b_arglist_option_iterator *it); +BLUE_API bool b_arglist_option_iterator_is_valid( + const b_arglist_option_iterator *it); + #endif diff --git a/cmd/option.c b/cmd/option.c index 50901a8..7588d11 100644 --- a/cmd/option.c +++ b/cmd/option.c @@ -231,3 +231,22 @@ void z__b_get_option_usage_string( } } } + +struct b_command_arg *b_command_option_get_arg_with_id( + struct b_command_option *opt, unsigned int id) +{ + b_queue_iterator it; + b_queue_foreach (&it, &opt->opt_args) { + struct b_command_arg *arg + = b_unbox(struct b_command_arg, it.entry, arg_entry); + if (!arg) { + continue; + } + + if (arg->arg_id == id) { + return arg; + } + } + + return NULL; +} diff --git a/cmd/report.c b/cmd/report.c new file mode 100644 index 0000000..74ae677 --- /dev/null +++ b/cmd/report.c @@ -0,0 +1,193 @@ +#include "command.h" + +#include +#include +#include +#include + +enum b_status b_arglist_report_missing_option( + const b_arglist *args, unsigned int opt_id) +{ + struct b_command_option *opt = NULL; + + if (opt_id != B_COMMAND_INVALID_ID) { + opt = b_command_get_option_with_id(args->list_command, opt_id); + } + + if (!opt) { + return B_ERR_INVALID_ARGUMENT; + } + + struct b_string *opt_string = b_string_create(); + z__b_get_option_usage_string(opt, 0, opt_string); + + char opt_name_s[128]; + struct b_stringstream opt_name; + b_stringstream_begin(&opt_name, opt_name_s, sizeof opt_name_s); + + int opt_names = 0; + if (opt->opt_short_name) { + opt_names++; + } + + if (opt->opt_long_name) { + opt_names++; + } + + if (opt_names == 2) { + b_stringstream_addf( + &opt_name, "-%c / --%s", opt->opt_short_name, + opt->opt_long_name); + } else if (opt->opt_short_name) { + b_stringstream_addf(&opt_name, "-%c", opt->opt_short_name); + } else if (opt->opt_long_name) { + b_stringstream_addf(&opt_name, "--%s", opt->opt_long_name); + } + + b_err("required option `" F_YELLOW "%s" F_RESET "` was not specified.", + opt_name_s); + b_i("usage: %s", b_string_ptr(opt_string)); + b_i("for more information, use `" F_YELLOW "--help" F_RESET "`"); + + b_string_release(opt_string); + + return B_SUCCESS; +} + +enum b_status b_arglist_report_unexpected_arg( + const b_arglist *args, const char *value) +{ + struct b_string *usage + = z__b_command_default_usage_string(args->list_command, NULL); + + b_err("unexpected argument '" F_YELLOW "%s" F_RESET "' found.", value); + b_i("usage: %s", b_string_ptr(usage)); + b_i("for more information, use '" F_YELLOW "--help" F_RESET "'"); + + return B_SUCCESS; +} + +enum b_status b_arglist_report_invalid_arg_value( + const b_arglist *args, unsigned int opt_id, unsigned int arg_id, + const char *value) +{ + struct b_command_option *opt = NULL; + struct b_command_arg *arg = NULL; + + if (opt_id != B_COMMAND_INVALID_ID) { + opt = b_command_get_option_with_id(args->list_command, opt_id); + } + + if (arg_id != B_COMMAND_INVALID_ID) { + arg = opt ? b_command_option_get_arg_with_id(opt, arg_id) + : b_command_get_arg_with_id(args->list_command, arg_id); + } + + struct b_string *usage + = z__b_command_default_usage_string(args->list_command, opt); + struct b_string *opt_string = b_string_create(); + + if (opt) { + z__b_get_option_usage_string(opt, 0, opt_string); + } else { + z__b_get_arg_usage_string(arg, 0, opt_string); + } + + b_err("invalid value '" F_YELLOW "%s" F_RESET "' for '" F_YELLOW + "%s" F_RESET "'.", + value, b_string_ptr(opt_string)); + + if (opt) { + b_i("'" F_YELLOW "%s" F_RESET + "' accepts the following values for '" F_YELLOW "%s" F_RESET + "':", + b_string_ptr(opt_string), arg->arg_name); + } else { + b_i("'" F_YELLOW "%s" F_RESET "' accepts the following values:", + b_string_ptr(opt_string)); + } + + for (int i = 0; arg->arg_allowed_values[i]; i++) { + b_printf( + " * " F_GREEN "%s" F_RESET "\n", + arg->arg_allowed_values[i]); + } + + b_printf("\n"); + b_i("usage: %s", b_string_ptr(usage)); + b_i("for more information, use '" F_YELLOW "--help" F_RESET); + + b_string_release(usage); + b_string_release(opt_string); + + return B_SUCCESS; +} + +enum b_status b_arglist_report_missing_args( + const b_arglist *args, unsigned int opt_id, unsigned int arg_id, + size_t args_supplied) +{ + struct b_command_option *opt = NULL; + struct b_command_arg *arg = NULL; + + if (opt_id != B_COMMAND_INVALID_ID) { + opt = b_command_get_option_with_id(args->list_command, opt_id); + } + + if (arg_id != B_COMMAND_INVALID_ID) { + arg = opt ? b_command_option_get_arg_with_id(opt, arg_id) + : b_command_get_arg_with_id(args->list_command, arg_id); + } + + struct b_string *usage + = z__b_command_default_usage_string(args->list_command, opt); + struct b_string *opt_string = b_string_create(); + + if (opt) { + z__b_get_option_usage_string(opt, 0, opt_string); + } else { + z__b_get_arg_usage_string(arg, 0, opt_string); + } + + char supplied_arg_str[64]; + if (args_supplied == 0) { + snprintf( + supplied_arg_str, sizeof supplied_arg_str, + F_RED_BOLD "none" F_RESET " were provided"); + } else if (args_supplied == 1) { + snprintf( + supplied_arg_str, sizeof supplied_arg_str, + "only " F_YELLOW_BOLD "%zu" F_RESET " was provided", + args_supplied); + } else { + snprintf( + supplied_arg_str, sizeof supplied_arg_str, + "only " F_YELLOW_BOLD "%zu" F_RESET " were provided", + args_supplied); + } + + char required_arg_count[64]; + switch (arg->arg_nr_values) { + case B_ARG_1_OR_MORE_VALUES: + snprintf( + required_arg_count, sizeof required_arg_count, + "one or more"); + break; + default: + snprintf( + required_arg_count, sizeof required_arg_count, "%d", + arg->arg_nr_values); + } + + b_err("argument `" F_YELLOW "%s" F_RESET "` requires " F_GREEN_BOLD + "%s" F_RESET " `" F_YELLOW "%s" F_RESET "` value%s, but %s.", + b_string_ptr(opt_string), required_arg_count, arg->arg_name, + (arg->arg_nr_values == 1) ? "" : "s", supplied_arg_str); + b_i("usage: %s", b_string_ptr(usage)); + b_i("for more information, use '" F_YELLOW "--help" F_RESET "'"); + + b_string_release(usage); + b_string_release(opt_string); + + return B_SUCCESS; +}