Files
fx/cmd/command.c

1082 lines
23 KiB
C

#include "command.h"
#include <fx/cmd.h>
#include <fx/core/bst.h>
#include <fx/ds/string.h>
#include <fx/term/print.h>
#include <fx/term/tty.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OUTPUT_STREAM fx_stdtty_err
static struct fx_bst command_list = {0};
FX_BST_DEFINE_SIMPLE_GET(struct fx_command, unsigned int, c_node, c_id, get_command)
FX_BST_DEFINE_SIMPLE_INSERT(struct fx_command, c_node, c_id, put_command)
enum item_type {
ITEM_OPTION,
ITEM_ARG,
ITEM_SUBCOMMAND,
};
static void command_list_cleanup(void)
{
struct fx_bst_node *node = fx_bst_first(&command_list);
while (node) {
struct fx_command *cmd = fx_unbox(struct fx_command, node, c_node);
if (!cmd) {
break;
}
struct fx_bst_node *next = fx_bst_next(node);
fx_bst_delete(&command_list, node);
fx_command_destroy(cmd);
node = next;
}
}
struct fx_command *fx_command_create(unsigned int id)
{
struct fx_command *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->c_id = id;
out->c_parent_id = FX_COMMAND_INVALID_ID;
return out;
}
static void command_usage_destroy(struct fx_command_usage *usage)
{
struct fx_queue_entry *entry = fx_queue_first(&usage->u_parts);
while (entry) {
struct fx_command_usage_entry *arg
= fx_unbox(struct fx_command_usage_entry, entry, e_entry);
if (!arg) {
continue;
}
struct fx_queue_entry *next = fx_queue_next(entry);
fx_queue_delete(&usage->u_parts, entry);
free(arg);
entry = next;
}
free(usage);
}
void fx_command_destroy(struct fx_command *cmd)
{
if (cmd->c_name) {
free(cmd->c_name);
}
if (cmd->c_long_name) {
free(cmd->c_long_name);
}
if (cmd->c_description) {
free(cmd->c_description);
}
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt);
struct fx_queue_entry *next = NULL;
while (entry) {
struct fx_command_option *opt
= fx_unbox(struct fx_command_option, entry, opt_entry);
if (!opt) {
break;
}
next = fx_queue_next(entry);
fx_queue_delete(&cmd->c_opt, entry);
fx_command_option_destroy(opt);
entry = next;
}
entry = fx_queue_first(&cmd->c_arg);
while (entry) {
struct fx_command_arg *arg
= fx_unbox(struct fx_command_arg, entry, arg_entry);
if (!arg) {
break;
}
next = fx_queue_next(entry);
fx_queue_delete(&cmd->c_arg, entry);
fx_command_arg_destroy(arg);
entry = next;
}
#if 0
entry = fx_queue_first(&cmd->c_subcommands);
while (entry) {
struct fx_command *subcmd
= fx_unbox(struct fx_command, entry, c_entry);
if (!subcmd) {
break;
}
next = fx_queue_next(entry);
fx_command_destroy(subcmd);
entry = next;
}
#endif
entry = fx_queue_first(&cmd->c_usage);
while (entry) {
struct fx_command_usage *usage
= fx_unbox(struct fx_command_usage, entry, u_entry);
if (!usage) {
break;
}
next = fx_queue_next(entry);
fx_queue_delete(&cmd->c_usage, entry);
command_usage_destroy(usage);
entry = next;
}
free(cmd);
}
fx_status fx_command_register(struct fx_command *cmd)
{
struct fx_command *tmp = get_command(&command_list, cmd->c_id);
if (tmp) {
return FX_ERR_NAME_EXISTS;
}
if (fx_bst_empty(&command_list)) {
atexit(command_list_cleanup);
}
put_command(&command_list, cmd);
return FX_SUCCESS;
}
fx_status fx_command_set_name(struct fx_command *cmd, const char *name)
{
char *n = fx_strdup(name);
if (!n) {
return FX_ERR_NO_MEMORY;
}
if (cmd->c_name) {
free(cmd->c_name);
cmd->c_name = NULL;
}
cmd->c_name = n;
return FX_SUCCESS;
}
fx_status fx_command_set_long_name(struct fx_command *cmd, const char *name)
{
char *n = fx_strdup(name);
if (!n) {
return FX_ERR_NO_MEMORY;
}
if (cmd->c_long_name) {
free(cmd->c_long_name);
cmd->c_long_name = NULL;
}
cmd->c_long_name = n;
return FX_SUCCESS;
}
fx_status fx_command_set_short_name(struct fx_command *cmd, char name)
{
cmd->c_short_name = name;
return FX_SUCCESS;
}
fx_status fx_command_set_flags(struct fx_command *cmd, fx_command_flags flags)
{
cmd->c_flags = flags;
return FX_SUCCESS;
}
fx_status fx_command_set_description(struct fx_command *cmd, const char *description)
{
char *desc = fx_strdup(description);
if (!desc) {
return FX_ERR_NO_MEMORY;
}
if (cmd->c_description) {
free(cmd->c_description);
cmd->c_description = NULL;
}
cmd->c_description = desc;
return FX_SUCCESS;
}
fx_status fx_command_set_callback(
struct fx_command *cmd, fx_command_callback callback)
{
cmd->c_callback = callback;
return FX_SUCCESS;
}
fx_status fx_command_set_parent(fx_command *cmd, unsigned int parent_id)
{
cmd->c_parent_id = parent_id;
cmd->c_parent = NULL;
return FX_SUCCESS;
}
struct fx_command_option *fx_command_add_option(struct fx_command *cmd, int id)
{
struct fx_command_option *opt = fx_command_option_create();
if (!opt) {
return NULL;
}
opt->opt_id = id;
fx_queue_push_back(&cmd->c_opt, &opt->opt_entry);
return opt;
}
struct fx_command_arg *fx_command_add_arg(struct fx_command *cmd, int id)
{
struct fx_command_arg *arg = fx_command_arg_create();
if (!arg) {
return NULL;
}
arg->arg_id = id;
fx_queue_push_back(&cmd->c_arg, &arg->arg_entry);
return arg;
}
struct fx_command_usage *fx_command_add_usage(struct fx_command *cmd)
{
struct fx_command_usage *usage = malloc(sizeof *usage);
if (!usage) {
return NULL;
}
memset(usage, 0x0, sizeof *usage);
fx_queue_push_back(&cmd->c_usage, &usage->u_entry);
return usage;
}
const struct fx_command_option *fx_command_get_option(
const struct fx_command *cmd, int id)
{
struct fx_queue_entry *cur = fx_queue_first(&cmd->c_opt);
while (cur) {
const struct fx_command_option *opt
= fx_unbox(struct fx_command_option, cur, opt_entry);
if (opt->opt_id == id) {
return opt;
}
cur = fx_queue_next(cur);
}
return NULL;
}
fx_status fx_command_usage_add_option(
struct fx_command_usage *usage, struct fx_command_option *opt)
{
struct fx_command_usage_entry *u_opt = malloc(sizeof *u_opt);
if (!u_opt) {
return FX_ERR_NO_MEMORY;
}
memset(u_opt, 0x0, sizeof *u_opt);
u_opt->e_type = CMD_USAGE_OPT;
u_opt->e_opt = opt;
fx_queue_push_back(&usage->u_parts, &u_opt->e_entry);
return FX_SUCCESS;
}
fx_status fx_command_usage_add_arg(
struct fx_command_usage *usage, struct fx_command_arg *arg)
{
struct fx_command_usage_entry *u_arg = malloc(sizeof *u_arg);
if (!u_arg) {
return FX_ERR_NO_MEMORY;
}
memset(u_arg, 0x0, sizeof *u_arg);
u_arg->e_type = CMD_USAGE_ARG;
u_arg->e_arg = arg;
fx_queue_push_back(&usage->u_parts, &u_arg->e_entry);
return FX_SUCCESS;
}
fx_status fx_command_usage_add_command(fx_command_usage *usage, unsigned int cmd_id)
{
struct fx_command_usage_entry *u_cmd = malloc(sizeof *u_cmd);
if (!u_cmd) {
return FX_ERR_NO_MEMORY;
}
memset(u_cmd, 0x0, sizeof *u_cmd);
u_cmd->e_type = CMD_USAGE_COMMAND;
u_cmd->e_cmd_id = cmd_id;
fx_queue_push_back(&usage->u_parts, &u_cmd->e_entry);
return FX_SUCCESS;
}
static void prepend_command_name(struct fx_command *cmd, fx_string *out)
{
int nr_names = 0;
cmd->c_name &&nr_names++;
cmd->c_long_name &&nr_names++;
cmd->c_short_name &&nr_names++;
if (nr_names > 1) {
fx_string_prepend_cstr(out, "}");
int r = 0;
if (cmd->c_name) {
fx_string_prepend_cstrf(out, "%s", cmd->c_name);
r++;
}
if (r == 1) {
fx_string_prepend_cstr(out, "|");
}
if (cmd->c_long_name) {
fx_string_prepend_cstrf(out, "--%s", cmd->c_long_name);
r++;
}
if (r == 2) {
fx_string_prepend_cstr(out, "|");
}
if (cmd->c_short_name) {
fx_string_prepend_cstrf(out, "-%c", cmd->c_short_name);
r++;
}
fx_string_prepend_cstr(out, "{");
} else {
fx_string_prepend_cstr(out, cmd->c_name);
}
}
static void get_qualified_command_name(
struct fx_command *cmd, const struct fx_arglist *args, fx_string *out)
{
for (unsigned int i = 0; i <= args->list_argv_last_command; i++) {
if (i > 0) {
fx_string_append_cstr(out, " ");
}
fx_string_append_cstr(out, args->list_argv[i]);
}
#if 0
prepend_command_name(cmd, out);
cmd = cmd->c_parent;
while (cmd) {
fx_string_prepend_cstr(out, " ");
prepend_command_name(cmd, out);
cmd = cmd->c_parent;
}
#endif
}
static void get_usage_string(
struct fx_command *cmd, struct fx_arglist *args,
struct fx_command_usage *usage, fx_string *out)
{
get_qualified_command_name(cmd, args, out);
fx_string *cmd_name = fx_string_create();
struct fx_queue_entry *q_entry = fx_queue_first(&usage->u_parts);
while (q_entry) {
struct fx_command_usage_entry *entry = fx_unbox(
struct fx_command_usage_entry, q_entry, e_entry);
if (!entry) {
break;
}
fx_string_clear(cmd_name);
fx_string_append_cstr(out, " ");
switch (entry->e_type) {
case CMD_USAGE_ARG:
if (entry->e_arg) {
z__fx_get_arg_usage_string(entry->e_arg, false, out);
} else {
fx_string_append_cstr(out, "[[ARGS]");
}
break;
case CMD_USAGE_OPT:
if (entry->e_opt) {
z__fx_get_option_usage_string(
entry->e_opt, CMD_STR_DIRECT_USAGE, out);
} else {
fx_string_append_cstr(out, "[[OPTIONS]");
}
break;
case CMD_USAGE_COMMAND:
if (entry->e_cmd_id == FX_COMMAND_INVALID_ID) {
fx_string_append_cstr(out, "[[COMMAND]");
break;
}
struct fx_command *subcmd_info
= get_command(&command_list, entry->e_cmd_id);
prepend_command_name(subcmd_info, cmd_name);
fx_string_append_s(out, cmd_name);
break;
default:
break;
}
q_entry = fx_queue_next(q_entry);
}
fx_string_unref(cmd_name);
}
fx_string *z__fx_command_default_usage_string(
struct fx_command *cmd, struct fx_command_option *with_opt,
const struct fx_arglist *args)
{
fx_string *str = fx_string_create();
get_qualified_command_name(cmd, args, str);
if (with_opt) {
fx_string_append_cstr(str, " ");
z__fx_get_option_usage_string(with_opt, CMD_STR_DIRECT_USAGE, str);
} else if (!fx_queue_empty(&cmd->c_opt)) {
fx_string_append_cstr(str, " [OPTIONS]");
}
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_arg);
while (entry) {
struct fx_command_arg *arg
= fx_unbox(struct fx_command_arg, entry, arg_entry);
fx_string_append_cstr(str, " ");
z__fx_get_arg_usage_string(arg, false, str);
entry = fx_queue_next(entry);
}
if (!fx_queue_empty(&cmd->c_subcommands)) {
fx_string_append_cstr(str, " [COMMAND]");
}
return str;
}
static void get_command_string(struct fx_command *cmd, fx_string *out)
{
int r = 0;
if (cmd->c_name) {
fx_string_append_cstrf(out, F_GREEN "%s" F_RESET, cmd->c_name);
r++;
}
if (cmd->c_short_name) {
if (r > 0) {
fx_string_append_cstr(out, ", ");
}
fx_string_append_cstrf(
out, F_GREEN "-%c" F_RESET, cmd->c_short_name);
r++;
}
if (cmd->c_long_name) {
if (r > 0) {
fx_string_append_cstr(out, ", ");
}
fx_string_append_cstrf(
out, F_GREEN "--%s" F_RESET, cmd->c_long_name);
r++;
}
}
static void get_command_description(struct fx_command *cmd, fx_string *out)
{
fx_string_append_cstr(out, cmd->c_description);
}
/* 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))
static void print_options_list(struct fx_command *cmd)
{
unsigned int term_width = 0;
fx_tty_get_dimensions(OUTPUT_STREAM, &term_width, NULL);
unsigned int newline_threshold = 1000000;
if (term_width) {
newline_threshold = term_width - 12 - 35;
}
fx_tty_printf(OUTPUT_STREAM, 0, "\n" F_YELLOW "OPTIONS:" F_RESET "\n");
size_t desc_margin = 0;
fx_string *opt_str = fx_string_create();
fx_string *desc_str = fx_string_create();
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt);
while (entry) {
struct fx_command_option *opt
= fx_unbox(struct fx_command_option, entry, opt_entry);
fx_string_clear(opt_str);
fx_string_clear(desc_str);
z__fx_get_option_usage_string(opt, CMD_STR_COLOUR, opt_str);
z__fx_get_option_description(opt, desc_str);
size_t opt_len = fx_string_get_size(
opt_str, FX_STRLEN_IGNORE_ESC
| FX_STRLEN_IGNORE_MOD)
+ 4;
size_t desc_len = fx_string_get_size(
desc_str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD);
if (description_on_separate_line(opt_len, desc_len)) {
goto skip;
}
if (opt_len > desc_margin) {
desc_margin = opt_len;
}
skip:
entry = fx_queue_next(entry);
}
fx_paragraph_format format = {0};
format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE;
format.p_left_margin = desc_margin + 4;
format.p_right_margin = 4;
size_t i = 0;
entry = fx_queue_first(&cmd->c_opt);
while (entry) {
struct fx_command_option *opt
= fx_unbox(struct fx_command_option, entry, opt_entry);
if (!opt) {
break;
}
fx_string_clear(opt_str);
fx_string_clear(desc_str);
z__fx_get_option_usage_string(opt, CMD_STR_COLOUR, opt_str);
z__fx_get_option_description(opt, desc_str);
size_t opt_len = fx_string_get_size(
opt_str, FX_STRLEN_IGNORE_ESC
| FX_STRLEN_IGNORE_MOD)
+ 4;
size_t desc_len = fx_string_get_size(
desc_str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD);
bool new_paragraph
= description_on_separate_line(opt_len, desc_len);
if (new_paragraph) {
fx_tty_putc(OUTPUT_STREAM, 0, '\n');
}
fx_tty_puts(OUTPUT_STREAM, 0, " ");
fx_tty_puts(OUTPUT_STREAM, 0, fx_string_ptr(opt_str));
if (new_paragraph) {
format.p_flags = 0;
format.p_left_margin = 8;
format.p_right_margin = 4;
fx_tty_putc(OUTPUT_STREAM, 0, '\n');
} else {
format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE;
format.p_left_margin = desc_margin + 4;
format.p_right_margin = 4;
}
unsigned int len = opt_len;
while (len < format.p_left_margin) {
fx_tty_putc(OUTPUT_STREAM, 0, ' ');
len++;
}
fx_print_paragraph(fx_string_ptr(desc_str), OUTPUT_STREAM, &format);
if (new_paragraph) {
fx_tty_putc(OUTPUT_STREAM, 0, '\n');
}
entry = fx_queue_next(entry);
}
fx_string_unref(opt_str);
fx_string_unref(desc_str);
}
static void print_args_list(struct fx_command *cmd)
{
fx_tty_printf(OUTPUT_STREAM, 0, "\n" F_YELLOW "ARGS:" F_RESET "\n");
size_t desc_margin = 0;
fx_string *str = fx_string_create();
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_arg);
while (entry) {
struct fx_command_arg *arg
= fx_unbox(struct fx_command_arg, entry, arg_entry);
fx_string_clear(str);
z__fx_get_arg_usage_string(arg, true, str);
size_t len = fx_string_get_size(
str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD)
+ 4;
if (len > desc_margin) {
desc_margin = len;
}
entry = fx_queue_next(entry);
}
fx_paragraph_format format = {0};
format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE;
format.p_left_margin = desc_margin + 4;
format.p_right_margin = 4;
size_t i = 0;
entry = fx_queue_first(&cmd->c_arg);
while (entry) {
struct fx_command_arg *arg
= fx_unbox(struct fx_command_arg, entry, arg_entry);
fx_string_clear(str);
z__fx_get_arg_usage_string(arg, true, str);
fx_tty_puts(OUTPUT_STREAM, 0, " ");
fx_tty_puts(OUTPUT_STREAM, 0, fx_string_ptr(str));
unsigned int len = fx_string_get_size(
str, FX_STRLEN_IGNORE_ESC
| FX_STRLEN_IGNORE_MOD)
+ 4;
while (len < format.p_left_margin) {
fx_tty_putc(OUTPUT_STREAM, 0, ' ');
len++;
}
fx_string_clear(str);
z__fx_get_arg_description(arg, str);
fx_print_paragraph(fx_string_ptr(str), OUTPUT_STREAM, &format);
entry = fx_queue_next(entry);
}
fx_string_unref(str);
}
static void print_commands_list(struct fx_command *cmd)
{
fx_tty_puts(OUTPUT_STREAM, 0, "\n" F_YELLOW "COMMANDS:" F_RESET "\n");
size_t desc_margin = 0;
fx_string *str = fx_string_create();
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_subcommands);
while (entry) {
struct fx_command *sub
= fx_unbox(struct fx_command, entry, c_entry);
fx_string_clear(str);
get_command_string(sub, str);
size_t len = fx_string_get_size(
str, FX_STRLEN_IGNORE_ESC | FX_STRLEN_IGNORE_MOD)
+ 4;
if (len > desc_margin) {
desc_margin = len;
}
entry = fx_queue_next(entry);
}
fx_paragraph_format format = {0};
format.p_flags = FX_PARAGRAPH_DONT_INDENT_FIRST_LINE;
format.p_left_margin = desc_margin + 4;
format.p_right_margin = 4;
size_t i = 0;
entry = fx_queue_first(&cmd->c_subcommands);
while (entry) {
struct fx_command *sub
= fx_unbox(struct fx_command, entry, c_entry);
fx_string_clear(str);
get_command_string(sub, str);
fx_tty_puts(OUTPUT_STREAM, 0, " ");
fx_tty_puts(OUTPUT_STREAM, 0, fx_string_ptr(str));
unsigned int len = fx_string_get_size(
str, FX_STRLEN_IGNORE_ESC
| FX_STRLEN_IGNORE_MOD)
+ 4;
while (len < format.p_left_margin) {
fx_tty_putc(OUTPUT_STREAM, 0, ' ');
len++;
}
fx_string_clear(str);
get_command_description(sub, str);
fx_print_paragraph(fx_string_ptr(str), OUTPUT_STREAM, &format);
entry = fx_queue_next(entry);
}
fx_string_unref(str);
}
struct fx_command *fx_command_get_subcommand_with_name(
struct fx_command *cmd, const char *name)
{
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_subcommands);
while (entry) {
struct fx_command *subcmd
= fx_unbox(struct fx_command, entry, c_entry);
if (!subcmd || !subcmd->c_name) {
goto skip;
}
if (!strcmp(subcmd->c_name, name)) {
return subcmd;
}
skip:
entry = fx_queue_next(entry);
}
return NULL;
}
struct fx_command *fx_command_get_subcommand_with_long_name(
struct fx_command *cmd, const char *long_name)
{
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_subcommands);
while (entry) {
struct fx_command *subcmd
= fx_unbox(struct fx_command, entry, c_entry);
if (!subcmd || !subcmd->c_long_name) {
goto skip;
}
if (!strcmp(subcmd->c_name, long_name)) {
return subcmd;
}
skip:
entry = fx_queue_next(entry);
}
return NULL;
}
struct fx_command *fx_command_get_subcommand_with_short_name(
struct fx_command *cmd, char short_name)
{
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_subcommands);
while (entry) {
struct fx_command *subcmd
= fx_unbox(struct fx_command, entry, c_entry);
if (!subcmd || !subcmd->c_short_name) {
goto skip;
}
if (subcmd->c_short_name == short_name) {
return subcmd;
}
skip:
entry = fx_queue_next(entry);
}
return NULL;
}
struct fx_command_option *fx_command_get_option_with_long_name(
struct fx_command *cmd, const char *long_name)
{
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt);
while (entry) {
struct fx_command_option *opt
= fx_unbox(struct fx_command_option, entry, opt_entry);
if (!opt || !opt->opt_long_name) {
goto skip;
}
if (!strcmp(opt->opt_long_name, long_name)) {
return opt;
}
skip:
entry = fx_queue_next(entry);
}
return NULL;
}
struct fx_command_option *fx_command_get_option_with_short_name(
struct fx_command *cmd, char short_name)
{
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt);
while (entry) {
struct fx_command_option *opt
= fx_unbox(struct fx_command_option, entry, opt_entry);
if (!opt || !opt->opt_long_name) {
goto skip;
}
if (opt->opt_short_name == short_name) {
return opt;
}
skip:
entry = fx_queue_next(entry);
}
return NULL;
}
struct fx_command_option *fx_command_get_option_with_id(
struct fx_command *cmd, unsigned int id)
{
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_opt);
while (entry) {
struct fx_command_option *opt
= fx_unbox(struct fx_command_option, entry, opt_entry);
if (opt->opt_id == id) {
return opt;
}
entry = fx_queue_next(entry);
}
return NULL;
}
struct fx_command_arg *fx_command_get_arg_with_id(
struct fx_command *cmd, unsigned int id)
{
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_arg);
while (entry) {
struct fx_command_arg *arg
= fx_unbox(struct fx_command_arg, entry, arg_entry);
if (arg->arg_id == id) {
return arg;
}
entry = fx_queue_next(entry);
}
return NULL;
}
static void print_usage(struct fx_command *cmd, struct fx_arglist *args)
{
fx_paragraph_format format = {0};
format.p_left_margin = format.p_right_margin = 4;
fx_tty_puts(OUTPUT_STREAM, 0, F_YELLOW "USAGE:" F_RESET "\n");
if (fx_queue_empty(&cmd->c_usage)) {
fx_string *usage
= z__fx_command_default_usage_string(cmd, NULL, args);
fx_print_paragraph(fx_string_ptr(usage), OUTPUT_STREAM, &format);
fx_string_unref(usage);
return;
}
fx_string *str = fx_string_create();
struct fx_queue_entry *entry = fx_queue_first(&cmd->c_usage);
while (entry) {
struct fx_command_usage *usage
= fx_unbox(struct fx_command_usage, entry, u_entry);
fx_string_clear(str);
get_usage_string(cmd, args, usage, str);
fx_print_paragraph(fx_string_ptr(str), OUTPUT_STREAM, &format);
entry = fx_queue_next(entry);
}
fx_string_unref(str);
}
static void print_help(struct fx_command *cmd, struct fx_arglist *args)
{
fx_paragraph_format format = {0};
if (!cmd->c_parent) {
fx_tty_printf(
OUTPUT_STREAM, 0, F_GREEN "%s" F_RESET "\n", cmd->c_name);
}
if (cmd->c_description) {
fx_print_paragraph(cmd->c_description, OUTPUT_STREAM, &format);
}
fx_tty_putc(OUTPUT_STREAM, 0, '\n');
print_usage(cmd, args);
if (!fx_queue_empty(&cmd->c_opt)) {
print_options_list(cmd);
}
if (!fx_queue_empty(&cmd->c_arg)) {
print_args_list(cmd);
}
if (!fx_queue_empty(&cmd->c_subcommands)) {
print_commands_list(cmd);
}
}
static int execute_command(struct fx_command *cmd, struct fx_arglist *args)
{
if (!cmd) {
return -1;
}
size_t nr_items = fx_arglist_get_count(
args, FX_COMMAND_INVALID_ID, FX_COMMAND_INVALID_ID);
size_t nr_help = fx_arglist_get_count(
args, FX_COMMAND_OPTION_HELP, FX_COMMAND_INVALID_ID);
if ((cmd->c_flags & FX_COMMAND_SHOW_HELP_BY_DEFAULT) && nr_items == 0) {
print_help(cmd, args);
return 0;
}
if (nr_help > 0) {
print_help(cmd, args);
return 0;
}
if (cmd->c_callback) {
return cmd->c_callback(cmd, args, NULL);
}
return -1;
}
static fx_status add_subcommand(struct fx_command *parent, struct fx_command *child)
{
fx_queue_push_back(&parent->c_subcommands, &child->c_entry);
child->c_parent = parent;
return FX_SUCCESS;
}
static int resolve_command_parents(struct fx_bst *commands)
{
struct fx_bst_node *node = fx_bst_first(commands);
while (node) {
struct fx_command *cmd = fx_unbox(struct fx_command, node, c_node);
if (cmd->c_parent_id == FX_COMMAND_INVALID_ID) {
goto skip;
}
cmd->c_parent = get_command(commands, cmd->c_parent_id);
if (!cmd->c_parent) {
return -1;
}
add_subcommand(cmd->c_parent, cmd);
skip:
node = fx_bst_next(node);
}
return 0;
}
int fx_command_dispatch(unsigned int cmd_id, int argc, const char **argv)
{
if (resolve_command_parents(&command_list) != 0) {
return -1;
}
struct fx_command *cmd = get_command(&command_list, cmd_id);
if (!cmd) {
return -1;
}
struct fx_arglist *args = fx_arglist_create();
args->list_argc = argc;
args->list_argv = argv;
args->list_command = cmd;
fx_status status = fx_arglist_parse(args, &cmd, argc, argv);
if (FX_ERR(status)) {
fx_arglist_destroy(args);
return -1;
}
int ret = execute_command(cmd, args);
fx_arglist_destroy(args);
// print_help(cmd);
return ret;
}