2024-10-24 21:32:28 +01:00
|
|
|
#include "command.h"
|
|
|
|
|
|
|
|
|
|
#include <blue/cmd.h>
|
|
|
|
|
#include <blue/object/string.h>
|
2024-11-20 22:12:50 +00:00
|
|
|
#include <blue/term/print.h>
|
|
|
|
|
#include <blue/term/tty.h>
|
2024-10-24 21:32:28 +01:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2024-11-20 22:12:50 +00:00
|
|
|
#include <stdio.h>
|
2024-10-24 21:32:28 +01:00
|
|
|
|
|
|
|
|
B_BTREE_DEFINE_SIMPLE_INSERT(
|
|
|
|
|
struct b_arglist_option, opt_node, opt_id, put_arglist_option)
|
|
|
|
|
B_BTREE_DEFINE_SIMPLE_GET(
|
|
|
|
|
struct b_arglist_option, unsigned int, opt_node, opt_id, get_arglist_option)
|
|
|
|
|
|
|
|
|
|
B_BTREE_DEFINE_SIMPLE_INSERT(
|
|
|
|
|
struct b_arglist_value, val_node, val_id, put_arglist_value)
|
|
|
|
|
B_BTREE_DEFINE_SIMPLE_GET(
|
|
|
|
|
struct b_arglist_value, unsigned int, val_node, val_id, get_arglist_value)
|
|
|
|
|
|
|
|
|
|
struct argv_parser {
|
|
|
|
|
struct b_command *cmd;
|
|
|
|
|
struct b_arglist *arglist;
|
|
|
|
|
|
|
|
|
|
b_queue_iterator 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 b_arglist *b_arglist_create(void)
|
|
|
|
|
{
|
|
|
|
|
struct b_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 b_command *cmd)
|
|
|
|
|
{
|
|
|
|
|
parser->cmd = cmd;
|
|
|
|
|
b_queue_iterator_begin(&cmd->b_arg, &parser->arg_it);
|
|
|
|
|
parser->nr_values_cur_arg = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static b_status set_opt(struct b_arglist *args, struct b_command_option *opt)
|
|
|
|
|
{
|
|
|
|
|
struct b_arglist_option *arg_opt = malloc(sizeof *arg_opt);
|
|
|
|
|
if (!arg_opt) {
|
|
|
|
|
return B_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 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)
|
|
|
|
|
{
|
|
|
|
|
struct b_arglist_option *arglist_opt
|
|
|
|
|
= get_arglist_option(&args->list_options, B_COMMAND_INVALID_ID);
|
|
|
|
|
if (!arglist_opt) {
|
|
|
|
|
arglist_opt = malloc(sizeof *arglist_opt);
|
|
|
|
|
|
|
|
|
|
if (!arglist_opt) {
|
|
|
|
|
return B_ERR_NO_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(arglist_opt, 0x0, sizeof *arglist_opt);
|
|
|
|
|
|
|
|
|
|
arglist_opt->opt_id = B_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 B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct b_arglist_value *val = malloc(sizeof *val);
|
|
|
|
|
if (!val) {
|
|
|
|
|
return B_ERR_NO_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(val, 0x0, sizeof *val);
|
|
|
|
|
val->val_id = arg->arg_id;
|
|
|
|
|
val->val_type = B_COMMAND_ARG_STRING;
|
|
|
|
|
val->val_str = b_strdup(value);
|
|
|
|
|
|
|
|
|
|
put_arglist_value(&arglist_opt->opt_values, val);
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static b_status put_opt_arg(
|
|
|
|
|
struct b_arglist *args, struct b_command_option *opt,
|
|
|
|
|
struct b_command_arg *arg, const char *value)
|
|
|
|
|
{
|
|
|
|
|
struct b_arglist_option *arglist_opt
|
|
|
|
|
= get_arglist_option(&args->list_options, opt->opt_id);
|
|
|
|
|
if (!arglist_opt) {
|
|
|
|
|
arglist_opt = malloc(sizeof *arglist_opt);
|
|
|
|
|
|
|
|
|
|
if (!arglist_opt) {
|
|
|
|
|
return B_ERR_NO_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(arglist_opt, 0x0, sizeof *arglist_opt);
|
|
|
|
|
|
|
|
|
|
arglist_opt->opt_id = opt->opt_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 B_ERR_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct b_arglist_value *val = malloc(sizeof *val);
|
|
|
|
|
if (!val) {
|
|
|
|
|
return B_ERR_NO_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(val, 0x0, sizeof *val);
|
|
|
|
|
val->val_id = arg->arg_id;
|
|
|
|
|
val->val_type = B_COMMAND_ARG_STRING;
|
|
|
|
|
val->val_str = b_strdup(value);
|
|
|
|
|
|
|
|
|
|
put_arglist_value(&arglist_opt->opt_values, val);
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 "'");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static b_status check_required_args(struct argv_parser *parser)
|
|
|
|
|
{
|
|
|
|
|
if (!b_queue_iterator_is_valid(&parser->arg_it)) {
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct b_command_arg *arg
|
|
|
|
|
= b_unbox(struct b_command_arg, parser->arg_it.entry, arg_entry);
|
|
|
|
|
if (!arg) {
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_0_OR_MORE_VALUES) {
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_1_OR_MORE_VALUES) {
|
|
|
|
|
if (parser->nr_values_cur_arg > 0) {
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
report_missing_args(parser, NULL, arg, 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);
|
|
|
|
|
return B_ERR_BAD_FORMAT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static b_status parse_arg(struct argv_parser *parser)
|
|
|
|
|
{
|
|
|
|
|
while (1) {
|
|
|
|
|
const char *value = peek(parser);
|
|
|
|
|
if (!value || value[0] == '-') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct b_command *subcmd
|
|
|
|
|
= b_command_get_subcommand_with_name(parser->cmd, value);
|
|
|
|
|
if (subcmd) {
|
|
|
|
|
move_to_subcommand(parser, subcmd);
|
|
|
|
|
advance(parser);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct b_command_arg *arg = b_unbox(
|
|
|
|
|
struct b_command_arg, parser->arg_it.entry, arg_entry);
|
|
|
|
|
|
|
|
|
|
if (!arg) {
|
|
|
|
|
report_unexpected_arg(parser, value);
|
|
|
|
|
return B_ERR_BAD_FORMAT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_status status = put_arg(parser->arglist, arg, value);
|
|
|
|
|
parser->nr_values_cur_arg++;
|
|
|
|
|
advance(parser);
|
|
|
|
|
|
|
|
|
|
if (status == B_ERR_INVALID_ARGUMENT) {
|
|
|
|
|
report_invalid_arg_value(parser, NULL, arg, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (B_ERR(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_0_OR_1_VALUES) {
|
|
|
|
|
b_queue_iterator_next(&parser->arg_it);
|
|
|
|
|
parser->nr_values_cur_arg = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (parser->nr_values_cur_arg == arg->arg_nr_values) {
|
|
|
|
|
b_queue_iterator_next(&parser->arg_it);
|
|
|
|
|
parser->nr_values_cur_arg = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct b_command_arg *get_first_arg(struct b_command *cmd)
|
|
|
|
|
{
|
|
|
|
|
struct b_command_arg *arg = NULL;
|
|
|
|
|
|
|
|
|
|
struct b_queue_entry *first_arg = b_queue_first(&cmd->b_arg);
|
|
|
|
|
if (first_arg) {
|
|
|
|
|
arg = b_unbox(struct b_command_arg, first_arg, arg_entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return arg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static b_status parse_short_opt(struct argv_parser *parser)
|
|
|
|
|
{
|
|
|
|
|
const char *flags = peek(parser);
|
|
|
|
|
flags += 1;
|
|
|
|
|
|
|
|
|
|
struct b_command_option *opt = NULL;
|
|
|
|
|
struct b_command_arg *arg = NULL;
|
|
|
|
|
struct b_command *subcmd = NULL;
|
|
|
|
|
|
|
|
|
|
int nr_args_cur_opt = 0;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
char flag = *flags;
|
|
|
|
|
if (!flag) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-19 15:20:11 +00:00
|
|
|
subcmd = NULL;
|
2024-10-24 21:32:28 +01:00
|
|
|
opt = b_command_get_option_with_short_name(parser->cmd, flag);
|
|
|
|
|
if (!opt) {
|
|
|
|
|
subcmd = b_command_get_subcommand_with_short_name(
|
|
|
|
|
parser->cmd, flag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (subcmd) {
|
|
|
|
|
move_to_subcommand(parser, subcmd);
|
|
|
|
|
flags++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!opt) {
|
|
|
|
|
struct b_string *usage = z__b_command_default_usage_string(
|
|
|
|
|
parser->cmd, NULL);
|
|
|
|
|
b_err("unrecognised argument '" F_YELLOW "-%c" F_RESET
|
|
|
|
|
"'\n\n",
|
|
|
|
|
flag, b_string_ptr(usage));
|
|
|
|
|
b_i("usage: %s", b_string_ptr(usage));
|
|
|
|
|
b_i("for more information, use '" F_YELLOW
|
|
|
|
|
"--help" F_RESET "'\n");
|
|
|
|
|
b_string_release(usage);
|
|
|
|
|
|
|
|
|
|
return B_ERR_NO_ENTRY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flags++;
|
|
|
|
|
|
|
|
|
|
if (b_queue_empty(&opt->opt_args)) {
|
|
|
|
|
set_opt(parser->arglist, opt);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
advance(parser);
|
|
|
|
|
|
|
|
|
|
if (!opt || b_queue_empty(&opt->opt_args)) {
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_queue_iterator it;
|
|
|
|
|
b_queue_iterator_begin(&opt->opt_args, &it);
|
|
|
|
|
const char *value = flags;
|
|
|
|
|
if (*value == '\0') {
|
|
|
|
|
value = advance(parser);
|
|
|
|
|
if (!value || *value == '-') {
|
|
|
|
|
value = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (b_queue_iterator_is_valid(&it)) {
|
|
|
|
|
arg = b_unbox(struct b_command_arg, it.entry, arg_entry);
|
|
|
|
|
|
|
|
|
|
if (!arg) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!value || *value == '\0' || *value == '-') {
|
|
|
|
|
value = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
b_status status
|
|
|
|
|
= put_opt_arg(parser->arglist, opt, arg, value);
|
|
|
|
|
nr_args_cur_opt++;
|
|
|
|
|
|
|
|
|
|
if (status == B_ERR_INVALID_ARGUMENT) {
|
|
|
|
|
report_invalid_arg_value(parser, NULL, arg, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (B_ERR(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_0_OR_1_VALUES) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
nr_args_cur_opt = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_0_OR_MORE_VALUES && !value) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
nr_args_cur_opt = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_1_OR_MORE_VALUES && !value) {
|
|
|
|
|
if (nr_args_cur_opt > 0) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
nr_args_cur_opt = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
report_missing_args(parser, opt, arg, nr_args_cur_opt);
|
|
|
|
|
return B_ERR_BAD_FORMAT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nr_args_cur_opt == arg->arg_nr_values) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
nr_args_cur_opt = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!value) {
|
|
|
|
|
report_missing_args(parser, opt, arg, nr_args_cur_opt);
|
|
|
|
|
return B_ERR_BAD_FORMAT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = advance(parser);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static b_status parse_long_opt(struct argv_parser *parser)
|
|
|
|
|
{
|
|
|
|
|
const char *opt_name = peek(parser);
|
|
|
|
|
opt_name += 2;
|
|
|
|
|
|
|
|
|
|
struct b_command_option *opt = NULL;
|
|
|
|
|
struct b_command *subcmd = NULL;
|
|
|
|
|
|
|
|
|
|
opt = b_command_get_option_with_long_name(parser->cmd, opt_name);
|
|
|
|
|
if (!opt) {
|
|
|
|
|
subcmd = b_command_get_subcommand_with_long_name(
|
|
|
|
|
parser->cmd, opt_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!opt && !subcmd) {
|
|
|
|
|
struct b_string *usage
|
|
|
|
|
= z__b_command_default_usage_string(parser->cmd, NULL);
|
|
|
|
|
b_err("unrecognised argument '" F_YELLOW "--%s" F_RESET
|
|
|
|
|
"'\n\nusage: %s\n\nfor more information, use '" F_YELLOW
|
|
|
|
|
"--help" F_RESET "'\n",
|
|
|
|
|
opt_name, b_string_ptr(usage));
|
|
|
|
|
b_string_release(usage);
|
|
|
|
|
|
|
|
|
|
return B_ERR_NO_ENTRY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
advance(parser);
|
|
|
|
|
|
|
|
|
|
if (subcmd) {
|
|
|
|
|
parser->cmd = subcmd;
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (b_queue_empty(&opt->opt_args)) {
|
|
|
|
|
return set_opt(parser->arglist, opt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int nr_args_total = 0;
|
|
|
|
|
int nr_args_cur_opt = 0;
|
|
|
|
|
|
|
|
|
|
b_queue_iterator it;
|
|
|
|
|
b_queue_iterator_begin(&opt->opt_args, &it);
|
|
|
|
|
struct b_command_arg *arg
|
|
|
|
|
= b_unbox(struct b_command_arg, it.entry, arg_entry);
|
|
|
|
|
|
|
|
|
|
while (b_queue_iterator_is_valid(&it)) {
|
|
|
|
|
arg = b_unbox(struct b_command_arg, it.entry, arg_entry);
|
|
|
|
|
if (!arg) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *value = peek(parser);
|
|
|
|
|
if (!value || value[0] == '-') {
|
|
|
|
|
value = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
b_status status
|
|
|
|
|
= put_opt_arg(parser->arglist, opt, arg, value);
|
|
|
|
|
nr_args_total++;
|
|
|
|
|
nr_args_cur_opt++;
|
|
|
|
|
advance(parser);
|
|
|
|
|
|
|
|
|
|
if (status == B_ERR_INVALID_ARGUMENT) {
|
|
|
|
|
report_invalid_arg_value(parser, NULL, arg, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (B_ERR(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_0_OR_1_VALUES) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
nr_args_cur_opt = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_0_OR_MORE_VALUES && !value) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
nr_args_cur_opt = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg->arg_nr_values == B_ARG_1_OR_MORE_VALUES && !value) {
|
|
|
|
|
if (nr_args_cur_opt > 0) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
nr_args_cur_opt = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
report_missing_args(parser, opt, arg, nr_args_cur_opt);
|
|
|
|
|
return B_ERR_BAD_FORMAT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nr_args_cur_opt == arg->arg_nr_values) {
|
|
|
|
|
b_queue_iterator_next(&it);
|
|
|
|
|
nr_args_cur_opt = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!value) {
|
|
|
|
|
report_missing_args(parser, opt, arg, nr_args_cur_opt);
|
|
|
|
|
return B_ERR_BAD_FORMAT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool should_show_help(struct b_command *cmd, struct b_arglist *args)
|
|
|
|
|
{
|
|
|
|
|
bool show_help = false;
|
|
|
|
|
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
|
|
|
|
|
b_arglist_iterator it;
|
|
|
|
|
b_arglist_foreach(&it, args)
|
|
|
|
|
{
|
|
|
|
|
if (it.opt_id == B_COMMAND_OPTION_HELP) {
|
|
|
|
|
show_help = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (count == 0 && (cmd->b_flags & B_COMMAND_SHOW_HELP_BY_DEFAULT)) {
|
|
|
|
|
show_help = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return show_help;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_status b_arglist_parse(
|
|
|
|
|
struct b_arglist *args, struct b_command **cmd, int argc, const char **argv)
|
|
|
|
|
{
|
|
|
|
|
struct argv_parser parser
|
|
|
|
|
= {.arglist = args, .argc = argc - 1, .argv = argv + 1};
|
|
|
|
|
|
|
|
|
|
move_to_subcommand(&parser, *cmd);
|
|
|
|
|
|
|
|
|
|
b_status status = B_SUCCESS;
|
|
|
|
|
bool arg_only = false;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
const char *arg = peek(&parser);
|
|
|
|
|
if (!arg) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t len = strlen(arg);
|
|
|
|
|
status = B_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 = B_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 (B_ERR(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool show_help = should_show_help(parser.cmd, args);
|
|
|
|
|
|
|
|
|
|
if (!show_help) {
|
|
|
|
|
status = check_required_args(&parser);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (B_ERR(status)) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*cmd = parser.cmd;
|
|
|
|
|
return B_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void arglist_option_destroy(struct b_arglist_option *opt)
|
|
|
|
|
{
|
|
|
|
|
free(opt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void arglist_value_destroy(struct b_arglist_value *val)
|
|
|
|
|
{
|
|
|
|
|
if (val->val_type == B_COMMAND_ARG_STRING) {
|
|
|
|
|
free(val->val_str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void b_arglist_destroy(struct b_arglist *args)
|
|
|
|
|
{
|
|
|
|
|
b_btree_iterator opt_it, args_it;
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_begin(&args->list_options, &opt_it);
|
|
|
|
|
while (b_btree_iterator_is_valid(&opt_it)) {
|
|
|
|
|
struct b_arglist_option *opt
|
|
|
|
|
= b_unbox(struct b_arglist_option, opt_it.node, opt_node);
|
|
|
|
|
|
|
|
|
|
if (!opt) {
|
|
|
|
|
b_btree_iterator_next(&opt_it);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_begin(&opt->opt_values, &args_it);
|
|
|
|
|
while (b_btree_iterator_is_valid(&args_it)) {
|
|
|
|
|
struct b_arglist_value *val = b_unbox(
|
|
|
|
|
struct b_arglist_value, args_it.node, val_node);
|
|
|
|
|
|
|
|
|
|
if (!val) {
|
|
|
|
|
b_btree_iterator_next(&args_it);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_erase(&args_it);
|
|
|
|
|
arglist_value_destroy(val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_erase(&opt_it);
|
|
|
|
|
arglist_option_destroy(opt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t b_arglist_get_count(
|
|
|
|
|
const b_arglist *args, unsigned int opt_id, unsigned int arg_id)
|
|
|
|
|
{
|
|
|
|
|
size_t count = 0;
|
|
|
|
|
b_arglist_iterator it;
|
|
|
|
|
b_arglist_iterator_begin(args, opt_id, arg_id, &it);
|
|
|
|
|
while (b_arglist_iterator_is_valid(&it)) {
|
|
|
|
|
count++;
|
|
|
|
|
b_arglist_iterator_next(&it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool arglist_iterator_next(struct b_iterator *it)
|
|
|
|
|
{
|
|
|
|
|
return b_arglist_iterator_next((struct b_arglist_iterator *)it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool arglist_iterator_is_valid(const struct b_iterator *it)
|
|
|
|
|
{
|
|
|
|
|
return b_arglist_iterator_is_valid((const struct b_arglist_iterator *)it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct b_arglist_option *advance_to_next_opt(struct b_arglist_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 struct b_arglist_value *advance_to_next_arg(struct b_arglist_iterator *it)
|
|
|
|
|
{
|
|
|
|
|
struct b_arglist_value *val;
|
|
|
|
|
|
|
|
|
|
// b_btree_iterator_next(&it->_arg_it);
|
|
|
|
|
|
|
|
|
|
while (b_btree_iterator_is_valid(&it->_arg_it)) {
|
|
|
|
|
val = b_unbox(struct b_arglist_value, it->_arg_it.node, val_node);
|
|
|
|
|
if (val
|
|
|
|
|
&& (val->val_id == it->_arg_filter
|
|
|
|
|
|| it->_arg_filter == B_COMMAND_INVALID_ID)) {
|
|
|
|
|
it->value = val;
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_next(&it->_arg_it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static b_iterator_ops it_ops = {
|
|
|
|
|
.it_next = arglist_iterator_next,
|
|
|
|
|
.it_is_valid = arglist_iterator_is_valid,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int b_arglist_iterator_begin(
|
|
|
|
|
const struct b_arglist *args, unsigned int opt_filter,
|
|
|
|
|
unsigned int arg_filter, struct b_arglist_iterator *it)
|
|
|
|
|
{
|
|
|
|
|
memset(it, 0x0, sizeof *it);
|
|
|
|
|
|
2024-11-19 15:20:11 +00:00
|
|
|
it->opt_id = B_COMMAND_INVALID_ID;
|
|
|
|
|
|
2024-10-24 21:32:28 +01:00
|
|
|
it->_base.it_ops = &it_ops;
|
|
|
|
|
it->_opt_filter = opt_filter;
|
|
|
|
|
it->_arg_filter = arg_filter;
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_begin(&args->list_options, &it->_opt_it);
|
2024-11-19 15:20:11 +00:00
|
|
|
struct b_arglist_option *opt = NULL;
|
|
|
|
|
struct b_arglist_value *val = NULL;
|
2024-10-24 21:32:28 +01:00
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
|
b_btree_iterator_next(&it->_opt_it);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_begin(&opt->opt_values, &it->_arg_it);
|
|
|
|
|
while (1) {
|
|
|
|
|
if (!b_btree_iterator_is_valid(&it->_arg_it)) {
|
2024-11-19 15:20:11 +00:00
|
|
|
if (arg_filter == B_COMMAND_INVALID_ID) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-24 21:32:28 +01:00
|
|
|
val = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val = b_unbox(
|
|
|
|
|
struct b_arglist_value, it->_arg_it.node, val_node);
|
|
|
|
|
if (!val
|
|
|
|
|
|| (arg_filter != val->val_id
|
|
|
|
|
&& arg_filter != B_COMMAND_INVALID_ID)) {
|
|
|
|
|
b_btree_iterator_next(&it->_arg_it);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
it->opt_id = opt->opt_id;
|
|
|
|
|
it->value = val;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool b_arglist_iterator_next(struct b_arglist_iterator *it)
|
|
|
|
|
{
|
|
|
|
|
struct b_arglist_option *opt;
|
|
|
|
|
struct b_arglist_value *val;
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_next(&it->_arg_it);
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
val = advance_to_next_arg(it);
|
|
|
|
|
|
|
|
|
|
if (val) {
|
|
|
|
|
it->value = val;
|
|
|
|
|
it->i++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_next(&it->_opt_it);
|
|
|
|
|
opt = advance_to_next_opt(it);
|
|
|
|
|
if (!opt) {
|
|
|
|
|
it->value = NULL;
|
2024-11-19 15:20:11 +00:00
|
|
|
it->opt_id = B_COMMAND_INVALID_ID;
|
2024-10-24 21:32:28 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b_btree_iterator_begin(&opt->opt_values, &it->_arg_it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool b_arglist_iterator_is_valid(const struct b_arglist_iterator *it)
|
|
|
|
|
{
|
2024-11-19 15:20:11 +00:00
|
|
|
return it->opt_id != B_COMMAND_INVALID_ID || it->value != NULL;
|
2024-10-24 21:32:28 +01:00
|
|
|
}
|