Files
bluelib/cmd/command.c

904 lines
19 KiB
C
Raw Normal View History

2024-10-24 21:32:28 +01:00
#include "command.h"
#include <blue/cmd.h>
2024-10-27 19:43:05 +00:00
#include <blue/core/btree.h>
2024-10-24 21:32:28 +01:00
#include <blue/object/string.h>
#include <blue/term.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,
};
2024-10-27 19:43:05 +00:00
static void command_list_cleanup(void)
{
struct b_btree_iterator it = {0};
b_btree_iterator_begin(&command_list, &it);
while (b_btree_iterator_is_valid(&it)) {
struct b_command *cmd = b_unbox(struct b_command, it.node, b_node);
if (!cmd) {
break;
}
b_btree_iterator_erase(&it);
b_command_destroy(cmd);
}
}
2024-10-24 21:32:28 +01:00
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;
}
2024-10-27 19:43:05 +00:00
static void command_usage_destroy(struct b_command_usage *usage)
{
struct b_queue_iterator it = {0};
b_queue_iterator_begin(&usage->u_arg, &it);
while (b_queue_iterator_is_valid(&it)) {
struct b_command_usage_arg *arg = b_unbox(
struct b_command_usage_arg, it.entry, arg_entry);
if (!arg) {
continue;
}
b_queue_iterator_erase(&it);
free(arg);
}
b_queue_iterator_begin(&usage->u_opt, &it);
while (b_queue_iterator_is_valid(&it)) {
struct b_command_usage_opt *opt = b_unbox(
struct b_command_usage_opt, it.entry, opt_entry);
if (!opt) {
continue;
}
b_queue_iterator_erase(&it);
free(opt);
}
free(usage);
}
2024-10-24 21:32:28 +01:00
void b_command_destroy(struct b_command *cmd)
{
2024-10-27 19:43:05 +00:00
struct b_queue_iterator it = {0};
if (cmd->b_name) {
free(cmd->b_name);
}
if (cmd->b_long_name) {
free(cmd->b_long_name);
}
if (cmd->b_description) {
free(cmd->b_description);
}
b_queue_iterator_begin(&cmd->b_opt, &it);
while (b_queue_iterator_is_valid(&it)) {
struct b_command_option *opt
= b_unbox(struct b_command_option, it.entry, opt_entry);
if (!opt) {
break;
}
b_queue_iterator_erase(&it);
b_command_option_destroy(opt);
}
b_queue_iterator_begin(&cmd->b_arg, &it);
while (b_queue_iterator_is_valid(&it)) {
struct b_command_arg *arg
= b_unbox(struct b_command_arg, it.entry, arg_entry);
if (!arg) {
break;
}
b_queue_iterator_erase(&it);
b_command_arg_destroy(arg);
}
b_queue_iterator_begin(&cmd->b_arg, &it);
while (b_queue_iterator_is_valid(&it)) {
struct b_command *subcmd
= b_unbox(struct b_command, it.entry, b_entry);
if (!subcmd) {
break;
}
b_queue_iterator_erase(&it);
b_command_destroy(subcmd);
}
b_queue_iterator_begin(&cmd->b_usage, &it);
while (b_queue_iterator_is_valid(&it)) {
struct b_command_usage *usage
= b_unbox(struct b_command_usage, it.entry, u_entry);
if (!usage) {
break;
}
b_queue_iterator_erase(&it);
command_usage_destroy(usage);
}
free(cmd);
2024-10-24 21:32:28 +01:00
}
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;
}
2024-10-27 19:43:05 +00:00
if (b_btree_empty(&command_list)) {
atexit(command_list_cleanup);
}
2024-10-24 21:32:28 +01:00
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 (cmd->b_short_name) {
if (r > 0) {
b_string_append_cstr(out, ", ");
}
2024-10-24 21:32:28 +01:00
b_string_append_cstrf(out, F_GREEN "-%c" F_RESET, cmd->b_short_name);
r++;
}
if (cmd->b_long_name) {
if (r > 0) {
b_string_append_cstr(out, ", ");
}
2024-10-24 21:32:28 +01:00
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);
}
2024-10-27 19:43:05 +00:00
/* criteria for printing the option description on a separate line:
* 1) the length of the usage string exceeds the newline threshold; and
* 2) the length of the description string exceeds the remaining line length
* (once the usage string has been printed.
* or,
* 3) the length of the description string is more than three terminal lines.
*/
#define description_on_separate_line(opt_len, desc_len) \
((opt_len >= newline_threshold \
&& desc_len >= (term_width - newline_threshold)) \
|| desc_len > (3 * term_width - newline_threshold))
2024-10-24 21:32:28 +01:00
static void print_options_list(struct b_command *cmd)
{
unsigned int term_width = 0;
b_term_get_dimensions(OUTPUT_STREAM, &term_width, NULL);
unsigned int newline_threshold = 1000000;
if (term_width) {
2024-10-27 19:43:05 +00:00
newline_threshold = term_width - 12 - 35;
}
2024-10-24 21:32:28 +01:00
b_fprintf(OUTPUT_STREAM, "\n" F_YELLOW "OPTIONS:" F_RESET "\n");
size_t desb_margin = 0;
2024-10-27 19:43:05 +00:00
b_string *opt_str = b_string_create();
b_string *desc_str = b_string_create();
2024-10-24 21:32:28 +01:00
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;
}
2024-10-27 19:43:05 +00:00
b_string_clear(opt_str);
b_string_clear(desc_str);
z__b_get_option_usage_string(opt, CMD_STR_COLOUR, opt_str);
z__b_get_option_description(opt, desc_str);
2024-10-24 21:32:28 +01:00
2024-10-27 19:43:05 +00:00
size_t opt_len
= b_string_get_size(opt_str, B_STRLEN_IGNORE_ESC) + 4;
size_t desc_len = b_string_get_size(desc_str, B_STRLEN_IGNORE_ESC);
if (description_on_separate_line(opt_len, desc_len)) {
continue;
}
2024-10-27 19:43:05 +00:00
if (opt_len > desb_margin) {
desb_margin = opt_len;
2024-10-24 21:32:28 +01:00
}
}
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;
}
2024-10-27 19:43:05 +00:00
b_string_clear(opt_str);
b_string_clear(desc_str);
z__b_get_option_usage_string(opt, CMD_STR_COLOUR, opt_str);
z__b_get_option_description(opt, desc_str);
size_t opt_len
= b_string_get_size(opt_str, B_STRLEN_IGNORE_ESC) + 4;
size_t desc_len = b_string_get_size(desc_str, B_STRLEN_IGNORE_ESC);
2024-10-27 19:43:05 +00:00
bool new_paragraph
= description_on_separate_line(opt_len, desc_len);
if (new_paragraph) {
fputc('\n', OUTPUT_STREAM);
}
2024-10-24 21:32:28 +01:00
fputs(" ", OUTPUT_STREAM);
2024-10-27 19:43:05 +00:00
b_fputs(b_string_ptr(opt_str), OUTPUT_STREAM);
2024-10-27 19:43:05 +00:00
if (new_paragraph) {
format.p_flags = 0;
format.p_left_margin = 8;
format.p_right_margin = 4;
fputc('\n', OUTPUT_STREAM);
} else {
format.p_flags = B_PARAGRAPH_DONT_INDENT_FIRST_LINE;
format.p_left_margin = desb_margin + 4;
format.p_right_margin = 4;
}
2024-10-27 19:43:05 +00:00
unsigned int len = opt_len;
2024-10-24 21:32:28 +01:00
while (len < format.p_left_margin) {
fputc(' ', OUTPUT_STREAM);
len++;
}
2024-10-27 19:43:05 +00:00
b_print_paragraph(b_string_ptr(desc_str), OUTPUT_STREAM, &format);
2024-10-27 19:43:05 +00:00
if (new_paragraph) {
fputc('\n', OUTPUT_STREAM);
}
2024-10-24 21:32:28 +01:00
}
2024-10-27 19:43:05 +00:00
b_string_release(opt_str);
b_string_release(desc_str);
2024-10-24 21:32:28 +01:00
}
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;
}