From 9cdf2df3b30627d75f7bc348dfdc25a621fb1a5e Mon Sep 17 00:00:00 2001 From: x3 Date: Tue, 8 Aug 2023 19:15:19 +0200 Subject: [PATCH] Add menu based arg parsgin --- src/cmd.c | 8 +- src/cmd.h | 1 + src/cmd_add.c | 8 ++ src/cmd_ed2k.c | 2 +- src/cmd_modify.c | 4 +- src/config.c | 340 +++++++++++++++++++++++++++++++++++++++++-------------- src/config.h | 33 ++++++ src/error.h | 1 + 8 files changed, 306 insertions(+), 91 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index c5fd9d0..a5cb0d4 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -14,6 +14,7 @@ struct cmd_entry { bool need_cache : 1; /* Does this cmd needs the file cache? */ const char *arg_name; /* If this argument is present, execute this cmd */ + enum error (*argcheck)(void); /* Function to check argument correctness before calling fn */ enum error (*fn)(void *data); /* The function for the command */ }; @@ -22,7 +23,7 @@ static const struct cmd_entry ents[] = { { .arg_name = "server-version", .fn = cmd_server_version, .need_api = true }, { .arg_name = "uptime", .fn = cmd_server_uptime, .need_auth = true }, { .arg_name = "ed2k", .fn = cmd_ed2k, }, - { .arg_name = "add", .fn = cmd_add, .need_auth = true, .need_cache = true, }, + { .arg_name = "add", .fn = cmd_add, .argcheck = cmd_add_argcheck, .need_auth = true, .need_cache = true, }, { .arg_name = "modify", .fn = cmd_modify, .need_auth = true, .need_cache = false, }, }; static const int32_t ents_len = sizeof(ents)/sizeof(*ents); @@ -31,6 +32,11 @@ static enum error cmd_run_one(const struct cmd_entry *ent) { enum error err = NOERR; + if (ent->argcheck) { + err = ent->argcheck(); + if (err != NOERR) + goto end; + } if (ent->need_cache) { err = cache_init(); if (err != NOERR) diff --git a/src/cmd.h b/src/cmd.h index 57442dc..27da97f 100644 --- a/src/cmd.h +++ b/src/cmd.h @@ -13,6 +13,7 @@ enum error cmd_main(); * Add files to the AniDB list */ enum error cmd_add(void *); +enum error cmd_add_argcheck(); /* * Take in a file/folder and print out diff --git a/src/cmd_add.c b/src/cmd_add.c index af534aa..988a8dc 100644 --- a/src/cmd_add.c +++ b/src/cmd_add.c @@ -175,3 +175,11 @@ enum error cmd_add(void *data) return err; } +enum error cmd_add_argcheck() +{ + if (config_get_nonopt_count() == 0) { + uio_error("No files specified"); + return ERR_CMD_ARG; + } + return NOERR; +} diff --git a/src/cmd_ed2k.c b/src/cmd_ed2k.c index 16847aa..4fbea6a 100644 --- a/src/cmd_ed2k.c +++ b/src/cmd_ed2k.c @@ -49,7 +49,7 @@ enum error cmd_ed2k(void *data) return ERR_CMD_ARG; } - if (config_get("link", (void**)&link) == NOERR) + if (config_get_subopt("ed2k", "link", (void**)&link) == NOERR) opts.link = *link; for (int i = 0; i < fcount; i++) { diff --git a/src/cmd_modify.c b/src/cmd_modify.c index c5b40bb..795ac4e 100644 --- a/src/cmd_modify.c +++ b/src/cmd_modify.c @@ -73,11 +73,11 @@ enum error cmd_modify(void *data) return ERR_CMD_ARG; } - if (config_get("watched", (void**)&watched) == NOERR && *watched) { + if (config_get_subopt("modify", "watched", (void**)&watched) == NOERR && *watched) { mopt.watched = *watched; mopt.watched_set = true; - if (config_get("wdate", (void**)&wdate_str) == NOERR) { + if (config_get_subopt("modify", "wdate", (void**)&wdate_str) == NOERR) { uint64_t wdate = util_iso2unix(*wdate_str); if (wdate == 0) { diff --git a/src/config.c b/src/config.c index 3f0da3b..c22148c 100644 --- a/src/config.c +++ b/src/config.c @@ -19,8 +19,10 @@ #include "config.h" #include "error.h" #include "util.h" +#include "uio.h" static int show_help(struct conf_entry *ce); +static int show_subcomm_help(struct conf_entry *ce); static int config_parse_file(); static enum error config_required_check(); @@ -35,35 +37,44 @@ static int config_set_bool(struct conf_entry *ce, char *arg); /* Everything not explicitly defined, is 0 */ /* If an option only has a long name, the short name also has to be * defined. For example, a number larger than UCHAR_MAX */ -static struct conf_entry options[] = { - { .l_name = "help", .s_name = 'h', .has_arg = no_argument, - .action_func = show_help, .in_args = true, - .type = OTYPE_ACTION, .handle_order = 0, - .h_desc = "Display the help and exit", }, -/* - { .l_name = "config-dir", .s_name = 'b', .has_arg = required_argument, - .default_func = config_def_config_dir, .set_func = config_set_str, - .in_args = true, .type = OTYPE_S, .handle_order = 0 }, - { .l_name = "default-download-dir", .s_name = 'd', .has_arg = required_argument, - .default_func = config_def_default_download_dir, .set_func = config_set_str, - .in_file = true, .in_args = true, .type = OTYPE_S, .handle_order = 1 }, +#define SUBCOMM_HELP { \ + .l_name = "help", .s_name = 'h', .has_arg = no_argument, \ + .action_func = show_subcomm_help, .in_args = true, \ + .type = OTYPE_ACTION, .handle_order = 0, \ + .h_desc = "Display the help for the subcommand and exit", } - { .l_name = "port", .s_name = 'p', .has_arg = required_argument, - .set_func = config_set_port, .value.hu = 21729, .value_is_set = true, - .in_file = true, .in_args = true, .type = OTYPE_HU, .handle_order = 1 }, - { .l_name = "foreground", .s_name = 'f', .has_arg = no_argument, - .set_func = config_set_bool, .value.b = false, .value_is_set = true, - .in_args = true, .type = OTYPE_B, .handle_order = 1 }, +static struct conf_entry subcomm_def_help_opt = SUBCOMM_HELP; - { .l_name = "write-config", .s_name = UCHAR_MAX + 1, .has_arg = no_argument, - .action_func = config_action_write_config, .value_is_set = true, - .in_args = true, .type = OTYPE_ACTION, .handle_order = 2 }, +static struct conf_entry ed2k_subopts[] = { + SUBCOMM_HELP, - { .l_name = "peer-id", .s_name = UCHAR_MAX + 2, .has_arg = required_argument, - .default_func = config_def_peer_id, .type = OTYPE_S, .handle_order = 1 }, - */ + { .l_name = "link", .s_name = 'l', .has_arg = no_argument, + .set_func = config_set_bool, .in_args = true, + .type = OTYPE_B, .handle_order = 0, .value_is_set = true, + .h_desc = "Print an ed2k link for the files", }, +}; + +static struct conf_entry modify_add_subopts[] = { + SUBCOMM_HELP, + + { .l_name = "watched", .s_name = 'w', .has_arg = no_argument, + .set_func = config_set_bool, .in_args = true, + .type = OTYPE_B, .handle_order = 0, .value_is_set = true, + .h_desc = "Mark the episode as watched when adding files", }, + + { .l_name = "wdate", .s_name = UCHAR_MAX + 4, .has_arg = required_argument, + .set_func = config_set_str, .in_args = true, + .type = OTYPE_S, .handle_order = 0, + .h_desc = "Set the watched date when adding files", }, +}; + +static struct conf_entry options[] = { + { .l_name = "help", .s_name = 'h', .has_arg = no_argument, + .action_func = show_help, .in_args = true, + .type = OTYPE_ACTION, .handle_order = 0, + .h_desc = "Display the help and exit", }, { .l_name = "username", .s_name = 'u', .has_arg = required_argument, .set_func = config_set_str, .in_args = true, .in_file = true, @@ -102,16 +113,6 @@ static struct conf_entry options[] = { .type = OTYPE_B, .handle_order = 1, .value_is_set = true, .h_desc = "not implemented", }, - { .l_name = "watched", .s_name = 'w', .has_arg = no_argument, - .set_func = config_set_bool, .in_args = true, - .type = OTYPE_B, .handle_order = 1, .value_is_set = true, - .h_desc = "Mark the episode as watched when adding files", }, - - { .l_name = "link", .s_name = 'l', .has_arg = no_argument, - .set_func = config_set_bool, .in_args = true, - .type = OTYPE_B, .handle_order = 1, .value_is_set = true, - .h_desc = "Print an ed2k link when running the ed2k command", }, - { .l_name = "cachedb", .s_name = 'd', .has_arg = required_argument, .set_func = config_set_str, .in_args = true, .in_file = true, .type = OTYPE_S, .handle_order = 1, /*.default_func = config_def_cachedb*/ @@ -122,45 +123,50 @@ static struct conf_entry options[] = { .type = OTYPE_B, .handle_order = 1, .value_is_set = true, .h_desc = "Enable debug output", }, - { .l_name = "wdate", .s_name = UCHAR_MAX + 4, .has_arg = required_argument, - .set_func = config_set_str, .in_args = true, - .type = OTYPE_S, .handle_order = 1, - .h_desc = "Set the watched date when adding files", }, - - /*### cmd ###*/ + /*### cmds ###*/ { .l_name = "server-version", .s_name = UCHAR_MAX + 2, .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, - .type = OTYPE_B, .handle_order = 1, .value_is_set = true, - .h_desc = "CMD: Request the server version", }, + .type = OTYPE_SUBCOMMAND, .handle_order = 1, .value_is_set = true, + .h_desc = "Request the server version", + }, { .l_name = "version", .s_name = 'v', .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, - .type = OTYPE_B, .handle_order = 1, .value_is_set = true, - .h_desc = "CMD: Print the caniadd version", }, + .type = OTYPE_SUBCOMMAND, .handle_order = 1, .value_is_set = true, + .h_desc = "Print the caniadd version", }, { .l_name = "uptime", .s_name = UCHAR_MAX + 3, .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, - .type = OTYPE_B, .handle_order = 1, .value_is_set = true, - .h_desc = "CMD: Request the uptime of the api servers", }, + .type = OTYPE_SUBCOMMAND, .handle_order = 1, .value_is_set = true, + .h_desc = "Request the uptime of the api servers", }, { .l_name = "ed2k", .s_name = 'e', .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, - .type = OTYPE_B, .handle_order = 1, - .h_desc = "CMD: Run an ed2k hash on the file arguments", }, + .type = OTYPE_SUBCOMMAND, .handle_order = 1, + .h_desc = "Run an ed2k hash on the file arguments", + .subopts = ed2k_subopts, + .subopts_count = sizeof(ed2k_subopts) / sizeof(ed2k_subopts[0]), + }, { .l_name = "add", .s_name = 'a', .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, - .type = OTYPE_B, .handle_order = 1, - .h_desc = "CMD: Add files to your anidb list", }, + .type = OTYPE_SUBCOMMAND, .handle_order = 1, + .h_desc = "Add files to your anidb list", + .subopts = modify_add_subopts, + .subopts_count = sizeof(modify_add_subopts) / sizeof(modify_add_subopts[0]), + }, /* Arguments are either mylist id's, or file sizes and names * in the format '[watch_date/]/'. The filename can't contain * '/' characters. */ { .l_name = "modify", .s_name = 'W', .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, - .type = OTYPE_B, .handle_order = 1, - .h_desc = "CMD: Modify files in your anidb list", }, + .type = OTYPE_SUBCOMMAND, .handle_order = 1, + .h_desc = "Modify files in your anidb list", + .subopts = modify_add_subopts, + .subopts_count = sizeof(modify_add_subopts) / sizeof(modify_add_subopts[0]), + }, /*{ .l_name = "stats", .s_name = UCHAR_MAX + 5, .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, @@ -169,33 +175,42 @@ static struct conf_entry options[] = { }; static const size_t options_count = sizeof(options) / sizeof(options[0]); -static const char **opt_argv = NULL; +/* Used in show_subcomm_help to output info about the subcommand */ +static struct conf_entry *current_subcommand = NULL; +static char **opt_argv = NULL; static int opt_argc = 0; -static void config_build_getopt_args(char out_sopt[options_count * 2 + 1], - struct option out_lopt[options_count + 1]) +static void config_build_getopt_args(int opts_count, const struct conf_entry opts[opts_count], + char out_sopt[opts_count * 2 + 1], struct option out_lopt[opts_count + 1], + bool stop_at_1st_nonopt) { int i_sopt = 0, i_lopt = 0; + if (stop_at_1st_nonopt) { + /* Tell getopts to stop at the 1st non-option argument */ + out_sopt[i_sopt++] = '+'; + } - for (int i = 0; i < options_count; i++) { + for (int i = 0; i < opts_count; i++) { + if (opts[i].type == OTYPE_SUBCOMMAND) + continue; /* Short options */ - if (options[i].s_name && options[i].s_name <= UCHAR_MAX) { - out_sopt[i_sopt++] = options[i].s_name; - if (options[i].has_arg == required_argument) + if (opts[i].s_name && opts[i].s_name <= UCHAR_MAX) { + out_sopt[i_sopt++] = opts[i].s_name; + if (opts[i].has_arg == required_argument) out_sopt[i_sopt++] = ':'; - assert(options[i].has_arg == required_argument || - options[i].has_arg == no_argument); + assert(opts[i].has_arg == required_argument || + opts[i].has_arg == no_argument); } - /* Long options */ - if (options[i].l_name) { - assert(options[i].s_name); + /* Long opts */ + if (opts[i].l_name) { + assert(opts[i].s_name); - out_lopt[i_lopt].name = options[i].l_name; - out_lopt[i_lopt].has_arg = options[i].has_arg; + out_lopt[i_lopt].name = opts[i].l_name; + out_lopt[i_lopt].has_arg = opts[i].has_arg; out_lopt[i_lopt].flag = NULL; - out_lopt[i_lopt].val = options[i].s_name; + out_lopt[i_lopt].val = opts[i].s_name; i_lopt++; } @@ -205,33 +220,36 @@ static void config_build_getopt_args(char out_sopt[options_count * 2 + 1], memset(&out_lopt[i_lopt], 0, sizeof(struct option)); } -static int config_read_args(int argc, char **argv, char sopt[options_count * 2 + 1], - struct option lopt[options_count + 1], int level) +static int config_read_args(int argc, char **argv, + int opts_count, struct conf_entry opts[opts_count], + char sopt[opts_count * 2 + 1], struct option lopt[opts_count + 1], int level) { int optc, err = NOERR; optind = 1; + //uio_debug("Before %d %s", argc, argv[0]); while ((optc = getopt_long(argc, argv, sopt, lopt, NULL)) >= 0) { bool handled = false; + //uio_debug("Optc: %c", optc); - for (int i = 0; i < options_count; i++) { - if (options[i].handle_order != level) { + for (int i = 0; i < opts_count; i++) { + if (opts[i].handle_order != level) { /* Lie a lil :x */ handled = true; continue; } - if (optc == options[i].s_name) { - if (options[i].type != OTYPE_ACTION) - err = options[i].set_func(&options[i], optarg); + if (optc == opts[i].s_name) { + if (opts[i].type != OTYPE_ACTION) + err = opts[i].set_func(&opts[i], optarg); else - err = options[i].action_func(&options[i]); + err = opts[i].action_func(&opts[i]); if (err != NOERR) goto end; - options[i].value_is_set = true; + opts[i].value_is_set = true; handled = true; break; @@ -269,16 +287,72 @@ static enum error config_required_check() return err; } +static enum error config_parse_subcomm_subopts(struct conf_entry *subcomm) +{ + char sopt[64]; + struct option lopt[32]; + enum error err; + + /* Set the global current subcommand pointer */ + current_subcommand = subcomm; + config_build_getopt_args(subcomm->subopts_count, subcomm->subopts, sopt, lopt, false); + + //uio_debug("Parsing subconn %s", subcomm->l_name); + //uio_debug("sopt: %s", sopt); + + /* Update args for next parsing and nonopts parsing for later */ + opt_argv = &opt_argv[optind]; + opt_argc = config_get_nonopt_count(); + /* argv[0] (which is the subcommand string) will be treated as the filename here, and skipped */ + err = config_read_args(opt_argc, opt_argv, + subcomm->subopts_count, subcomm->subopts, sopt, lopt, 0); + if (err == NOERR) { + /* Mark subcommand as set */ + subcomm->value_is_set = true; + subcomm->value.b = true; + } + + current_subcommand = NULL; + return err; +} + +static enum error config_parse_subcommands() +{ + const char *subcomm_str; + enum error err = ERR_OPT_NOTFOUND; + + if (config_get_nonopt_count() <= 0) { + return ERR_OPT_NO_SUBCOMMAND; + } + + subcomm_str = config_get_nonopt(0); + for (int i = 0; i < options_count; i++) { + if (options[i].type != OTYPE_SUBCOMMAND) + continue; + if (strcmp(options[i].l_name, subcomm_str) != 0) + continue; + if (options[i].subopts == NULL) { + /* If no suboptions is defined, define the default + * help one here */ + options[i].subopts = &subcomm_def_help_opt; + options[i].subopts_count = 1; + } + /* Found, parse subopts */ + err = config_parse_subcomm_subopts(&options[i]); + } + return err; +} + enum error config_parse(int argc, char **argv) { enum error err = NOERR; char sopt[options_count * 2 + 1]; struct option lopt[options_count + 1]; - opt_argv = (const char**)argv; + opt_argv = argv; opt_argc = argc; - config_build_getopt_args(sopt, lopt); + config_build_getopt_args(options_count, options, sopt, lopt, true); - err = config_read_args(argc, argv, sopt, lopt, 0); + err = config_read_args(argc, argv, options_count, options, sopt, lopt, 0); if (err != NOERR) goto end; @@ -286,7 +360,7 @@ enum error config_parse(int argc, char **argv) if (err != NOERR) goto end; - err = config_read_args(argc, argv, sopt, lopt, 1); + err = config_read_args(argc, argv, options_count, options, sopt, lopt, 1); if (err != NOERR) goto end; @@ -301,11 +375,24 @@ enum error config_parse(int argc, char **argv) } } - err = config_read_args(argc, argv, sopt, lopt, 2); + err = config_read_args(argc, argv, options_count, options, sopt, lopt, 2); if (err != NOERR) goto end; err = config_required_check(); + if (err != NOERR) + goto end; + + /* Now that all of the global arguments are parsed, do the subcommands */ + err = config_parse_subcommands(); + if (err != NOERR) { + if (err == ERR_OPT_NO_SUBCOMMAND) + printf("No subcommand given!\n\n"); + else if (err == ERR_OPT_NOTFOUND) + printf("The given subcommand doesn't exist\n\n"); + if (err != ERR_OPT_EXIT) /* If err is this, then help() was already called */ + show_help(NULL); + } end: if (err != NOERR) @@ -384,15 +471,63 @@ static int config_set_bool(struct conf_entry *ce, char *arg) return NOERR; } +static int show_subcomm_help(struct conf_entry *ce) +{ + assert(current_subcommand); + const struct conf_entry *subopts = current_subcommand->subopts; + + printf( + "Usage: caniadd [OPTIONS] %s [SUBCOMMAND_OPTIONS]\n" + "%s\n" + "\n" + "SUBCOMMAND_OPTIONS:\n", + current_subcommand->l_name, + current_subcommand->h_desc + ); + + for (size_t i = 0; i < current_subcommand->subopts_count; i++) { + int printed = 0, pad; + + printed += printf(" "); + + if (subopts[i].l_name) + printed += printf("--%s", subopts[i].l_name); + if (subopts[i].s_name < UCHAR_MAX) + printed += printf(", -%c", subopts[i].s_name); + if (subopts[i].has_arg != no_argument) + printed += printf(" arg"); + + pad = 25 - printed; + if (pad <= 0) + pad = 1; + + printf("%*s%s", pad, "", subopts[i].h_desc); + if (subopts[i].value_is_set) { + printf(" Val: "); + if (subopts[i].type == OTYPE_S) + printed += printf("%s", subopts[i].value.s); + else if (subopts[i].type == OTYPE_HU) + printed += printf("%hu", subopts[i].value.hu); + else if (subopts[i].type == OTYPE_B) + printed += printf("%s", subopts[i].value.b ? "true" : "false"); + } + printf("\n"); + } + return ERR_OPT_EXIT; +} + static int show_help(struct conf_entry *ce) { printf( - "Usage: caniadd [OPTION]...\n" + "Usage: caniadd [OPTIONS] SUBCOMMAND [SUBCOMMAND_OPTIONS]\n" "Caniadd will add files to an AniDB list, and possibly more.\n" "\n" "OPTIONS:\n" ); for (size_t i = 0; i < options_count; i++) { + if (options[i].type == OTYPE_SUBCOMMAND) + continue; + int printed = 0, pad; printed += printf(" "); @@ -410,7 +545,7 @@ static int show_help(struct conf_entry *ce) printf("%*s%s", pad, "", options[i].h_desc); if (options[i].value_is_set) { - printf(" Def: "); + printf(" Val: "); if (options[i].type == OTYPE_S) printed += printf("%s", options[i].value.s); else if (options[i].type == OTYPE_HU) @@ -420,6 +555,19 @@ static int show_help(struct conf_entry *ce) } printf("\n"); } + + printf("\nSUBCOMMANDS:\n"); + for (size_t i = 0; i < options_count; i++) { + if (options[i].type != OTYPE_SUBCOMMAND) + continue; + + int printed = 0, pad; + + printed += printf(" %s", options[i].l_name); + + pad = 25 - printed; + printf("%*s%s\n", pad, "", options[i].h_desc); + } return ERR_OPT_EXIT; } @@ -537,17 +685,18 @@ end: } #endif -enum error config_get(const char *key, void **out) +enum error config_get_inter(int cenf_count, const struct conf_entry cenf[cenf_count], + const char *key, void **out) { enum error err = ERR_OPT_NOTFOUND; - for (int i = 0; i < options_count; i++) { - struct conf_entry *cc = &options[i]; + for (int i = 0; i < cenf_count; i++) { + const struct conf_entry *cc = &cenf[i]; if (strcmp(cc->l_name, key) == 0) { if (cc->value_is_set) { if (out) - *out = &cc->value.s; + *out = (void**)&cc->value.s; err = NOERR; } else { err = ERR_OPT_UNSET; @@ -559,6 +708,23 @@ enum error config_get(const char *key, void **out) return err; } +enum error config_get(const char *key, void **out) +{ + return config_get_inter(options_count, options, key, out); +} + +enum error config_get_subopt(const char *subcomm, const char *key, void **out) +{ + for (int i = 0; i < options_count; i++) { + struct conf_entry *cc = &options[i]; + + if (strcmp(cc->l_name, subcomm) == 0) { + return config_get_inter(cc->subopts_count, cc->subopts, key, out); + } + } + return ERR_OPT_NOTFOUND; +} + const char *config_get_nonopt(int index) { if (index >= config_get_nonopt_count()) diff --git a/src/config.h b/src/config.h index 9393e60..963d037 100644 --- a/src/config.h +++ b/src/config.h @@ -16,10 +16,36 @@ enum option_type { /* Does not store anything, does an action. Handled after every * other option are parsed, and defaults set */ OTYPE_ACTION, + OTYPE_SUBCOMMAND, _OTYPE_COUNT }; +#if 0 +/* This should be used to make loopups O(1), but w/e for now */ +enum config_global_options { + OPT_HELP = 0, + OPT_USERNAME, + OPT_PASSWORD, + OPT_PORT, + OPT_API_SERVER, + OPT_API_KEY, + OPT_SAVE_SESSION, + OPT_DESTROY_SESSION, + OPT_CACHEDB, + OPT_DEBUG, +}; + +enum config_subcommands { + SUBCOMM_SERVER_VERSION = 0, + SUBCOMM_VERSION, + SUBCOMM_UPTIME, + SUBCOMM_ED2k, + SUBCOMM_ADD, + SUBCOMM_MODIFY, +}; +#endif + struct conf_entry { const char *l_name; /* The long name for the option, or for the config file */ int s_name; /* Short option name */ @@ -56,6 +82,12 @@ struct conf_entry { * 4. Execute action arguments */ int handle_order : 4; + /* If type is a subcommand, this is the arguments for that subcommand */ + /* '-h' option is available for all of the subcommands if this is not set */ + int subopts_count; + struct conf_entry *subopts; + /* The subcommand parent of this entry, if it is inside of a subcommand */ + //struct conf_entry *subcomm_parent; }; /* @@ -84,6 +116,7 @@ void config_dump(); * It the options is not found, it returns ERR_OPT_NOTFOUND, and out is unchanged */ enum error config_get(const char *key, void **out); +enum error config_get_subopt(const char *subcomm, const char *key, void **out); /* * Return an cmd line that is not an option diff --git a/src/error.h b/src/error.h index 6cf854b..a5ca7a2 100644 --- a/src/error.h +++ b/src/error.h @@ -13,6 +13,7 @@ E(ERR_OPT_EXIT) /* We should exit in main, if config_parse returns this */ \ E(ERR_OPT_UNSET) /* In config_get, if the value isn't set */ \ E(ERR_OPT_NOTFOUND) /* In config_get, if the options is not found */ \ + E(ERR_OPT_NO_SUBCOMMAND) \ \ E(ERR_NET_APIADDR) /* If there are problems with the api servers address */ \ E(ERR_NET_SOCKET) /* If there are problems with the udp socket */ \