add cmd module from corelib
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(bluelib C)
|
||||
|
||||
set(b_modules core object term)
|
||||
set(b_modules core object term cmd)
|
||||
|
||||
set(b_system_name ${CMAKE_SYSTEM_NAME})
|
||||
string(TOLOWER ${b_system_name} b_system_name)
|
||||
|
||||
175
cmd-test/example.c
Normal file
175
cmd-test/example.c
Normal file
@@ -0,0 +1,175 @@
|
||||
#include <blue/cmd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
enum {
|
||||
CMD_TEST,
|
||||
CMD_SUB,
|
||||
OPT_NAME,
|
||||
OPT_NAME_VALUE,
|
||||
OPT_OTHER,
|
||||
OPT_OTHER_VALUE,
|
||||
OPT_VERBOSE,
|
||||
OPT_REALLY_VERBOSE,
|
||||
OPT_MODE,
|
||||
OPT_MODE_VALUE,
|
||||
OPT_MODE_VALUE2,
|
||||
ARG_FILE,
|
||||
ARG_MORE,
|
||||
};
|
||||
|
||||
const char *text
|
||||
= "No one rejects, dislikes, or avoids pleasure itself, because it is "
|
||||
"pleasure, but because those who do not know how to pursue pleasure "
|
||||
"rationally encounter consequences that are extremely painful. Nor "
|
||||
"again is there anyone who loves or pursues or desires to obtain "
|
||||
"pain of itself, because it is pain, but because occasionally "
|
||||
"circumstances occur in which toil and pain can procure him some "
|
||||
"great pleasure.";
|
||||
|
||||
static int test_command(
|
||||
const b_command *self, const b_arglist *opt, const b_array *args)
|
||||
{
|
||||
printf("Hello, world!\n");
|
||||
|
||||
b_arglist_iterator it;
|
||||
b_arglist_iterator_begin(
|
||||
opt, B_COMMAND_INVALID_ID, B_COMMAND_INVALID_ID, &it);
|
||||
while (b_arglist_iterator_is_valid(&it)) {
|
||||
printf("opt:%u,arg:%u,i:%zu,value: %s\n", it.opt_id,
|
||||
it.value->val_id, it.i, it.value->val_str);
|
||||
|
||||
b_arglist_iterator_next(&it);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
B_COMMAND(CMD_TEST, B_COMMAND_INVALID_ID)
|
||||
{
|
||||
B_COMMAND_NAME("test");
|
||||
B_COMMAND_DESC("A test command.");
|
||||
B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT);
|
||||
B_COMMAND_FUNCTION(test_command);
|
||||
|
||||
B_COMMAND_OPTION(OPT_NAME)
|
||||
{
|
||||
B_OPTION_LONG_NAME("name");
|
||||
B_OPTION_SHORT_NAME('n');
|
||||
B_OPTION_DESC("The name of the data.");
|
||||
B_OPTION_ARG(OPT_NAME_VALUE)
|
||||
{
|
||||
B_ARG_NAME("name");
|
||||
B_ARG_NR_VALUES(2);
|
||||
}
|
||||
}
|
||||
|
||||
B_COMMAND_OPTION(OPT_OTHER)
|
||||
{
|
||||
B_OPTION_LONG_NAME("other");
|
||||
B_OPTION_DESC(text);
|
||||
|
||||
B_OPTION_ARG(OPT_OTHER_VALUE)
|
||||
{
|
||||
B_ARG_NAME("value");
|
||||
B_ARG_NR_VALUES(B_ARG_1_OR_MORE_VALUES);
|
||||
}
|
||||
}
|
||||
|
||||
B_COMMAND_OPTION(OPT_VERBOSE)
|
||||
{
|
||||
B_OPTION_LONG_NAME("verbose");
|
||||
B_OPTION_SHORT_NAME('v');
|
||||
B_OPTION_DESC("Show detailed log output.");
|
||||
}
|
||||
|
||||
B_COMMAND_OPTION(OPT_REALLY_VERBOSE)
|
||||
{
|
||||
B_OPTION_LONG_NAME("really-verbose");
|
||||
B_OPTION_SHORT_NAME('V');
|
||||
B_OPTION_DESC("Show REALLY detailed log output.");
|
||||
}
|
||||
|
||||
B_COMMAND_OPTION(OPT_MODE)
|
||||
{
|
||||
B_OPTION_SHORT_NAME('m');
|
||||
B_OPTION_LONG_NAME("mode");
|
||||
B_OPTION_DESC("modes to operate in.");
|
||||
|
||||
B_OPTION_ARG(OPT_MODE_VALUE)
|
||||
{
|
||||
B_ARG_NAME("mode");
|
||||
B_ARG_NR_VALUES(1);
|
||||
B_ARG_ALLOWED_VALUES("fast", "slow");
|
||||
}
|
||||
|
||||
B_OPTION_ARG(OPT_MODE_VALUE2)
|
||||
{
|
||||
B_ARG_NAME("mode2");
|
||||
B_ARG_NR_VALUES(1);
|
||||
B_ARG_ALLOWED_VALUES("very-fast", "very-slow");
|
||||
}
|
||||
}
|
||||
|
||||
B_COMMAND_ARG(ARG_FILE)
|
||||
{
|
||||
B_ARG_NAME("file");
|
||||
B_ARG_DESC("The file(s) to use");
|
||||
B_ARG_NR_VALUES(2);
|
||||
}
|
||||
|
||||
B_COMMAND_ARG(ARG_MORE)
|
||||
{
|
||||
B_ARG_NAME("more");
|
||||
B_ARG_DESC("More args to use");
|
||||
B_ARG_ALLOWED_VALUES("how", "wow");
|
||||
B_ARG_NR_VALUES(2);
|
||||
}
|
||||
|
||||
B_COMMAND_HELP_OPTION();
|
||||
|
||||
B_COMMAND_USAGE()
|
||||
{
|
||||
B_COMMAND_USAGE_OPT(OPT_NAME);
|
||||
B_COMMAND_USAGE_ARG(ARG_FILE);
|
||||
B_COMMAND_USAGE_ARG(ARG_MORE);
|
||||
}
|
||||
}
|
||||
|
||||
B_COMMAND(CMD_SUB, CMD_TEST)
|
||||
{
|
||||
B_COMMAND_NAME("sub");
|
||||
B_COMMAND_LONG_NAME("sub");
|
||||
B_COMMAND_SHORT_NAME('S');
|
||||
B_COMMAND_DESC("A test subcommand");
|
||||
B_COMMAND_FLAGS(B_COMMAND_SHOW_HELP_BY_DEFAULT);
|
||||
B_COMMAND_FUNCTION(test_command);
|
||||
|
||||
B_COMMAND_OPTION(OPT_NAME)
|
||||
{
|
||||
B_OPTION_LONG_NAME("name");
|
||||
B_OPTION_SHORT_NAME('n');
|
||||
B_OPTION_DESC("The name of the data");
|
||||
B_OPTION_ARG(OPT_NAME_VALUE)
|
||||
{
|
||||
B_ARG_NAME("name");
|
||||
}
|
||||
}
|
||||
|
||||
B_COMMAND_OPTION(OPT_OTHER)
|
||||
{
|
||||
B_OPTION_LONG_NAME("other");
|
||||
B_OPTION_SHORT_NAME('o');
|
||||
B_OPTION_DESC("The other argument");
|
||||
B_OPTION_ARG(OPT_OTHER_VALUE)
|
||||
{
|
||||
B_ARG_NAME("value");
|
||||
}
|
||||
}
|
||||
|
||||
B_COMMAND_HELP_OPTION();
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
return b_command_dispatch(CMD_TEST, argc, argv);
|
||||
}
|
||||
3
cmd/CMakeLists.txt
Normal file
3
cmd/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
include(../cmake/Templates.cmake)
|
||||
|
||||
add_bluelib_module(NAME cmd DEPENDENCIES core object term)
|
||||
154
cmd/arg.c
Normal file
154
cmd/arg.c
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "command.h"
|
||||
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/object/string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct b_command_arg *b_command_arg_create(void)
|
||||
{
|
||||
struct b_command_arg *out = malloc(sizeof *out);
|
||||
if (!out) {
|
||||
return out;
|
||||
}
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
return out;
|
||||
}
|
||||
|
||||
b_status b_command_arg_set_name(struct b_command_arg *arg, const char *name)
|
||||
{
|
||||
char *n = b_strdup(name);
|
||||
if (!n) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (arg->arg_name) {
|
||||
free(arg->arg_name);
|
||||
arg->arg_name = NULL;
|
||||
}
|
||||
|
||||
arg->arg_name = n;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_arg_set_description(
|
||||
struct b_command_arg *arg, const char *description)
|
||||
{
|
||||
char *desc = b_strdup(description);
|
||||
if (!desc) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (arg->arg_description) {
|
||||
free(arg->arg_description);
|
||||
arg->arg_description = NULL;
|
||||
}
|
||||
|
||||
arg->arg_description = desc;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_arg_set_nr_values(
|
||||
struct b_command_arg *arg, enum b_command_arg_value_count nr_values)
|
||||
{
|
||||
arg->arg_nr_values = nr_values;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_arg_set_allowed_values(
|
||||
struct b_command_arg *arg, const char **allowed_values)
|
||||
{
|
||||
size_t count;
|
||||
for (count = 0; allowed_values[count]; count++)
|
||||
;
|
||||
|
||||
char **values = calloc(count + 1, sizeof *values);
|
||||
if (!values) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
values[i] = b_strdup(allowed_values[i]);
|
||||
if (!values[i]) {
|
||||
/* TODO also free strings in `values` */
|
||||
free(values);
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
arg->arg_allowed_values = values;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
void z__b_get_arg_usage_string(
|
||||
struct b_command_arg *arg, bool colour, struct b_string *out)
|
||||
{
|
||||
bool optional = false, multi = false;
|
||||
switch (arg->arg_nr_values) {
|
||||
case B_ARG_0_OR_1_VALUES:
|
||||
optional = true;
|
||||
multi = false;
|
||||
break;
|
||||
case B_ARG_0_OR_MORE_VALUES:
|
||||
optional = true;
|
||||
multi = true;
|
||||
break;
|
||||
case B_ARG_1_OR_MORE_VALUES:
|
||||
optional = false;
|
||||
multi = true;
|
||||
break;
|
||||
default:
|
||||
optional = false;
|
||||
multi = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (optional) {
|
||||
b_string_append_cstrf(
|
||||
out, colour ? F_GREEN "[%s]" : "[%s]", arg->arg_name);
|
||||
} else {
|
||||
b_string_append_cstrf(
|
||||
out, colour ? F_GREEN "<%s>" : "<%s>", arg->arg_name);
|
||||
}
|
||||
|
||||
for (int i = 1; i < arg->arg_nr_values; i++) {
|
||||
b_string_append_cstrf(out, " <%s>", arg->arg_name);
|
||||
}
|
||||
|
||||
if (multi) {
|
||||
b_string_append_cstr(out, "...");
|
||||
}
|
||||
|
||||
if (colour) {
|
||||
b_string_append_cstr(out, F_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
void z__b_get_arg_description(struct b_command_arg *arg, b_string *out)
|
||||
{
|
||||
if (arg->arg_description) {
|
||||
b_string_append_cstr(out, arg->arg_description);
|
||||
}
|
||||
|
||||
if (!arg->arg_allowed_values) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg->arg_description) {
|
||||
b_string_append_cstr(out, " ");
|
||||
}
|
||||
|
||||
b_string_append_cstr(out, "[values:");
|
||||
|
||||
for (size_t i = 0; arg->arg_allowed_values[i]; i++) {
|
||||
if (i > 0) {
|
||||
b_string_append_cstr(out, ",");
|
||||
}
|
||||
|
||||
b_string_append_cstrf(
|
||||
out, " " F_GREEN "%s" F_RESET, arg->arg_allowed_values[i]);
|
||||
}
|
||||
|
||||
b_string_append_cstr(out, "]");
|
||||
}
|
||||
914
cmd/arglist.c
Normal file
914
cmd/arglist.c
Normal file
@@ -0,0 +1,914 @@
|
||||
#include "command.h"
|
||||
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/object/string.h>
|
||||
#include <blue/term.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
struct b_arglist_option *opt;
|
||||
struct b_arglist_value *val;
|
||||
|
||||
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)) {
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
b_btree_iterator_begin(&opt->opt_values, &it->_arg_it);
|
||||
}
|
||||
}
|
||||
|
||||
bool b_arglist_iterator_is_valid(const struct b_arglist_iterator *it)
|
||||
{
|
||||
return it->value != NULL;
|
||||
}
|
||||
732
cmd/command.c
Normal file
732
cmd/command.c
Normal file
@@ -0,0 +1,732 @@
|
||||
#include "command.h"
|
||||
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/object/string.h>
|
||||
#include <blue/term.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define OUTPUT_STREAM stderr
|
||||
|
||||
static struct b_btree command_list = {};
|
||||
|
||||
B_BTREE_DEFINE_SIMPLE_GET(struct b_command, unsigned int, b_node, b_id, get_command)
|
||||
B_BTREE_DEFINE_SIMPLE_INSERT(struct b_command, b_node, b_id, put_command)
|
||||
|
||||
enum item_type {
|
||||
ITEM_OPTION,
|
||||
ITEM_ARG,
|
||||
ITEM_SUBCOMMAND,
|
||||
};
|
||||
|
||||
struct b_command *b_command_create(unsigned int id)
|
||||
{
|
||||
struct b_command *out = malloc(sizeof *out);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
|
||||
out->b_id = id;
|
||||
out->b_parent_id = B_COMMAND_INVALID_ID;
|
||||
return out;
|
||||
}
|
||||
|
||||
void b_command_destroy(struct b_command *cmd)
|
||||
{
|
||||
}
|
||||
|
||||
b_status b_command_register(struct b_command *cmd)
|
||||
{
|
||||
struct b_command *tmp = get_command(&command_list, cmd->b_id);
|
||||
if (tmp) {
|
||||
return B_ERR_NAME_EXISTS;
|
||||
}
|
||||
|
||||
put_command(&command_list, cmd);
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_set_name(struct b_command *cmd, const char *name)
|
||||
{
|
||||
char *n = b_strdup(name);
|
||||
if (!n) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (cmd->b_name) {
|
||||
free(cmd->b_name);
|
||||
cmd->b_name = NULL;
|
||||
}
|
||||
|
||||
cmd->b_name = n;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_set_long_name(struct b_command *cmd, const char *name)
|
||||
{
|
||||
char *n = b_strdup(name);
|
||||
if (!n) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (cmd->b_long_name) {
|
||||
free(cmd->b_long_name);
|
||||
cmd->b_long_name = NULL;
|
||||
}
|
||||
|
||||
cmd->b_long_name = n;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_set_short_name(struct b_command *cmd, char name)
|
||||
{
|
||||
cmd->b_short_name = name;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_set_flags(struct b_command *cmd, b_command_flags flags)
|
||||
{
|
||||
cmd->b_flags = flags;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_set_description(struct b_command *cmd, const char *description)
|
||||
{
|
||||
char *desc = b_strdup(description);
|
||||
if (!desc) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (cmd->b_description) {
|
||||
free(cmd->b_description);
|
||||
cmd->b_description = NULL;
|
||||
}
|
||||
|
||||
cmd->b_description = desc;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_set_callback(struct b_command *cmd, b_command_callback callback)
|
||||
{
|
||||
cmd->b_callback = callback;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_set_parent(b_command *cmd, unsigned int parent_id)
|
||||
{
|
||||
cmd->b_parent_id = parent_id;
|
||||
cmd->b_parent = NULL;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
struct b_command_option *b_command_add_option(struct b_command *cmd, int id)
|
||||
{
|
||||
struct b_command_option *opt = b_command_option_create();
|
||||
if (!opt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
opt->opt_id = id;
|
||||
b_queue_push_back(&cmd->b_opt, &opt->opt_entry);
|
||||
return opt;
|
||||
}
|
||||
|
||||
struct b_command_arg *b_command_add_arg(struct b_command *cmd, int id)
|
||||
{
|
||||
struct b_command_arg *arg = b_command_arg_create();
|
||||
if (!arg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
arg->arg_id = id;
|
||||
b_queue_push_back(&cmd->b_arg, &arg->arg_entry);
|
||||
return arg;
|
||||
}
|
||||
|
||||
struct b_command_usage *b_command_add_usage(struct b_command *cmd)
|
||||
{
|
||||
struct b_command_usage *usage = malloc(sizeof *usage);
|
||||
if (!usage) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(usage, 0x0, sizeof *usage);
|
||||
b_queue_push_back(&cmd->b_usage, &usage->u_entry);
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!u_opt) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
memset(u_opt, 0x0, sizeof *u_opt);
|
||||
|
||||
u_opt->opt = opt;
|
||||
|
||||
b_queue_push_back(&usage->u_opt, &u_opt->opt_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);
|
||||
if (!u_arg) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
memset(u_arg, 0x0, sizeof *u_arg);
|
||||
|
||||
u_arg->arg = arg;
|
||||
|
||||
b_queue_push_back(&usage->u_arg, &u_arg->arg_entry);
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static void prepend_command_name(struct b_command *cmd, b_string *out)
|
||||
{
|
||||
int nr_names = 0;
|
||||
cmd->b_name &&nr_names++;
|
||||
cmd->b_long_name &&nr_names++;
|
||||
cmd->b_short_name &&nr_names++;
|
||||
|
||||
if (nr_names > 1) {
|
||||
b_string_prepend_cstr(out, "}");
|
||||
int r = 0;
|
||||
|
||||
if (cmd->b_name) {
|
||||
b_string_prepend_cstrf(out, "%s", cmd->b_name);
|
||||
r++;
|
||||
}
|
||||
|
||||
if (r == 1) {
|
||||
b_string_prepend_cstr(out, "|");
|
||||
}
|
||||
|
||||
if (cmd->b_long_name) {
|
||||
b_string_prepend_cstrf(out, "--%s", cmd->b_long_name);
|
||||
r++;
|
||||
}
|
||||
|
||||
if (r == 2) {
|
||||
b_string_prepend_cstr(out, "|");
|
||||
}
|
||||
|
||||
if (cmd->b_short_name) {
|
||||
b_string_prepend_cstrf(out, "-%c", cmd->b_short_name);
|
||||
r++;
|
||||
}
|
||||
|
||||
b_string_prepend_cstr(out, "{");
|
||||
} else {
|
||||
b_string_prepend_cstr(out, cmd->b_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void get_qualified_command_name(struct b_command *cmd, b_string *out)
|
||||
{
|
||||
prepend_command_name(cmd, out);
|
||||
cmd = cmd->b_parent;
|
||||
|
||||
while (cmd) {
|
||||
b_string_prepend_cstr(out, " ");
|
||||
prepend_command_name(cmd, out);
|
||||
cmd = cmd->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_usage_string(
|
||||
struct b_command *cmd, struct b_command_usage *usage, b_string *out)
|
||||
{
|
||||
get_qualified_command_name(cmd, out);
|
||||
|
||||
b_queue_iterator it;
|
||||
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, " ");
|
||||
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, " ");
|
||||
z__b_get_arg_usage_string(arg->arg, false, out);
|
||||
}
|
||||
}
|
||||
|
||||
b_string *z__b_command_default_usage_string(
|
||||
struct b_command *cmd, struct b_command_option *with_opt)
|
||||
{
|
||||
b_string *str = b_string_create();
|
||||
get_qualified_command_name(cmd, str);
|
||||
|
||||
if (with_opt) {
|
||||
b_string_append_cstr(str, " ");
|
||||
z__b_get_option_usage_string(with_opt, CMD_STR_DIRECT_USAGE, str);
|
||||
} else if (!b_queue_empty(&cmd->b_opt)) {
|
||||
b_string_append_cstr(str, " [OPTIONS]");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
b_string_append_cstr(str, " ");
|
||||
z__b_get_arg_usage_string(arg, false, str);
|
||||
}
|
||||
|
||||
if (!b_queue_empty(&cmd->b_subcommands)) {
|
||||
b_string_append_cstr(str, " [COMMAND]");
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void get_command_string(struct b_command *cmd, b_string *out)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if (cmd->b_name) {
|
||||
b_string_append_cstrf(out, F_GREEN "%s" F_RESET, cmd->b_name);
|
||||
r++;
|
||||
}
|
||||
|
||||
if (r == 1) {
|
||||
b_string_append_cstr(out, ", ");
|
||||
}
|
||||
|
||||
if (cmd->b_short_name) {
|
||||
b_string_append_cstrf(out, F_GREEN "-%c" F_RESET, cmd->b_short_name);
|
||||
r++;
|
||||
}
|
||||
|
||||
if (r == 2) {
|
||||
b_string_append_cstr(out, ", ");
|
||||
}
|
||||
|
||||
if (cmd->b_long_name) {
|
||||
b_string_append_cstrf(out, F_GREEN "--%s" F_RESET, cmd->b_long_name);
|
||||
r++;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_command_description(struct b_command *cmd, b_string *out)
|
||||
{
|
||||
b_string_append_cstr(out, cmd->b_description);
|
||||
}
|
||||
|
||||
static void print_options_list(struct b_command *cmd)
|
||||
{
|
||||
b_fprintf(OUTPUT_STREAM, "\n" F_YELLOW "OPTIONS:" F_RESET "\n");
|
||||
|
||||
size_t desb_margin = 0;
|
||||
b_string *str = b_string_create();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
b_string_clear(str);
|
||||
z__b_get_option_usage_string(opt, CMD_STR_COLOUR, str);
|
||||
size_t len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4;
|
||||
|
||||
if (len > desb_margin) {
|
||||
desb_margin = len;
|
||||
}
|
||||
}
|
||||
|
||||
b_paragraph_format format = {};
|
||||
format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE;
|
||||
format.p_left_margin = desb_margin + 4;
|
||||
format.p_right_margin = 4;
|
||||
|
||||
size_t i = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
b_string_clear(str);
|
||||
z__b_get_option_usage_string(opt, CMD_STR_COLOUR, str);
|
||||
|
||||
fputs(" ", OUTPUT_STREAM);
|
||||
b_fputs(b_string_ptr(str), OUTPUT_STREAM);
|
||||
unsigned int len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4;
|
||||
while (len < format.p_left_margin) {
|
||||
fputc(' ', OUTPUT_STREAM);
|
||||
len++;
|
||||
}
|
||||
|
||||
b_string_clear(str);
|
||||
z__b_get_option_description(opt, str);
|
||||
|
||||
b_print_paragraph(b_string_ptr(str), OUTPUT_STREAM, &format);
|
||||
}
|
||||
|
||||
b_string_release(str);
|
||||
}
|
||||
|
||||
static void print_args_list(struct b_command *cmd)
|
||||
{
|
||||
b_fprintf(OUTPUT_STREAM, "\n" F_YELLOW "ARGS:" F_RESET "\n");
|
||||
|
||||
size_t desb_margin = 0;
|
||||
b_string *str = b_string_create();
|
||||
|
||||
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);
|
||||
|
||||
b_string_clear(str);
|
||||
z__b_get_arg_usage_string(arg, true, str);
|
||||
size_t len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4;
|
||||
|
||||
if (len > desb_margin) {
|
||||
desb_margin = len;
|
||||
}
|
||||
}
|
||||
|
||||
b_paragraph_format format = {};
|
||||
format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE;
|
||||
format.p_left_margin = desb_margin + 4;
|
||||
format.p_right_margin = 4;
|
||||
|
||||
size_t i = 0;
|
||||
b_queue_foreach (&it, &cmd->b_arg) {
|
||||
struct b_command_arg *arg
|
||||
= b_unbox(struct b_command_arg, it.entry, arg_entry);
|
||||
|
||||
b_string_clear(str);
|
||||
z__b_get_arg_usage_string(arg, true, str);
|
||||
|
||||
fputs(" ", OUTPUT_STREAM);
|
||||
b_fputs(b_string_ptr(str), OUTPUT_STREAM);
|
||||
unsigned int len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4;
|
||||
while (len < format.p_left_margin) {
|
||||
fputc(' ', OUTPUT_STREAM);
|
||||
len++;
|
||||
}
|
||||
|
||||
b_string_clear(str);
|
||||
z__b_get_arg_description(arg, str);
|
||||
|
||||
b_print_paragraph(b_string_ptr(str), OUTPUT_STREAM, &format);
|
||||
}
|
||||
|
||||
b_string_release(str);
|
||||
}
|
||||
|
||||
static void print_commands_list(struct b_command *cmd)
|
||||
{
|
||||
b_fprintf(OUTPUT_STREAM, "\n" F_YELLOW "COMMANDS:" F_RESET "\n");
|
||||
|
||||
size_t desb_margin = 0;
|
||||
b_string *str = b_string_create();
|
||||
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &cmd->b_subcommands) {
|
||||
struct b_command *sub
|
||||
= b_unbox(struct b_command, it.entry, b_entry);
|
||||
|
||||
b_string_clear(str);
|
||||
get_command_string(sub, str);
|
||||
size_t len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4;
|
||||
|
||||
if (len > desb_margin) {
|
||||
desb_margin = len;
|
||||
}
|
||||
}
|
||||
|
||||
b_paragraph_format format = {};
|
||||
format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE;
|
||||
format.p_left_margin = desb_margin + 4;
|
||||
format.p_right_margin = 4;
|
||||
|
||||
size_t i = 0;
|
||||
b_queue_foreach (&it, &cmd->b_subcommands) {
|
||||
struct b_command *sub
|
||||
= b_unbox(struct b_command, it.entry, b_entry);
|
||||
|
||||
b_string_clear(str);
|
||||
get_command_string(sub, str);
|
||||
|
||||
fputs(" ", OUTPUT_STREAM);
|
||||
b_fputs(b_string_ptr(str), OUTPUT_STREAM);
|
||||
unsigned int len = b_string_get_size(str, B_STRLEN_IGNORE_ESC) + 4;
|
||||
while (len < format.p_left_margin) {
|
||||
fputc(' ', OUTPUT_STREAM);
|
||||
len++;
|
||||
}
|
||||
|
||||
b_string_clear(str);
|
||||
get_command_description(sub, str);
|
||||
|
||||
b_print_paragraph(b_string_ptr(str), OUTPUT_STREAM, &format);
|
||||
}
|
||||
|
||||
b_string_release(str);
|
||||
}
|
||||
|
||||
struct b_command *b_command_get_subcommand_with_name(
|
||||
struct b_command *cmd, const char *name)
|
||||
{
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &cmd->b_subcommands) {
|
||||
struct b_command *subcmd
|
||||
= b_unbox(struct b_command, it.entry, b_entry);
|
||||
if (!subcmd || !subcmd->b_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(subcmd->b_name, name)) {
|
||||
return subcmd;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_command *b_command_get_subcommand_with_long_name(
|
||||
struct b_command *cmd, const char *long_name)
|
||||
{
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &cmd->b_subcommands) {
|
||||
struct b_command *subcmd
|
||||
= b_unbox(struct b_command, it.entry, b_entry);
|
||||
if (!subcmd || !subcmd->b_long_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(subcmd->b_name, long_name)) {
|
||||
return subcmd;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_command *b_command_get_subcommand_with_short_name(
|
||||
struct b_command *cmd, char short_name)
|
||||
{
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &cmd->b_subcommands) {
|
||||
struct b_command *subcmd
|
||||
= b_unbox(struct b_command, it.entry, b_entry);
|
||||
if (!subcmd || !subcmd->b_short_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subcmd->b_short_name == short_name) {
|
||||
return subcmd;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_command_option *b_command_get_option_with_long_name(
|
||||
struct b_command *cmd, const char *long_name)
|
||||
{
|
||||
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 || !opt->opt_long_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(opt->opt_long_name, long_name)) {
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct b_command_option *b_command_get_option_with_short_name(
|
||||
struct b_command *cmd, char short_name)
|
||||
{
|
||||
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 || !opt->opt_long_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt->opt_short_name == short_name) {
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void print_usage(struct b_command *cmd)
|
||||
{
|
||||
b_paragraph_format format = {};
|
||||
format.p_left_margin = format.p_right_margin = 4;
|
||||
|
||||
b_fprintf(OUTPUT_STREAM, F_YELLOW "USAGE:" F_RESET "\n");
|
||||
|
||||
if (b_queue_empty(&cmd->b_usage)) {
|
||||
b_string *usage = z__b_command_default_usage_string(cmd, NULL);
|
||||
b_print_paragraph(b_string_ptr(usage), OUTPUT_STREAM, &format);
|
||||
b_string_release(usage);
|
||||
return;
|
||||
}
|
||||
|
||||
b_string *str = b_string_create();
|
||||
b_queue_iterator it;
|
||||
b_queue_foreach (&it, &cmd->b_usage) {
|
||||
struct b_command_usage *usage
|
||||
= b_unbox(struct b_command_usage, it.entry, u_entry);
|
||||
if (!usage) {
|
||||
break;
|
||||
}
|
||||
|
||||
b_string_clear(str);
|
||||
get_usage_string(cmd, usage, str);
|
||||
b_print_paragraph(b_string_ptr(str), OUTPUT_STREAM, &format);
|
||||
}
|
||||
|
||||
b_string_release(str);
|
||||
}
|
||||
|
||||
static void print_help(struct b_command *cmd)
|
||||
{
|
||||
b_paragraph_format format = {};
|
||||
|
||||
if (!cmd->b_parent) {
|
||||
b_fprintf(OUTPUT_STREAM, F_GREEN "%s" F_RESET "\n", cmd->b_name);
|
||||
}
|
||||
|
||||
if (cmd->b_description) {
|
||||
b_print_paragraph(cmd->b_description, OUTPUT_STREAM, &format);
|
||||
}
|
||||
|
||||
b_fprintf(OUTPUT_STREAM, "\n");
|
||||
|
||||
print_usage(cmd);
|
||||
|
||||
if (!b_queue_empty(&cmd->b_opt)) {
|
||||
print_options_list(cmd);
|
||||
}
|
||||
|
||||
if (!b_queue_empty(&cmd->b_arg)) {
|
||||
print_args_list(cmd);
|
||||
}
|
||||
|
||||
if (!b_queue_empty(&cmd->b_subcommands)) {
|
||||
print_commands_list(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static int execute_command(struct b_command *cmd, struct b_arglist *args)
|
||||
{
|
||||
if (!cmd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cmd->b_flags & B_COMMAND_SHOW_HELP_BY_DEFAULT
|
||||
&& b_arglist_get_count(args, B_COMMAND_INVALID_ID, B_COMMAND_INVALID_ID)
|
||||
== 0) {
|
||||
print_help(cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (b_arglist_get_count(args, B_COMMAND_OPTION_HELP, B_COMMAND_INVALID_ID)
|
||||
> 0) {
|
||||
print_help(cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd->b_callback) {
|
||||
return cmd->b_callback(cmd, args, NULL);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static b_status add_subcommand(struct b_command *parent, struct b_command *child)
|
||||
{
|
||||
b_queue_push_back(&parent->b_subcommands, &child->b_entry);
|
||||
child->b_parent = parent;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
static int resolve_command_parents(struct b_btree *commands)
|
||||
{
|
||||
struct b_btree_iterator it;
|
||||
b_btree_foreach (&it, commands) {
|
||||
struct b_command *cmd = b_unbox(struct b_command, it.node, b_node);
|
||||
|
||||
if (cmd->b_parent_id == B_COMMAND_INVALID_ID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cmd->b_parent = get_command(commands, cmd->b_parent_id);
|
||||
if (!cmd->b_parent) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
add_subcommand(cmd->b_parent, cmd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int b_command_dispatch(unsigned int cmd_id, int argc, const char **argv)
|
||||
{
|
||||
if (resolve_command_parents(&command_list) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct b_command *cmd = get_command(&command_list, cmd_id);
|
||||
if (!cmd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct b_arglist *args = b_arglist_create();
|
||||
b_status status = b_arglist_parse(args, &cmd, argc, argv);
|
||||
if (B_ERR(status)) {
|
||||
b_arglist_destroy(args);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = execute_command(cmd, args);
|
||||
|
||||
b_arglist_destroy(args);
|
||||
// print_help(cmd);
|
||||
return ret;
|
||||
}
|
||||
131
cmd/command.h
Normal file
131
cmd/command.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#ifndef _B_COMMAND_H_
|
||||
#define _B_COMMAND_H_
|
||||
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/core/btree.h>
|
||||
#include <blue/core/queue.h>
|
||||
|
||||
#define F_RED "\033[91m"
|
||||
#define F_GREEN "\033[92m"
|
||||
#define F_YELLOW "\033[93m"
|
||||
|
||||
#define F_RED_BOLD "\033[1;91m"
|
||||
#define F_GREEN_BOLD "\033[1;92m"
|
||||
#define F_YELLOW_BOLD "\033[1;93m"
|
||||
#define F_RESET "\033[0m"
|
||||
|
||||
enum cmd_string_flags {
|
||||
CMD_STR_COLOUR = 0x01u,
|
||||
CMD_STR_DIRECT_USAGE = 0x02u,
|
||||
};
|
||||
|
||||
struct b_string;
|
||||
|
||||
struct b_command {
|
||||
unsigned int b_id;
|
||||
unsigned int b_parent_id;
|
||||
struct b_command *b_parent;
|
||||
enum b_command_flags b_flags;
|
||||
|
||||
char *b_name;
|
||||
char *b_long_name;
|
||||
char b_short_name;
|
||||
char *b_description;
|
||||
struct b_queue b_opt;
|
||||
struct b_queue b_arg;
|
||||
struct b_queue b_subcommands;
|
||||
struct b_queue b_usage;
|
||||
|
||||
b_command_callback b_callback;
|
||||
struct b_queue_entry b_entry;
|
||||
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_arg {
|
||||
struct b_queue_entry arg_entry;
|
||||
struct b_command_arg *arg;
|
||||
};
|
||||
|
||||
struct b_command_usage {
|
||||
b_command_usage_flags u_flags;
|
||||
struct b_queue_entry u_entry;
|
||||
|
||||
struct b_queue u_opt;
|
||||
struct b_queue u_arg;
|
||||
};
|
||||
|
||||
struct b_command_option {
|
||||
unsigned int opt_id;
|
||||
|
||||
char *opt_long_name;
|
||||
char opt_short_name;
|
||||
char *opt_description;
|
||||
|
||||
// b_command_arg_value_count arg_nr_values;
|
||||
// char **arg_allowed_values;
|
||||
|
||||
struct b_queue opt_args;
|
||||
struct b_queue_entry opt_entry;
|
||||
};
|
||||
|
||||
struct b_arglist_option {
|
||||
unsigned int opt_id;
|
||||
struct b_btree opt_values;
|
||||
struct b_btree_node opt_node;
|
||||
};
|
||||
|
||||
struct b_arglist {
|
||||
struct b_btree list_options;
|
||||
};
|
||||
|
||||
struct b_command_arg {
|
||||
unsigned int arg_id;
|
||||
char *arg_name;
|
||||
char *arg_description;
|
||||
|
||||
b_command_arg_value_count arg_nr_values;
|
||||
char **arg_allowed_values;
|
||||
|
||||
struct b_queue_entry arg_entry;
|
||||
};
|
||||
|
||||
extern struct b_command *b_command_get_subcommand_with_name(
|
||||
struct b_command *cmd, const char *name);
|
||||
extern struct b_command *b_command_get_subcommand_with_long_name(
|
||||
struct b_command *cmd, const char *long_name);
|
||||
extern struct b_command *b_command_get_subcommand_with_short_name(
|
||||
struct b_command *cmd, char short_name);
|
||||
|
||||
extern struct b_command_option *b_command_get_option_with_long_name(
|
||||
struct b_command *cmd, const char *long_name);
|
||||
extern struct b_command_option *b_command_get_option_with_short_name(
|
||||
struct b_command *cmd, char short_name);
|
||||
|
||||
extern struct b_command_option *b_command_option_create(void);
|
||||
extern struct b_command_arg *b_command_arg_create(void);
|
||||
extern struct b_arglist *b_arglist_create(void);
|
||||
extern b_status b_arglist_parse(
|
||||
struct b_arglist *args, struct b_command **cmd, int argc,
|
||||
const char **argv);
|
||||
extern void b_arglist_destroy(struct b_arglist *args);
|
||||
|
||||
extern struct b_string *z__b_command_default_usage_string(
|
||||
struct b_command *cmd, struct b_command_option *with_opt);
|
||||
|
||||
extern void z__b_get_arg_usage_string(
|
||||
struct b_command_arg *arg, bool colour, struct b_string *out);
|
||||
extern void z__b_get_arg_description(
|
||||
struct b_command_arg *arg, struct b_string *out);
|
||||
|
||||
extern void z__b_get_option_usage_string(
|
||||
struct b_command_option *opt, enum cmd_string_flags flags,
|
||||
struct b_string *out);
|
||||
extern void z__b_get_option_description(
|
||||
struct b_command_option *opt, struct b_string *out);
|
||||
|
||||
#endif
|
||||
214
cmd/include/blue/cmd.h
Normal file
214
cmd/include/blue/cmd.h
Normal file
@@ -0,0 +1,214 @@
|
||||
#ifndef BLUELIB_COMMAND_H_
|
||||
#define BLUELIB_COMMAND_H_
|
||||
|
||||
#include <blue/core/btree.h>
|
||||
#include <blue/core/init.h>
|
||||
#include <blue/core/iterator.h>
|
||||
#include <blue/core/queue.h>
|
||||
#include <blue/object/array.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define b_arglist_foreach(it, q) \
|
||||
for (int z__b_unique_name() = b_arglist_iterator_begin( \
|
||||
q, B_COMMAND_INVALID_ID, B_COMMAND_INVALID_ID, (it)); \
|
||||
b_arglist_iterator_is_valid(it); b_arglist_iterator_next(it))
|
||||
|
||||
#define b_arglist_foreach_filtered(it, q, opt_id, arg_id) \
|
||||
for (int z__b_unique_name() \
|
||||
= b_arglist_iterator_begin(q, opt_id, arg_id, (it)); \
|
||||
b_arglist_iterator_is_valid(it); b_arglist_iterator_next(it))
|
||||
|
||||
#define B_COMMAND(id, parent_id) \
|
||||
static b_command *command_##id = NULL; \
|
||||
static void __init_##id( \
|
||||
b_command *, b_command_option *, b_command_arg *, \
|
||||
b_command_usage *); \
|
||||
B_INIT(init_##id) \
|
||||
{ \
|
||||
command_##id = b_command_create(id); \
|
||||
if ((parent_id) != B_COMMAND_INVALID_ID) { \
|
||||
b_command_set_parent(command_##id, parent_id); \
|
||||
} \
|
||||
__init_##id(command_##id, NULL, NULL, NULL); \
|
||||
b_command_register(command_##id); \
|
||||
} \
|
||||
static void __init_##id( \
|
||||
b_command *this_cmd, b_command_option *this_opt, \
|
||||
b_command_arg *this_arg, b_command_usage *this_usage)
|
||||
|
||||
#define B_COMMAND_NAME(name) b_command_set_name(this_cmd, (name))
|
||||
#define B_COMMAND_LONG_NAME(name) b_command_set_long_name(this_cmd, (name))
|
||||
#define B_COMMAND_SHORT_NAME(name) b_command_set_short_name(this_cmd, (name))
|
||||
#define B_COMMAND_DESC(desc) b_command_set_description(this_cmd, (desc))
|
||||
#define B_COMMAND_FLAGS(flags) b_command_set_flags(this_cmd, (flags))
|
||||
#define B_COMMAND_FUNCTION(fn) b_command_set_callback(this_cmd, (fn))
|
||||
|
||||
#define B_COMMAND_OPTION(id) \
|
||||
b_command_option *opt_##id = b_command_add_option(this_cmd, (id)); \
|
||||
this_opt = opt_##id; \
|
||||
if (this_opt)
|
||||
|
||||
#define B_COMMAND_HELP_OPTION() \
|
||||
do { \
|
||||
b_command_option *opt \
|
||||
= b_command_add_option(this_cmd, B_COMMAND_OPTION_HELP); \
|
||||
b_command_option_set_description(opt, "Show this help message"); \
|
||||
b_command_option_set_short_name(opt, 'h'); \
|
||||
b_command_option_set_long_name(opt, "help"); \
|
||||
} while (0)
|
||||
|
||||
#define B_OPTION_LONG_NAME(name) \
|
||||
b_command_option_set_long_name(this_opt, (name))
|
||||
|
||||
#define B_OPTION_SHORT_NAME(name) \
|
||||
b_command_option_set_short_name(this_opt, (name))
|
||||
|
||||
#define B_OPTION_DESC(desc) b_command_option_set_description(this_opt, (desc))
|
||||
|
||||
#define B_OPTION_ARG(id) \
|
||||
b_command_arg *arg_##id = b_command_option_add_arg(this_opt, (id)); \
|
||||
this_arg = arg_##id; \
|
||||
if (this_arg)
|
||||
|
||||
#define B_COMMAND_ARG(id) \
|
||||
b_command_arg *arg_##id = b_command_add_arg(this_cmd, (id)); \
|
||||
this_arg = arg_##id; \
|
||||
if (this_arg)
|
||||
|
||||
#define B_ARG_NAME(name) b_command_arg_set_name(this_arg, (name))
|
||||
|
||||
#define B_ARG_DESC(desc) b_command_arg_set_description(this_arg, (desc))
|
||||
|
||||
#define B_ARG_NR_VALUES(nr_values) \
|
||||
b_command_arg_set_nr_values(this_arg, (nr_values))
|
||||
|
||||
#define B_ARG_ALLOWED_VALUES(...) \
|
||||
static const char *allowed_values[] = { \
|
||||
__VA_ARGS__, \
|
||||
NULL, \
|
||||
}; \
|
||||
b_command_arg_set_allowed_values(this_arg, allowed_values)
|
||||
|
||||
#define B_COMMAND_USAGE() \
|
||||
b_command_usage *usage_##__LINE__ = b_command_add_usage(this_cmd); \
|
||||
this_usage = usage_##__LINE__; \
|
||||
if (this_usage)
|
||||
|
||||
#define B_COMMAND_USAGE_OPT(opt_id) \
|
||||
b_command_usage_add_option(this_usage, opt_##opt_id)
|
||||
|
||||
#define B_COMMAND_USAGE_ARG(opt_id) \
|
||||
b_command_usage_add_arg(this_usage, arg_##opt_id)
|
||||
|
||||
#define B_COMMAND_OPTION_HELP ((unsigned int)0xF0000001)
|
||||
#define B_COMMAND_INVALID_ID ((unsigned int)0xFFFFFFFF)
|
||||
|
||||
typedef enum b_command_arg_value_count {
|
||||
B_ARG_0_OR_1_VALUES = -1,
|
||||
B_ARG_0_OR_MORE_VALUES = -2,
|
||||
B_ARG_1_OR_MORE_VALUES = -3,
|
||||
} b_command_arg_value_count;
|
||||
|
||||
typedef enum b_command_arg_type {
|
||||
B_COMMAND_ARG_NONE = 0,
|
||||
B_COMMAND_ARG_STRING,
|
||||
B_COMMAND_ARG_SIGNED_INT,
|
||||
B_COMMAND_ARG_UNSIGNED_INT,
|
||||
|
||||
B_COMMAND_ARG_INT = B_COMMAND_ARG_SIGNED_INT,
|
||||
} b_command_arg_type;
|
||||
|
||||
typedef enum b_command_flags {
|
||||
B_COMMAND_SHOW_HELP_BY_DEFAULT = 0x01u,
|
||||
} b_command_flags;
|
||||
|
||||
typedef enum b_command_usage_flags {
|
||||
B_COMMAND_USAGE_SHOW_OPTIONS = 0x01u,
|
||||
} b_command_usage_flags;
|
||||
|
||||
typedef struct b_arglist_value {
|
||||
unsigned int val_id;
|
||||
b_command_arg_type val_type;
|
||||
struct b_btree_node val_node;
|
||||
|
||||
union {
|
||||
char *val_str;
|
||||
long long val_int;
|
||||
unsigned long long val_uint;
|
||||
};
|
||||
} b_arglist_value;
|
||||
|
||||
typedef struct b_arglist_iterator {
|
||||
b_iterator _base;
|
||||
size_t i;
|
||||
|
||||
unsigned int opt_id;
|
||||
struct b_arglist_value *value;
|
||||
|
||||
b_btree_iterator _opt_it, _arg_it;
|
||||
unsigned int _opt_filter, _arg_filter;
|
||||
} b_arglist_iterator;
|
||||
|
||||
typedef struct b_command b_command;
|
||||
typedef struct b_command_option b_command_option;
|
||||
typedef struct b_command_arg b_command_arg;
|
||||
typedef struct b_command_usage b_command_usage;
|
||||
typedef struct b_arglist b_arglist;
|
||||
|
||||
typedef int (*b_command_callback)(
|
||||
const b_command *, const b_arglist *, const b_array *);
|
||||
|
||||
extern b_command *b_command_create(unsigned int id);
|
||||
extern void b_command_destroy(b_command *cmd);
|
||||
extern b_status b_command_register(b_command *cmd);
|
||||
extern int b_command_dispatch(unsigned int cmd_id, int argc, const char **argv);
|
||||
|
||||
extern b_status b_command_set_name(b_command *cmd, const char *name);
|
||||
extern b_status b_command_set_long_name(b_command *cmd, const char *name);
|
||||
extern b_status b_command_set_short_name(b_command *cmd, char name);
|
||||
extern b_status b_command_set_flags(b_command *cmd, b_command_flags flags);
|
||||
extern b_status b_command_set_description(b_command *cmd, const char *description);
|
||||
extern b_status b_command_set_callback(b_command *cmd, b_command_callback callback);
|
||||
extern b_status b_command_set_parent(b_command *cmd, unsigned int parent_id);
|
||||
extern b_command_option *b_command_add_option(b_command *cmd, int id);
|
||||
extern b_command_arg *b_command_add_arg(b_command *cmd, int id);
|
||||
extern b_command_usage *b_command_add_usage(b_command *cmd);
|
||||
|
||||
extern b_status b_command_option_set_long_name(
|
||||
b_command_option *opt, const char *name);
|
||||
extern b_status b_command_option_set_short_name(b_command_option *opt, char name);
|
||||
extern b_status b_command_option_set_description(
|
||||
b_command_option *opt, const char *description);
|
||||
extern b_command_arg *b_command_option_add_arg(b_command_option *opt, int id);
|
||||
|
||||
extern b_status b_command_arg_set_name(b_command_arg *arg, const char *name);
|
||||
extern b_status b_command_arg_set_description(
|
||||
b_command_arg *arg, const char *description);
|
||||
extern b_status b_command_arg_set_nr_values(
|
||||
b_command_arg *arg, b_command_arg_value_count nr_values);
|
||||
extern b_status b_command_arg_set_allowed_values(
|
||||
b_command_arg *arg, const char **allowed_values);
|
||||
|
||||
extern b_status b_command_usage_add_option(
|
||||
b_command_usage *usage, b_command_option *opt);
|
||||
extern b_status b_command_usage_add_arg(b_command_usage *usage, b_command_arg *opt);
|
||||
|
||||
extern b_status b_arglist_get_string(
|
||||
const b_arglist *args, unsigned int opt_id, unsigned int arg_id,
|
||||
unsigned int index, const char **out);
|
||||
extern b_status b_arglist_get_int(
|
||||
const b_arglist *args, unsigned int opt_id, unsigned int arg_id,
|
||||
unsigned int index, long long *out);
|
||||
extern b_status b_arglist_get_uint(
|
||||
const b_arglist *args, unsigned int opt_id, unsigned int arg_id,
|
||||
unsigned int index, unsigned long long *out);
|
||||
extern size_t b_arglist_get_count(
|
||||
const b_arglist *args, unsigned int opt_id, unsigned int arg_id);
|
||||
|
||||
extern int b_arglist_iterator_begin(
|
||||
const b_arglist *args, unsigned int opt_filter, unsigned int arg_filter,
|
||||
b_arglist_iterator *it);
|
||||
extern bool b_arglist_iterator_next(b_arglist_iterator *it);
|
||||
extern bool b_arglist_iterator_is_valid(const b_arglist_iterator *it);
|
||||
|
||||
#endif
|
||||
206
cmd/option.c
Normal file
206
cmd/option.c
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "command.h"
|
||||
|
||||
#include <blue/cmd.h>
|
||||
#include <blue/object/string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct b_command_option *b_command_option_create(void)
|
||||
{
|
||||
struct b_command_option *out = malloc(sizeof *out);
|
||||
if (!out) {
|
||||
return out;
|
||||
}
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
return out;
|
||||
}
|
||||
|
||||
b_status b_command_option_set_long_name(
|
||||
struct b_command_option *opt, const char *name)
|
||||
{
|
||||
char *n = b_strdup(name);
|
||||
if (!n) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (opt->opt_long_name) {
|
||||
free(opt->opt_long_name);
|
||||
opt->opt_long_name = NULL;
|
||||
}
|
||||
|
||||
opt->opt_long_name = n;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_option_set_short_name(struct b_command_option *opt, char name)
|
||||
{
|
||||
opt->opt_short_name = name;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_command_option_set_description(
|
||||
struct b_command_option *opt, const char *description)
|
||||
{
|
||||
char *desc = b_strdup(description);
|
||||
if (!desc) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (opt->opt_description) {
|
||||
free(opt->opt_description);
|
||||
opt->opt_description = NULL;
|
||||
}
|
||||
|
||||
opt->opt_description = desc;
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
struct b_command_arg *b_command_option_add_arg(struct b_command_option *opt, int id)
|
||||
{
|
||||
struct b_command_arg *arg = malloc(sizeof *arg);
|
||||
if (!arg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(arg, 0x0, sizeof *arg);
|
||||
|
||||
arg->arg_id = id;
|
||||
|
||||
b_queue_push_back(&opt->opt_args, &arg->arg_entry);
|
||||
return arg;
|
||||
}
|
||||
|
||||
void z__b_get_option_description(struct b_command_option *opt, b_string *out)
|
||||
{
|
||||
if (opt->opt_description) {
|
||||
b_string_append_cstr(out, opt->opt_description);
|
||||
}
|
||||
|
||||
size_t nr_args = b_queue_length(&opt->opt_args);
|
||||
bool close_bracket = false;
|
||||
|
||||
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 || !arg->arg_allowed_values) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it.i > 0) {
|
||||
b_string_append_cstr(out, "; ");
|
||||
} else {
|
||||
b_string_append_cstr(out, " [");
|
||||
close_bracket = true;
|
||||
}
|
||||
|
||||
if (nr_args > 1) {
|
||||
b_string_append_cstrf(
|
||||
out, "values for `%s`:", arg->arg_name);
|
||||
} else {
|
||||
b_string_append_cstr(out, "values:");
|
||||
}
|
||||
|
||||
for (size_t i = 0; arg->arg_allowed_values[i]; i++) {
|
||||
if (i > 0) {
|
||||
b_string_append_cstr(out, ",");
|
||||
}
|
||||
|
||||
b_string_append_cstrf(
|
||||
out, " " F_GREEN "%s" F_RESET,
|
||||
arg->arg_allowed_values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (close_bracket) {
|
||||
b_string_append_cstr(out, "]");
|
||||
}
|
||||
}
|
||||
|
||||
void z__b_get_option_usage_string(
|
||||
struct b_command_option *opt, enum cmd_string_flags flags,
|
||||
struct b_string *out)
|
||||
{
|
||||
if (flags & CMD_STR_DIRECT_USAGE) {
|
||||
b_string_append_cstr(out, "{");
|
||||
}
|
||||
|
||||
if (opt->opt_short_name) {
|
||||
b_string_append_cstrf(
|
||||
out,
|
||||
(flags & CMD_STR_COLOUR) ? F_GREEN "-%c" F_RESET : "-%c",
|
||||
opt->opt_short_name);
|
||||
}
|
||||
|
||||
if (opt->opt_short_name && opt->opt_long_name) {
|
||||
b_string_append_cstr(
|
||||
out, (flags & CMD_STR_DIRECT_USAGE) ? "|" : ", ");
|
||||
}
|
||||
|
||||
if (opt->opt_long_name) {
|
||||
b_string_append_cstrf(
|
||||
out,
|
||||
(flags & CMD_STR_COLOUR) ? F_GREEN "--%s" F_RESET : "--%s",
|
||||
opt->opt_long_name);
|
||||
}
|
||||
|
||||
if (flags & CMD_STR_DIRECT_USAGE) {
|
||||
b_string_append_cstr(out, "}");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool optional = false, multi = false;
|
||||
switch (arg->arg_nr_values) {
|
||||
case B_ARG_0_OR_1_VALUES:
|
||||
optional = true;
|
||||
multi = false;
|
||||
break;
|
||||
case B_ARG_0_OR_MORE_VALUES:
|
||||
optional = true;
|
||||
multi = true;
|
||||
break;
|
||||
case B_ARG_1_OR_MORE_VALUES:
|
||||
optional = false;
|
||||
multi = true;
|
||||
break;
|
||||
default:
|
||||
optional = false;
|
||||
multi = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (optional) {
|
||||
b_string_append_cstrf(
|
||||
out,
|
||||
(flags & CMD_STR_COLOUR) ? " " F_GREEN "[%s]"
|
||||
: " [%s]",
|
||||
arg->arg_name);
|
||||
} else {
|
||||
b_string_append_cstrf(
|
||||
out,
|
||||
(flags & CMD_STR_COLOUR) ? " " F_GREEN "<%s>"
|
||||
: " <%s>",
|
||||
arg->arg_name);
|
||||
}
|
||||
|
||||
for (int i = 1; i < arg->arg_nr_values; i++) {
|
||||
b_string_append_cstrf(out, " <%s>", arg->arg_name);
|
||||
}
|
||||
|
||||
if (multi) {
|
||||
b_string_append_cstr(out, "...");
|
||||
}
|
||||
|
||||
if (flags & CMD_STR_COLOUR) {
|
||||
b_string_append_cstr(out, F_RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user