X-Git-Url: https://git.efficios.com/?a=blobdiff_plain;f=src%2Fcli%2Fbabeltrace2-cfg-cli-args.c;h=6c187b4f12df7a7b0c2a7b2b88d231138d4c4f2b;hb=353c2524c2d4606a79ac5f4c0354a683a14e63e5;hp=e6fce2f7a2f92901632d5586db1839804a4b4f31;hpb=97512754fcb0e5d23147995b15a0755d797c6637;p=babeltrace.git diff --git a/src/cli/babeltrace2-cfg-cli-args.c b/src/cli/babeltrace2-cfg-cli-args.c index e6fce2f7..6c187b4f 100644 --- a/src/cli/babeltrace2-cfg-cli-args.c +++ b/src/cli/babeltrace2-cfg-cli-args.c @@ -1,25 +1,9 @@ /* - * Babeltrace trace converter - parameter parsing + * SPDX-License-Identifier: MIT * * Copyright 2016 Philippe Proulx * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Babeltrace trace converter - parameter parsing */ #define BT_LOG_TAG "CLI/CFG-CLI-ARGS" @@ -48,6 +32,7 @@ #include "common/version.h" #define BT_CLI_LOGE_APPEND_CAUSE_OOM() BT_CLI_LOGE_APPEND_CAUSE("Out of memory.") +#define WHILE_PARSING_ARG_N_FMT "While parsing argument #%d (`%s`): " /* * Returns the plugin name, component class name, component class type, @@ -191,16 +176,96 @@ end: return; } +static +void print_and_indent(const char *str) +{ + const char *ch = &str[0]; + + for (; *ch != '\0'; ch++) { + if (*ch == '\n') { + if (ch[1] != '\0') { + printf("\n "); + } + } else { + printf("%c", *ch); + } + } + + printf("\n"); +} + /* * Prints the Babeltrace version. */ static void print_version(void) { - if (GIT_VERSION[0] == '\0') { - puts("Babeltrace " VERSION); - } else { - puts("Babeltrace " VERSION " - " GIT_VERSION); + bool has_extra_name = strlen(BT_VERSION_EXTRA_NAME) > 0; + bool has_extra_description = strlen(BT_VERSION_EXTRA_DESCRIPTION) > 0; + bool has_extra_patch_names = strlen(BT_VERSION_EXTRA_PATCHES) > 0; + bool has_extra = has_extra_name || has_extra_description || + has_extra_patch_names; + + printf("%sBabeltrace %s%s", + bt_common_color_bold(), + VERSION, + bt_common_color_reset()); + + if (strlen(BT_VERSION_NAME) > 0) { + printf(" \"%s%s%s%s\"", + bt_common_color_fg_bright_blue(), + bt_common_color_bold(), + BT_VERSION_NAME, + bt_common_color_reset()); + } + + if (strlen(BT_VERSION_GIT) > 0) { + printf(" [%s%s%s]", + bt_common_color_fg_yellow(), + BT_VERSION_GIT, + bt_common_color_reset()); + } + + printf("\n"); + + if (strlen(BT_VERSION_DESCRIPTION) > 0) { + unsigned int columns; + GString *descr; + + if (bt_common_get_term_size(&columns, NULL) < 0) { + /* Width not found: default to 80 */ + columns = 80; + } + + descr = bt_common_fold(BT_VERSION_DESCRIPTION, columns, 0); + BT_ASSERT(descr); + printf("\n%s\n", descr->str); + g_string_free(descr, TRUE); + } + + if (has_extra) { + printf("\n"); + + if (has_extra_name) { + printf("%sExtra name%s: %s\n", + bt_common_color_fg_cyan(), + bt_common_color_reset(), + BT_VERSION_EXTRA_NAME); + } + + if (has_extra_description) { + printf("%sExtra description%s:\n ", + bt_common_color_fg_cyan(), + bt_common_color_reset()); + print_and_indent(BT_VERSION_EXTRA_DESCRIPTION); + } + + if (has_extra_patch_names) { + printf("%sExtra patch names%s:\n ", + bt_common_color_fg_cyan(), + bt_common_color_reset()); + print_and_indent(BT_VERSION_EXTRA_PATCHES); + } } } @@ -1093,10 +1158,11 @@ end: } static -struct bt_config *bt_config_print_ctf_metadata_create( - const bt_value *plugin_paths) +enum bt_config_cli_args_status bt_config_print_ctf_metadata_create( + const bt_value *plugin_paths, struct bt_config **cfg_out) { struct bt_config *cfg; + enum bt_config_cli_args_status status; /* Create config */ cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA, @@ -1117,20 +1183,24 @@ struct bt_config *bt_config_print_ctf_metadata_create( goto error; } + BT_OBJECT_MOVE_REF(*cfg_out, cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_OK; goto end; error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: - return cfg; + bt_object_put_ref(cfg); + return status; } static -struct bt_config *bt_config_print_lttng_live_sessions_create( - const bt_value *plugin_paths) +enum bt_config_cli_args_status bt_config_print_lttng_live_sessions_create( + const bt_value *plugin_paths, struct bt_config **cfg_out) { struct bt_config *cfg; + enum bt_config_cli_args_status status; /* Create config */ cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, @@ -1152,13 +1222,16 @@ struct bt_config *bt_config_print_lttng_live_sessions_create( goto error; } + BT_OBJECT_MOVE_REF(*cfg_out, cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_OK; goto end; error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: - return cfg; + bt_object_put_ref(cfg); + return status; } static @@ -1225,32 +1298,155 @@ void print_expected_params_format(FILE *fp) fprintf(fp, "babeltrace2 from a shell.\n"); } +/* + * Given argpar error status `status` and error `error`, return a formatted + * error message describing the error. + * + * `argv` is the argument vector that was being parsed. + * + * `prefix_fmt` (formatted using the following arguments) is prepended to + * the error message. + * + * The returned string must be freed by the caller. + */ static -bool help_option_is_specified( - const struct argpar_parse_ret *argpar_parse_ret) +GString *__BT_ATTR_FORMAT_PRINTF(4, 5) format_arg_error( + const struct argpar_error *error, + const char **argv, unsigned int arg_index_offset, + const char *prefix_fmt, ...) { - int i; - bool specified = false; + GString *str = g_string_new(NULL); + va_list args; - for (i = 0; i < argpar_parse_ret->items->n_items; i++) { - struct argpar_item *argpar_item = - argpar_parse_ret->items->items[i]; - struct argpar_item_opt *argpar_item_opt; + va_start(args, prefix_fmt); + g_string_append_vprintf(str, prefix_fmt, args); + va_end(args); - if (argpar_item->type != ARGPAR_ITEM_TYPE_OPT) { - continue; + g_string_append(str, ": "); + + switch (argpar_error_type(error)) + { + case ARGPAR_ERROR_TYPE_MISSING_OPT_ARG: + { + bool is_short; + const struct argpar_opt_descr *descr = + argpar_error_opt_descr(error, &is_short); + int orig_index = argpar_error_orig_index(error); + const char *arg = argv[orig_index]; + + if (is_short) { + g_string_append_printf( + str, + WHILE_PARSING_ARG_N_FMT "Missing required argument for option `-%c`", + arg_index_offset + orig_index + 1, arg, descr->short_name); + } else { + g_string_append_printf( + str, + WHILE_PARSING_ARG_N_FMT "Missing required argument for option `--%s`", + arg_index_offset + orig_index + 1, arg, descr->long_name); } - argpar_item_opt = (struct argpar_item_opt *) argpar_item; - if (argpar_item_opt->descr->id == OPT_HELP) { - specified = true; - break; + break; + } + case ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG: + { + bool is_short; + const struct argpar_opt_descr *descr = + argpar_error_opt_descr(error, &is_short); + int orig_index = argpar_error_orig_index(error); + const char *arg = argv[orig_index]; + + if (is_short) { + g_string_append_printf( + str, + WHILE_PARSING_ARG_N_FMT "Unexpected argument for option `-%c`", + arg_index_offset + orig_index + 1, arg, descr->short_name); + } else { + g_string_append_printf( + str, + WHILE_PARSING_ARG_N_FMT "Unexpected argument for option `--%s`", + arg_index_offset + orig_index + 1, arg, descr->long_name); } + + break; + } + case ARGPAR_ERROR_TYPE_UNKNOWN_OPT: + { + int orig_index = argpar_error_orig_index(error); + const char *unknown_opt = argpar_error_unknown_opt_name(error); + const char *arg = argv[orig_index]; + + g_string_append_printf( + str, + WHILE_PARSING_ARG_N_FMT "Unknown option `%s`", + arg_index_offset + orig_index + 1, arg, unknown_opt); + + break; + } + + default: + BT_ASSERT(0); + } + + return str; +} + +enum parse_next_item_status +{ + PARSE_NEXT_ITEM_STATUS_OK = 0, + PARSE_NEXT_ITEM_STATUS_END = 1, + PARSE_NEXT_ITEM_STATUS_ERROR = -1, +}; + +/* + * Parse the next item using `iter`. Log and append an error if necessary. + * + * The item in `*item` on entry is freed, and the new item is also + * returned in `*item`. + */ +static +enum parse_next_item_status parse_next_item(struct argpar_iter *iter, + const struct argpar_item **item, const char **argv, + const char *command, unsigned int consumed_args) +{ + enum argpar_iter_next_status status; + const struct argpar_error *error = NULL; + enum parse_next_item_status ret; + + ARGPAR_ITEM_DESTROY_AND_RESET(*item); + status = argpar_iter_next(iter, item, &error); + + switch (status) { + case ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY: + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + ret = PARSE_NEXT_ITEM_STATUS_ERROR; + break; + case ARGPAR_ITER_NEXT_STATUS_ERROR: + { + GString *err_str = format_arg_error(error, argv, + consumed_args, + "While parsing `%s` command's command-line arguments", + command); + BT_CLI_LOGE_APPEND_CAUSE("%s", err_str->str); + g_string_free(err_str, TRUE); + ret = PARSE_NEXT_ITEM_STATUS_ERROR; + break; + } + case ARGPAR_ITER_NEXT_STATUS_END: + ret = PARSE_NEXT_ITEM_STATUS_END; + break; + case ARGPAR_ITER_NEXT_STATUS_OK: + ret = PARSE_NEXT_ITEM_STATUS_OK; + break; + default: + bt_common_abort(); } - return specified; + argpar_error_destroy(error); + return ret; } + /* * Prints the help command usage. */ @@ -1279,84 +1475,98 @@ const struct argpar_opt_descr help_options[] = { /* * Creates a Babeltrace config object from the arguments of a help * command. - * - * *retcode is set to the appropriate exit code to use. */ static -struct bt_config *bt_config_help_from_args(int argc, const char *argv[], - int *retcode, const bt_value *plugin_paths, - int default_log_level) +enum bt_config_cli_args_status bt_config_help_from_args(int argc, + const char *argv[], struct bt_config **cfg_out, + const bt_value *plugin_paths, int default_log_level, + unsigned int consumed_args) { - struct bt_config *cfg = NULL; + enum bt_config_cli_args_status status; + struct bt_config *cfg; + const char *plugin_comp_cls_arg = NULL; char *plugin_name = NULL, *comp_cls_name = NULL; - struct argpar_parse_ret argpar_parse_ret = { 0 }; - struct argpar_item_non_opt *non_opt; GString *substring = NULL; size_t end_pos; + struct argpar_iter *argpar_iter = NULL; + const struct argpar_item *argpar_item = NULL; - *retcode = 0; cfg = bt_config_help_create(plugin_paths, default_log_level); if (!cfg) { goto error; } - /* Parse options */ - argpar_parse_ret = argpar_parse(argc, argv, help_options, true); - if (argpar_parse_ret.error) { - BT_CLI_LOGE_APPEND_CAUSE( - "While parsing `help` command's command-line arguments: %s", - argpar_parse_ret.error); + argpar_iter = argpar_iter_create(argc, argv, help_options); + if (!argpar_iter) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (help_option_is_specified(&argpar_parse_ret)) { - print_help_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; + while (true) { + enum parse_next_item_status parse_status = + parse_next_item(argpar_iter, &argpar_item, argv, "help", + consumed_args); + + if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) { + goto error; + } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) { + break; + } + + if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_opt_descr *opt_descr = + argpar_item_opt_descr(argpar_item); + + switch (opt_descr->id) { + case OPT_HELP: + print_help_usage(stdout); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; + goto end; + default: + bt_common_abort(); + } + } else { + const char *arg = argpar_item_non_opt_arg(argpar_item); + + if (plugin_comp_cls_arg) { + BT_CLI_LOGE_APPEND_CAUSE( + "Extraneous command-line argument specified to `help` command: `%s`.", + arg); + goto error; + } + + plugin_comp_cls_arg = arg; + } } - if (argpar_parse_ret.items->n_items == 0) { + if (!plugin_comp_cls_arg) { BT_CLI_LOGE_APPEND_CAUSE( "Missing plugin name or component class descriptor."); goto error; - } else if (argpar_parse_ret.items->n_items > 1) { - /* - * At this point we know there are least two non-option - * arguments because we don't reach here with `--help`, - * the only option. - */ - non_opt = (struct argpar_item_non_opt *) argpar_parse_ret.items->items[1]; - BT_CLI_LOGE_APPEND_CAUSE( - "Extraneous command-line argument specified to `help` command: `%s`.", - non_opt->arg); - goto error; } - non_opt = (struct argpar_item_non_opt *) argpar_parse_ret.items->items[0]; - /* Look for unescaped dots in the argument. */ - substring = bt_common_string_until(non_opt->arg, ".\\", ".", &end_pos); + substring = bt_common_string_until(plugin_comp_cls_arg, ".\\", ".", &end_pos); if (!substring) { BT_CLI_LOGE_APPEND_CAUSE("Could not consume argument: arg=%s", - non_opt->arg); + plugin_comp_cls_arg); goto error; } - if (end_pos == strlen(non_opt->arg)) { + if (end_pos == strlen(plugin_comp_cls_arg)) { /* Didn't find an unescaped dot, treat it as a plugin name. */ g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name, - non_opt->arg); + plugin_comp_cls_arg); } else { /* * Found an unescaped dot, treat it as a component class name. */ - plugin_comp_cls_names(non_opt->arg, NULL, &plugin_name, &comp_cls_name, + plugin_comp_cls_names(plugin_comp_cls_arg, NULL, &plugin_name, &comp_cls_name, &cfg->cmd_data.help.cfg_component->type); if (!plugin_name || !comp_cls_name) { BT_CLI_LOGE_APPEND_CAUSE( "Could not parse argument as a component class name: arg=%s", - non_opt->arg); + plugin_comp_cls_arg); goto error; } @@ -1366,11 +1576,12 @@ struct bt_config *bt_config_help_from_args(int argc, const char *argv[], comp_cls_name); } + BT_OBJECT_MOVE_REF(*cfg_out, cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_OK; goto end; error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: g_free(plugin_name); @@ -1380,9 +1591,11 @@ end: g_string_free(substring, TRUE); } - argpar_parse_ret_fini(&argpar_parse_ret); + argpar_iter_destroy(argpar_iter); + argpar_item_destroy(argpar_item); + bt_object_put_ref(cfg); - return cfg; + return status; } /* @@ -1413,20 +1626,20 @@ const struct argpar_opt_descr query_options[] = { /* * Creates a Babeltrace config object from the arguments of a query * command. - * - * *retcode is set to the appropriate exit code to use. */ static -struct bt_config *bt_config_query_from_args(int argc, const char *argv[], - int *retcode, const bt_value *plugin_paths, - int default_log_level) +enum bt_config_cli_args_status bt_config_query_from_args(int argc, + const char *argv[], struct bt_config **cfg_out, + const bt_value *plugin_paths, int default_log_level, + unsigned int consumed_args) { - int i; + enum bt_config_cli_args_status status; struct bt_config *cfg = NULL; const char *component_class_spec = NULL; const char *query_object = NULL; GString *error_str = NULL; - struct argpar_parse_ret argpar_parse_ret = { 0 }; + struct argpar_iter *argpar_iter = NULL; + const struct argpar_item *argpar_item = NULL; bt_value *params = bt_value_map_create(); if (!params) { @@ -1434,7 +1647,6 @@ struct bt_config *bt_config_query_from_args(int argc, const char *argv[], goto error; } - *retcode = 0; cfg = bt_config_query_create(plugin_paths); if (!cfg) { goto error; @@ -1446,32 +1658,33 @@ struct bt_config *bt_config_query_from_args(int argc, const char *argv[], goto error; } - /* Parse options */ - argpar_parse_ret = argpar_parse(argc, argv, query_options, true); - if (argpar_parse_ret.error) { - BT_CLI_LOGE_APPEND_CAUSE( - "While parsing `query` command's command-line arguments: %s", - argpar_parse_ret.error); + argpar_iter = argpar_iter_create(argc, argv, query_options); + if (!argpar_iter) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (help_option_is_specified(&argpar_parse_ret)) { - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } + while (true) { + enum parse_next_item_status parse_status = + parse_next_item(argpar_iter, &argpar_item, argv, "query", + consumed_args); - for (i = 0; i < argpar_parse_ret.items->n_items; i++) { - struct argpar_item *argpar_item = - argpar_parse_ret.items->items[i]; + if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) { + goto error; + } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) { + break; + } - if (argpar_item->type == ARGPAR_ITEM_TYPE_OPT) { - struct argpar_item_opt *argpar_item_opt = - (struct argpar_item_opt *) argpar_item; - const char *arg = argpar_item_opt->arg; + if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_opt_descr *opt_descr = + argpar_item_opt_descr(argpar_item); + const char *arg = argpar_item_opt_arg(argpar_item); - switch (argpar_item_opt->descr->id) { + switch (opt_descr->id) { + case OPT_HELP: + print_query_usage(stdout); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; + goto end; case OPT_PARAMS: { bt_value *parsed_params = bt_param_parse(arg, error_str); @@ -1492,13 +1705,10 @@ struct bt_config *bt_config_query_from_args(int argc, const char *argv[], break; } default: - BT_CLI_LOGE_APPEND_CAUSE("Unknown command-line option specified (option code %d).", - argpar_item_opt->descr->id); - goto error; + bt_common_abort(); } } else { - struct argpar_item_non_opt *argpar_item_non_opt - = (struct argpar_item_non_opt *) argpar_item; + const char *arg = argpar_item_non_opt_arg(argpar_item); /* * We need exactly two non-option arguments @@ -1506,12 +1716,12 @@ struct bt_config *bt_config_query_from_args(int argc, const char *argv[], * specification and query object. */ if (!component_class_spec) { - component_class_spec = argpar_item_non_opt->arg; + component_class_spec = arg; } else if (!query_object) { - query_object = argpar_item_non_opt->arg; + query_object = arg; } else { BT_CLI_LOGE_APPEND_CAUSE("Extraneous command-line argument specified to `query` command: `%s`.", - argpar_item_non_opt->arg); + arg); goto error; } } @@ -1519,8 +1729,7 @@ struct bt_config *bt_config_query_from_args(int argc, const char *argv[], if (!component_class_spec || !query_object) { print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; goto end; } @@ -1542,21 +1751,25 @@ struct bt_config *bt_config_query_from_args(int argc, const char *argv[], } g_string_assign(cfg->cmd_data.query.object, query_object); + BT_OBJECT_MOVE_REF(*cfg_out, cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_OK; goto end; error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: - argpar_parse_ret_fini(&argpar_parse_ret); + argpar_iter_destroy(argpar_iter); + argpar_item_destroy(argpar_item); if (error_str) { g_string_free(error_str, TRUE); } bt_value_put_ref(params); - return cfg; + bt_object_put_ref(cfg); + + return status; } /* @@ -1586,63 +1799,72 @@ const struct argpar_opt_descr list_plugins_options[] = { /* * Creates a Babeltrace config object from the arguments of a * list-plugins command. - * - * *retcode is set to the appropriate exit code to use. */ static -struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[], - int *retcode, const bt_value *plugin_paths) +enum bt_config_cli_args_status bt_config_list_plugins_from_args(int argc, + const char *argv[], struct bt_config **cfg_out, + const bt_value *plugin_paths, unsigned int consumed_args) { + enum bt_config_cli_args_status status; struct bt_config *cfg = NULL; - struct argpar_parse_ret argpar_parse_ret = { 0 }; + struct argpar_iter *argpar_iter = NULL; + const struct argpar_item *argpar_item = NULL; - *retcode = 0; cfg = bt_config_list_plugins_create(plugin_paths); if (!cfg) { goto error; } - /* Parse options */ - argpar_parse_ret = argpar_parse(argc, argv, list_plugins_options, true); - if (argpar_parse_ret.error) { - BT_CLI_LOGE_APPEND_CAUSE( - "While parsing `list-plugins` command's command-line arguments: %s", - argpar_parse_ret.error); + argpar_iter = argpar_iter_create(argc, argv, list_plugins_options); + if (!argpar_iter) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (help_option_is_specified(&argpar_parse_ret)) { - print_list_plugins_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } + while (true) { + enum parse_next_item_status parse_status = + parse_next_item(argpar_iter, &argpar_item, argv, "list-plugins", + consumed_args); - if (argpar_parse_ret.items->n_items > 0) { - /* - * At this point we know there's at least one non-option - * argument because we don't reach here with `--help`, - * the only option. - */ - struct argpar_item_non_opt *non_opt = - (struct argpar_item_non_opt *) argpar_parse_ret.items->items[0]; + if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) { + goto error; + } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) { + break; + } - BT_CLI_LOGE_APPEND_CAUSE( - "Extraneous command-line argument specified to `list-plugins` command: `%s`.", - non_opt->arg); - goto error; - } + if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_opt_descr *opt_descr = + argpar_item_opt_descr(argpar_item); + switch (opt_descr->id) { + case OPT_HELP: + print_list_plugins_usage(stdout); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; + goto end; + default: + bt_common_abort(); + } + } else { + BT_CLI_LOGE_APPEND_CAUSE( + "Extraneous command-line argument specified to `list-plugins` command: `%s`.", + argpar_item_non_opt_arg(argpar_item)); + goto error; + } + } + + BT_OBJECT_MOVE_REF(*cfg_out, cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_OK; goto end; error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: - argpar_parse_ret_fini(&argpar_parse_ret); + argpar_iter_destroy(argpar_iter); + argpar_item_destroy(argpar_item); + bt_object_put_ref(cfg); - return cfg; + return status; } /* @@ -1720,14 +1942,13 @@ void print_run_usage(FILE *fp) /* * Creates a Babeltrace config object from the arguments of a run * command. - * - * *retcode is set to the appropriate exit code to use. */ static -struct bt_config *bt_config_run_from_args(int argc, const char *argv[], - int *retcode, const bt_value *plugin_paths, - int default_log_level) +enum bt_config_cli_args_status bt_config_run_from_args(int argc, const char *argv[], + struct bt_config **cfg_out, const bt_value *plugin_paths, + int default_log_level, unsigned int consumed_args) { + enum bt_config_cli_args_status status; struct bt_config_component *cur_cfg_comp = NULL; bt_value *cur_base_params = NULL; int ret = 0; @@ -1738,8 +1959,8 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], long retry_duration = -1; bt_value_map_extend_status extend_status; GString *error_str = NULL; - struct argpar_parse_ret argpar_parse_ret = { 0 }; - int i; + struct argpar_iter *argpar_iter = NULL; + const struct argpar_item *argpar_item = NULL; static const struct argpar_opt_descr run_options[] = { { OPT_BASE_PARAMS, 'b', "base-params", true }, @@ -1753,8 +1974,6 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], ARGPAR_OPT_DESCR_SENTINEL }; - *retcode = 0; - error_str = g_string_new(NULL); if (!error_str) { BT_CLI_LOGE_APPEND_CAUSE_OOM(); @@ -1763,7 +1982,7 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], if (argc < 1) { print_run_usage(stdout); - *retcode = -1; + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; goto end; } @@ -1791,42 +2010,40 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], goto error; } - /* Parse options */ - argpar_parse_ret = argpar_parse(argc, argv, run_options, true); - if (argpar_parse_ret.error) { - BT_CLI_LOGE_APPEND_CAUSE( - "While parsing `run` command's command-line arguments: %s", - argpar_parse_ret.error); + argpar_iter = argpar_iter_create(argc, argv, run_options); + if (!argpar_iter) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (help_option_is_specified(&argpar_parse_ret)) { - print_run_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - for (i = 0; i < argpar_parse_ret.items->n_items; i++) { - struct argpar_item *argpar_item = - argpar_parse_ret.items->items[i]; - struct argpar_item_opt *argpar_item_opt; + while (true) { + enum parse_next_item_status parse_status; + const struct argpar_opt_descr *opt_descr; const char *arg; - /* This command does not accept non-option arguments.*/ - if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) { - struct argpar_item_non_opt *argpar_nonopt_item = - (struct argpar_item_non_opt *) argpar_item; + parse_status = parse_next_item(argpar_iter, &argpar_item, argv, "run", + consumed_args); + if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) { + goto error; + } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) { + break; + } + /* This command does not accept non-option arguments.*/ + if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_NON_OPT) { BT_CLI_LOGE_APPEND_CAUSE("Unexpected argument: `%s`", - argpar_nonopt_item->arg); + argpar_item_non_opt_arg(argpar_item)); goto error; } - argpar_item_opt = (struct argpar_item_opt *) argpar_item; - arg = argpar_item_opt->arg; + opt_descr = argpar_item_opt_descr(argpar_item); + arg = argpar_item_opt_arg(argpar_item); - switch (argpar_item_opt->descr->id) { + switch (opt_descr->id) { + case OPT_HELP: + print_run_usage(stdout); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; + goto end; case OPT_COMPONENT: { enum bt_config_component_dest dest; @@ -1944,14 +2161,14 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], break; case OPT_RETRY_DURATION: { gchar *end; - size_t arg_len = strlen(argpar_item_opt->arg); + size_t arg_len = strlen(arg); - retry_duration = g_ascii_strtoll(argpar_item_opt->arg, &end, 10); + retry_duration = g_ascii_strtoll(arg, &end, 10); - if (arg_len == 0 || end != (argpar_item_opt->arg + arg_len)) { + if (arg_len == 0 || end != (arg + arg_len)) { BT_CLI_LOGE_APPEND_CAUSE( "Could not parse --retry-duration option's argument as an unsigned integer: `%s`", - argpar_item_opt->arg); + arg); goto error; } @@ -1966,9 +2183,7 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], break; } default: - BT_CLI_LOGE_APPEND_CAUSE("Unknown command-line option specified (option code %d).", - argpar_item_opt->descr->id); - goto error; + bt_common_abort(); } } @@ -1992,31 +2207,37 @@ struct bt_config *bt_config_run_from_args(int argc, const char *argv[], goto error; } + BT_OBJECT_MOVE_REF(*cfg_out, cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_OK; goto end; error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: if (error_str) { g_string_free(error_str, TRUE); } - argpar_parse_ret_fini(&argpar_parse_ret); + argpar_iter_destroy(argpar_iter); + argpar_item_destroy(argpar_item); + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); BT_VALUE_PUT_REF_AND_RESET(cur_base_params); BT_VALUE_PUT_REF_AND_RESET(instance_names); BT_VALUE_PUT_REF_AND_RESET(connection_args); - return cfg; + bt_object_put_ref(cfg); + + return status; } static -struct bt_config *bt_config_run_from_args_array(const bt_value *run_args, - int *retcode, const bt_value *plugin_paths, +enum bt_config_cli_args_status bt_config_run_from_args_array( + const bt_value *run_args, struct bt_config **cfg, + const bt_value *plugin_paths, int default_log_level) { - struct bt_config *cfg = NULL; + enum bt_config_cli_args_status status; const char **argv; uint64_t i, len = bt_value_array_get_length(run_args); @@ -2024,7 +2245,7 @@ struct bt_config *bt_config_run_from_args_array(const bt_value *run_args, argv = calloc((size_t) len, sizeof(*argv)); if (!argv) { BT_CLI_LOGE_APPEND_CAUSE_OOM(); - goto end; + goto error; } for (i = 0; i < len; i++) { @@ -2038,12 +2259,17 @@ struct bt_config *bt_config_run_from_args_array(const bt_value *run_args, argv[i] = arg; } - cfg = bt_config_run_from_args((int) len, argv, retcode, - plugin_paths, default_log_level); + status = bt_config_run_from_args((int) len, argv, cfg, + plugin_paths, default_log_level, 0); + + goto end; + +error: + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: free(argv); - return cfg; + return status; } /* @@ -3097,17 +3323,18 @@ enum convert_current_item_type { /* * Creates a Babeltrace config object from the arguments of a convert * command. - * - * *retcode is set to the appropriate exit code to use. */ static -struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], - int *retcode, const bt_value *plugin_paths, - int *default_log_level, const bt_interrupter *interrupter) +enum bt_config_cli_args_status bt_config_convert_from_args(int argc, + const char *argv[], struct bt_config **cfg_out, + const bt_value *plugin_paths, + int *default_log_level, const bt_interrupter *interrupter, + unsigned int consumed_args) { + enum bt_config_cli_args_status status; enum convert_current_item_type current_item_type = CONVERT_CURRENT_ITEM_TYPE_NONE; - int ret = 0; + int ret; struct bt_config *cfg = NULL; bool got_input_format_opt = false; bool got_output_format_opt = false; @@ -3138,7 +3365,8 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], char *output = NULL; struct auto_source_discovery auto_disc = { NULL }; GString *auto_disc_comp_name = NULL; - struct argpar_parse_ret argpar_parse_ret = { 0 }; + struct argpar_iter *argpar_iter = NULL; + const struct argpar_item *argpar_item = NULL; GString *name_gstr = NULL; GString *component_arg_for_run = NULL; bt_value *live_inputs_array_val = NULL; @@ -3159,11 +3387,10 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], bool ctf_fs_source_force_clock_class_unix_epoch_origin = false; gchar *ctf_fs_source_clock_class_offset_arg = NULL; gchar *ctf_fs_source_clock_class_offset_ns_arg = NULL; - *retcode = 0; if (argc < 1) { print_convert_usage(stdout); - *retcode = -1; + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; goto end; } @@ -3261,35 +3488,36 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], * arguments if needed to automatically name unnamed component * instances. */ - argpar_parse_ret = argpar_parse(argc, argv, convert_options, true); - if (argpar_parse_ret.error) { - BT_CLI_LOGE_APPEND_CAUSE( - "While parsing `convert` command's command-line arguments: %s", - argpar_parse_ret.error); + argpar_iter = argpar_iter_create(argc, argv, convert_options); + if (!argpar_iter) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - if (help_option_is_specified(&argpar_parse_ret)) { - print_convert_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - for (i = 0; i < argpar_parse_ret.items->n_items; i++) { - struct argpar_item *argpar_item = - argpar_parse_ret.items->items[i]; - struct argpar_item_opt *argpar_item_opt; + while (true) { + enum parse_next_item_status parse_status; char *name = NULL; char *plugin_name = NULL; char *comp_cls_name = NULL; - const char *arg; - if (argpar_item->type == ARGPAR_ITEM_TYPE_OPT) { - argpar_item_opt = (struct argpar_item_opt *) argpar_item; - arg = argpar_item_opt->arg; + parse_status = parse_next_item(argpar_iter, &argpar_item, argv, "convert", + consumed_args); + if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) { + goto error; + } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) { + break; + } + + if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_opt_descr *opt_descr = + argpar_item_opt_descr(argpar_item); + const char *arg = argpar_item_opt_arg(argpar_item); - switch (argpar_item_opt->descr->id) { + switch (opt_descr->id) { + case OPT_HELP: + print_convert_usage(stdout); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; + goto end; case OPT_COMPONENT: { bt_component_class_type type; @@ -3440,6 +3668,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], } } else if (current_item_type == CONVERT_CURRENT_ITEM_TYPE_NON_OPT) { uint64_t idx = bt_value_array_get_length(non_opt_loglevels) - 1; + enum bt_value_array_set_element_by_index_status set_element_status; bt_value *log_level_str_value; log_level_str_value = bt_value_string_create_init(arg); @@ -3448,9 +3677,11 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], goto error; } - if (bt_value_array_set_element_by_index(non_opt_loglevels, idx, - log_level_str_value)) { - bt_value_put_ref(log_level_str_value); + set_element_status = + bt_value_array_set_element_by_index(non_opt_loglevels, + idx, log_level_str_value); + bt_value_put_ref(log_level_str_value); + if (set_element_status != BT_VALUE_ARRAY_SET_ELEMENT_BY_INDEX_STATUS_OK) { BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } @@ -3503,20 +3734,15 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], /* Ignore in this pass */ break; default: - BT_CLI_LOGE_APPEND_CAUSE("Unknown command-line option specified (option code %d).", - argpar_item_opt->descr->id); - goto error; + bt_common_abort(); } - } else if (argpar_item->type == ARGPAR_ITEM_TYPE_NON_OPT) { - struct argpar_item_non_opt *argpar_item_non_opt; + } else { + const char *arg = argpar_item_non_opt_arg(argpar_item); bt_value_array_append_element_status append_status; current_item_type = CONVERT_CURRENT_ITEM_TYPE_NON_OPT; - argpar_item_non_opt = (struct argpar_item_non_opt *) argpar_item; - - append_status = bt_value_array_append_string_element(non_opts, - argpar_item_non_opt->arg); + append_status = bt_value_array_append_string_element(non_opts, arg); if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) { BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; @@ -3534,8 +3760,6 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - } else { - bt_common_abort(); } } @@ -3544,23 +3768,37 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], * arguments into implicit component instances for the run * command. */ - for (i = 0; i < argpar_parse_ret.items->n_items; i++) { - struct argpar_item *argpar_item = - argpar_parse_ret.items->items[i]; - struct argpar_item_opt *argpar_item_opt; + argpar_iter_destroy(argpar_iter); + argpar_iter = argpar_iter_create(argc, argv, convert_options); + if (!argpar_iter) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + } + + while (true) { + enum parse_next_item_status parse_status; + const struct argpar_opt_descr *opt_descr; const char *arg; - if (argpar_item->type != ARGPAR_ITEM_TYPE_OPT) { + parse_status = parse_next_item(argpar_iter, &argpar_item, argv, "convert", + consumed_args); + if (parse_status == PARSE_NEXT_ITEM_STATUS_ERROR) { + goto error; + } else if (parse_status == PARSE_NEXT_ITEM_STATUS_END) { + break; + } + + if (argpar_item_type(argpar_item) != ARGPAR_ITEM_TYPE_OPT) { continue; } - argpar_item_opt = (struct argpar_item_opt *) argpar_item; - arg = argpar_item_opt->arg; + opt_descr = argpar_item_opt_descr(argpar_item); + arg = argpar_item_opt_arg(argpar_item); - switch (argpar_item_opt->descr->id) { + switch (opt_descr->id) { case OPT_BEGIN: if (trimmer_has_begin) { - printf("At --begin option: --begin or --timerange option already specified\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("At --begin option: --begin or --timerange option already specified\n %s\n", arg); goto error; } @@ -3575,7 +3813,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], break; case OPT_END: if (trimmer_has_end) { - printf("At --end option: --end or --timerange option already specified\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("At --end option: --end or --timerange option already specified\n %s\n", arg); goto error; } @@ -3594,7 +3832,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], char *end; if (trimmer_has_begin || trimmer_has_end) { - printf("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n", + BT_CLI_LOGE_APPEND_CAUSE("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n", arg); goto error; } @@ -3827,8 +4065,18 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], *default_log_level = logging_level_min(*default_log_level, BT_LOG_TRACE); break; - default: + case OPT_COMPONENT: + case OPT_HELP: + case OPT_LOG_LEVEL: + case OPT_OMIT_HOME_PLUGIN_PATH: + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + case OPT_PARAMS: + case OPT_PLUGIN_PATH: + case OPT_RETRY_DURATION: + /* Ignore in this pass */ break; + default: + bt_common_abort(); } } @@ -3859,9 +4107,9 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], goto error; } - cfg = bt_config_print_ctf_metadata_create(plugin_paths); - if (!cfg) { - goto error; + status = bt_config_print_ctf_metadata_create(plugin_paths, &cfg); + if (status != BT_CONFIG_CLI_ARGS_STATUS_OK) { + goto end; } bt_val_non_opt = bt_value_array_borrow_element_by_index_const(non_opts, 0); @@ -3874,6 +4122,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], output); } + BT_OBJECT_MOVE_REF(*cfg_out, cfg); goto end; } @@ -3953,10 +4202,10 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], if (!lttng_live_url_parts.session_name) { /* Print LTTng live sessions */ - cfg = bt_config_print_lttng_live_sessions_create( - plugin_paths); - if (!cfg) { - goto error; + status = bt_config_print_lttng_live_sessions_create( + plugin_paths, &cfg); + if (status != BT_CONFIG_CLI_ARGS_STATUS_OK) { + goto end; } g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url, @@ -3968,6 +4217,7 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], output); } + BT_OBJECT_MOVE_REF(*cfg_out, cfg); goto end; } @@ -3998,13 +4248,13 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], goto error; } } else { - int status; size_t plugin_count; const bt_plugin **plugins; const bt_plugin *plugin; + auto_source_discovery_status auto_disc_status; - status = require_loaded_plugins(plugin_paths); - if (status != 0) { + ret = require_loaded_plugins(plugin_paths); + if (ret != 0) { goto error; } @@ -4017,22 +4267,23 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], plugins = borrow_loaded_plugins(); } - status = auto_discover_source_components(non_opts, plugins, plugin_count, + auto_disc_status = auto_discover_source_components( + non_opts, plugins, plugin_count, auto_source_discovery_restrict_component_class_name, *default_log_level, &auto_disc, interrupter); - if (status != 0) { - if (status == AUTO_SOURCE_DISCOVERY_STATUS_INTERRUPTED) { + if (auto_disc_status != AUTO_SOURCE_DISCOVERY_STATUS_OK) { + if (auto_disc_status == AUTO_SOURCE_DISCOVERY_STATUS_INTERRUPTED) { BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( "Babeltrace CLI", "Automatic source discovery interrupted by the user"); } goto error; } - status = create_implicit_component_args_from_auto_discovered_sources( + ret = create_implicit_component_args_from_auto_discovered_sources( &auto_disc, non_opts, non_opt_params, non_opt_loglevels, discovered_source_args); - if (status != 0) { + if (ret != 0) { goto error; } } @@ -4316,26 +4567,26 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], } } - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; goto end; } - cfg = bt_config_run_from_args_array(run_args, retcode, + status = bt_config_run_from_args_array(run_args, &cfg, plugin_paths, *default_log_level); - if (!cfg) { - goto error; + if (status != BT_CONFIG_CLI_ARGS_STATUS_OK) { + goto end; } cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode; + BT_OBJECT_MOVE_REF(*cfg_out, cfg); goto end; error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: - argpar_parse_ret_fini(&argpar_parse_ret); + argpar_iter_destroy(argpar_iter); + argpar_item_destroy(argpar_item); free(output); @@ -4377,7 +4628,9 @@ end: g_string_free(auto_disc_comp_name, TRUE); } - return cfg; + bt_object_put_ref(cfg); + + return status; } /* @@ -4413,22 +4666,25 @@ void print_gen_usage(FILE *fp) fprintf(fp, "Use `babeltrace2 COMMAND --help` to show the help of COMMAND.\n"); } -struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], - int *retcode, bool omit_system_plugin_path, +enum bt_config_cli_args_status bt_config_cli_args_create(int argc, + const char *argv[], struct bt_config **cfg, + bool omit_system_plugin_path, bool omit_home_plugin_path, const bt_value *initial_plugin_paths, const bt_interrupter *interrupter) { - struct bt_config *config = NULL; - int i; + enum bt_config_cli_args_status status; int top_level_argc; const char **top_level_argv; int command_argc = -1; const char **command_argv = NULL; const char *command_name = NULL; int default_log_level = -1; - struct argpar_parse_ret argpar_parse_ret = { 0 }; + struct argpar_iter *argpar_iter = NULL; + const struct argpar_item *argpar_item = NULL; + const struct argpar_error *argpar_error = NULL; bt_value *plugin_paths = NULL; + unsigned int consumed_args; /* Top-level option descriptions. */ static const struct argpar_opt_descr descrs[] = { @@ -4452,8 +4708,6 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], COMMAND_TYPE_QUERY, } command_type = COMMAND_TYPE_NONE; - *retcode = -1; - if (!initial_plugin_paths) { plugin_paths = bt_value_array_create(); if (!plugin_paths) { @@ -4482,99 +4736,137 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], print_version(); puts(""); print_gen_usage(stdout); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; goto end; } /* Skip first argument, the name of the program. */ top_level_argc = argc - 1; top_level_argv = argv + 1; - argpar_parse_ret = argpar_parse(top_level_argc, top_level_argv, - descrs, false); - if (argpar_parse_ret.error) { - BT_CLI_LOGE_APPEND_CAUSE( - "While parsing command-line arguments: %s", - argpar_parse_ret.error); + argpar_iter = argpar_iter_create(top_level_argc, top_level_argv, descrs); + if (!argpar_iter) { + BT_CLI_LOGE_APPEND_CAUSE_OOM(); goto error; } - for (i = 0; i < argpar_parse_ret.items->n_items; i++) { - struct argpar_item *item; + while (true) { + enum argpar_iter_next_status argpar_status; + + ARGPAR_ITEM_DESTROY_AND_RESET(argpar_item); + argpar_status = argpar_iter_next(argpar_iter, &argpar_item, &argpar_error); + + switch (argpar_status) { + case ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY: + BT_CLI_LOGE_APPEND_CAUSE_OOM(); + goto error; + case ARGPAR_ITER_NEXT_STATUS_ERROR: + { + if (argpar_error_type(argpar_error) + != ARGPAR_ERROR_TYPE_UNKNOWN_OPT) { + GString *err_str = format_arg_error(argpar_error, top_level_argv, + 0, "While parsing command-line arguments"); + BT_CLI_LOGE_APPEND_CAUSE("%s", err_str->str); + g_string_free(err_str, TRUE); + goto error; + } - item = argpar_parse_ret.items->items[i]; + break; + } + default: + break; + } - if (item->type == ARGPAR_ITEM_TYPE_OPT) { - struct argpar_item_opt *item_opt = - (struct argpar_item_opt *) item; + if (argpar_status == ARGPAR_ITER_NEXT_STATUS_END) { + break; + } - switch (item_opt->descr->id) { - case OPT_DEBUG: - default_log_level = - logging_level_min(default_log_level, BT_LOG_TRACE); - break; - case OPT_VERBOSE: - default_log_level = - logging_level_min(default_log_level, BT_LOG_INFO); - break; - case OPT_LOG_LEVEL: - { - int level = bt_log_get_level_from_string(item_opt->arg); + if (argpar_status == ARGPAR_ITER_NEXT_STATUS_ERROR) { + BT_ASSERT(argpar_error_type(argpar_error) == + ARGPAR_ERROR_TYPE_UNKNOWN_OPT); + /* + * Unknown option, assume this is implicitly the + * convert command, stop processing arguments. + */ + break; + } - if (level < 0) { - BT_CLI_LOGE_APPEND_CAUSE( - "Invalid argument for --log-level option:\n %s", - item_opt->arg); - goto error; - } + if (argpar_item_type(argpar_item) == ARGPAR_ITEM_TYPE_OPT) { + const struct argpar_opt_descr *opt_descr = + argpar_item_opt_descr(argpar_item); + const char *arg = argpar_item_opt_arg(argpar_item); - default_log_level = - logging_level_min(default_log_level, level); - break; + switch (opt_descr->id) { + case OPT_DEBUG: + default_log_level = + logging_level_min(default_log_level, BT_LOG_TRACE); + break; + case OPT_VERBOSE: + default_log_level = + logging_level_min(default_log_level, BT_LOG_INFO); + break; + case OPT_LOG_LEVEL: + { + int level = bt_log_get_level_from_string(arg); + + if (level < 0) { + BT_CLI_LOGE_APPEND_CAUSE( + "Invalid argument for --log-level option:\n %s", + arg); + goto error; } - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - plugin_paths, item_opt->arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - omit_home_plugin_path = true; - break; - case OPT_VERSION: - print_version(); - goto end; - case OPT_HELP: - print_gen_usage(stdout); - goto end; + + default_log_level = + logging_level_min(default_log_level, level); + break; + } + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + omit_home_plugin_path = true; + break; + case OPT_VERSION: + print_version(); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; + goto end; + case OPT_HELP: + print_gen_usage(stdout); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; + goto end; + default: + bt_common_abort(); } - } else if (item->type == ARGPAR_ITEM_TYPE_NON_OPT) { - struct argpar_item_non_opt *item_non_opt = - (struct argpar_item_non_opt *) item; + } else { + const char *arg = argpar_item_non_opt_arg(argpar_item); + unsigned int orig_index = argpar_item_non_opt_orig_index(argpar_item); + /* * First unknown argument: is it a known command * name? */ - command_argc = - top_level_argc - item_non_opt->orig_index - 1; - command_argv = - &top_level_argv[item_non_opt->orig_index + 1]; + command_argc = top_level_argc - orig_index - 1; + command_argv = &top_level_argv[orig_index + 1]; - if (strcmp(item_non_opt->arg, "convert") == 0) { + if (strcmp(arg, "convert") == 0) { command_type = COMMAND_TYPE_CONVERT; command_name = "convert"; - } else if (strcmp(item_non_opt->arg, "list-plugins") == 0) { + } else if (strcmp(arg, "list-plugins") == 0) { command_type = COMMAND_TYPE_LIST_PLUGINS; command_name = "list-plugins"; - } else if (strcmp(item_non_opt->arg, "help") == 0) { + } else if (strcmp(arg, "help") == 0) { command_type = COMMAND_TYPE_HELP; command_name = "help"; - } else if (strcmp(item_non_opt->arg, "query") == 0) { + } else if (strcmp(arg, "query") == 0) { command_type = COMMAND_TYPE_QUERY; command_name = "query"; - } else if (strcmp(item_non_opt->arg, "run") == 0) { + } else if (strcmp(arg, "run") == 0) { command_type = COMMAND_TYPE_RUN; command_name = "run"; } else { @@ -4588,12 +4880,16 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], command_argc++; command_argv--; } + + /* Stop processing arguments. */ break; } } if (command_type == COMMAND_TYPE_NONE) { - if (argpar_parse_ret.ingested_orig_args == top_level_argc) { + unsigned int ingested_orig_args = argpar_iter_ingested_orig_args(argpar_iter); + + if (ingested_orig_args == top_level_argc) { /* * We only got non-help, non-version general options * like --verbose and --debug, without any other @@ -4601,6 +4897,7 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], * usage and quit. */ print_gen_usage(stdout); + status = BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY; goto end; } @@ -4610,10 +4907,8 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], */ command_type = COMMAND_TYPE_CONVERT; command_name = "convert"; - command_argc = - top_level_argc - argpar_parse_ret.ingested_orig_args; - command_argv = - &top_level_argv[argpar_parse_ret.ingested_orig_args]; + command_argc = top_level_argc - ingested_orig_args; + command_argv = &top_level_argv[ingested_orig_args]; } BT_ASSERT(command_argv); @@ -4644,47 +4939,59 @@ struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], goto error; } + consumed_args = argpar_iter_ingested_orig_args(argpar_iter); + switch (command_type) { case COMMAND_TYPE_RUN: - config = bt_config_run_from_args(command_argc, command_argv, - retcode, plugin_paths, - default_log_level); + status = bt_config_run_from_args(command_argc, command_argv, + cfg, plugin_paths, + default_log_level, consumed_args); break; case COMMAND_TYPE_CONVERT: - config = bt_config_convert_from_args(command_argc, command_argv, - retcode, plugin_paths, &default_log_level, interrupter); + status = bt_config_convert_from_args(command_argc, command_argv, + cfg, plugin_paths, &default_log_level, interrupter, + consumed_args); break; case COMMAND_TYPE_LIST_PLUGINS: - config = bt_config_list_plugins_from_args(command_argc, - command_argv, retcode, plugin_paths); + status = bt_config_list_plugins_from_args(command_argc, + command_argv, cfg, plugin_paths, consumed_args); break; case COMMAND_TYPE_HELP: - config = bt_config_help_from_args(command_argc, - command_argv, retcode, plugin_paths, - default_log_level); + status = bt_config_help_from_args(command_argc, + command_argv, cfg, plugin_paths, + default_log_level, consumed_args); break; case COMMAND_TYPE_QUERY: - config = bt_config_query_from_args(command_argc, - command_argv, retcode, plugin_paths, - default_log_level); + status = bt_config_query_from_args(command_argc, + command_argv, cfg, plugin_paths, + default_log_level, consumed_args); break; default: bt_common_abort(); } - if (config) { - BT_ASSERT(default_log_level >= BT_LOG_TRACE); - config->log_level = default_log_level; - config->command_name = command_name; + if (status == BT_CONFIG_CLI_ARGS_STATUS_ERROR) { + goto error; + } else if (status == BT_CONFIG_CLI_ARGS_STATUS_INFO_ONLY) { + goto end; } + BT_ASSERT(status == BT_CONFIG_CLI_ARGS_STATUS_OK); + BT_ASSERT(*cfg); + BT_ASSERT(default_log_level >= BT_LOG_TRACE); + (*cfg)->log_level = default_log_level; + (*cfg)->command_name = command_name; + + status = BT_CONFIG_CLI_ARGS_STATUS_OK; goto end; error: - *retcode = 1; + status = BT_CONFIG_CLI_ARGS_STATUS_ERROR; end: - argpar_parse_ret_fini(&argpar_parse_ret); + argpar_error_destroy(argpar_error); + argpar_item_destroy(argpar_item); + argpar_iter_destroy(argpar_iter); bt_value_put_ref(plugin_paths); - return config; + return status; }