From: Michael Tremer Date: Sun, 8 Oct 2023 14:39:02 +0000 (+0000) Subject: cli: pakfire-build: Implement command line option parsing with argp X-Git-Tag: 0.9.30~1525 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=732e2f3786d7ba713e04de9f17b312243ff690ac;p=pakfire.git cli: pakfire-build: Implement command line option parsing with argp Signed-off-by: Michael Tremer --- diff --git a/src/cli/lib/build.c b/src/cli/lib/build.c index 875657038..f1e98f087 100644 --- a/src/cli/lib/build.c +++ b/src/cli/lib/build.c @@ -186,7 +186,7 @@ int cli_build(struct pakfire* pakfire, int argc, char* argv[]) { } // Process all packages - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { // Run the build r = pakfire_build_exec(build, argv[i]); if (r) { diff --git a/src/cli/lib/command.c b/src/cli/lib/command.c index 6fa3f7bf1..81b46a461 100644 --- a/src/cli/lib/command.c +++ b/src/cli/lib/command.c @@ -19,16 +19,13 @@ #############################################################################*/ #include -#include +#include #include #include #include "command.h" -// Do not let getopt_long print any error messages -int opterr = 0; - static const struct command* command_find(const struct command* commands, const char* verb) { for (const struct command* command = commands; command->verb; command++) { if (strcmp(command->verb, verb) == 0) @@ -38,15 +35,15 @@ static const struct command* command_find(const struct command* commands, const return NULL; } -int command_dispatch(struct pakfire* pakfire, const struct command* commands, int argc, char* argv[]) { +int command_dispatch(struct pakfire* pakfire, const struct command* commands, + int arg_index, int argc, char* argv[]) { const struct command* command = NULL; - if (optind >= argc) { - fprintf(stderr, "Command required\n"); - return -EINVAL; - } + const char* verb = argv[arg_index - 1]; - const char* verb = argv[optind]; + // Shift + argc -= arg_index; + argv += arg_index; // Find a matching command command = command_find(commands, verb); @@ -55,8 +52,5 @@ int command_dispatch(struct pakfire* pakfire, const struct command* commands, in return -EINVAL; } - // Reset optind (to parse everything again) - optind = 0; - return command->callback(pakfire, argc, argv); } diff --git a/src/cli/lib/command.h b/src/cli/lib/command.h index 1f7046d06..5caa7d0dd 100644 --- a/src/cli/lib/command.h +++ b/src/cli/lib/command.h @@ -29,6 +29,7 @@ struct command { int (*callback)(struct pakfire* pakfire, int argc, char* argv[]); }; -int command_dispatch(struct pakfire* pakfire, const struct command* commands, int argc, char* argv[]); +int command_dispatch(struct pakfire* pakfire, const struct command* commands, + int arg_index, int argc, char* argv[]); #endif /* PAKFIRE_CLI_COMMAND_H */ diff --git a/src/cli/lib/dist.c b/src/cli/lib/dist.c index c78f2a302..e4ade4cfd 100644 --- a/src/cli/lib/dist.c +++ b/src/cli/lib/dist.c @@ -90,7 +90,7 @@ int cli_dist(struct pakfire* pakfire, int argc, char* argv[]) { config.resultdir = getcwd(cwd, sizeof(cwd)); // Run for all arguments - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { r = pakfire_dist(pakfire, argv[i], config.resultdir, NULL); if (r) break; diff --git a/src/cli/lib/image.c b/src/cli/lib/image.c index cdc44bd93..79d050c9b 100644 --- a/src/cli/lib/image.c +++ b/src/cli/lib/image.c @@ -30,5 +30,5 @@ int cli_image(struct pakfire* pakfire, int argc, char* argv[]) { { NULL }, }; - return command_dispatch(pakfire, commands, argc, argv); + return command_dispatch(pakfire, commands, 1, argc, argv); } diff --git a/src/cli/lib/image_create.c b/src/cli/lib/image_create.c index 58cc97594..205255bdd 100644 --- a/src/cli/lib/image_create.c +++ b/src/cli/lib/image_create.c @@ -77,11 +77,6 @@ int cli_image_create(struct pakfire* pakfire, int argc, char* argv[]) { // XXX check arguments - if (optind >= argc) { - fprintf(stderr, "Not enough arguments\n"); - return 1; - } - f = fopen(argv[2], "w"); if (!f) { fprintf(stderr, "Could not open %s: %m\n", argv[2]); diff --git a/src/cli/lib/info.c b/src/cli/lib/info.c index 85079c7a9..41c183aef 100644 --- a/src/cli/lib/info.c +++ b/src/cli/lib/info.c @@ -107,7 +107,7 @@ int cli_info(struct pakfire* pakfire, int argc, char* argv[]) { goto ERROR; // Perform search - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { r = pakfire_search(pakfire, argv[i], PAKFIRE_SEARCH_NAME_ONLY, list); if (r) goto ERROR; diff --git a/src/cli/lib/install.c b/src/cli/lib/install.c index b8fc4a8f1..e00457125 100644 --- a/src/cli/lib/install.c +++ b/src/cli/lib/install.c @@ -106,7 +106,7 @@ static int __cli_install(struct pakfire_transaction* transaction, int argc, char int r; // Add the remaining command line options as packages - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { r = pakfire_transaction_request(transaction, PAKFIRE_JOB_INSTALL, argv[i], config->job_flags); if (r) { diff --git a/src/cli/lib/provides.c b/src/cli/lib/provides.c index 9afe350ea..0552be37f 100644 --- a/src/cli/lib/provides.c +++ b/src/cli/lib/provides.c @@ -33,7 +33,7 @@ int cli_provides(struct pakfire* pakfire, int argc, char* argv[]) { goto ERROR; // Perform search - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { r = pakfire_whatprovides(pakfire, argv[i], 0, list); if (r) goto ERROR; diff --git a/src/cli/lib/remove.c b/src/cli/lib/remove.c index 8d86f8b1e..4b08ca13f 100644 --- a/src/cli/lib/remove.c +++ b/src/cli/lib/remove.c @@ -85,7 +85,7 @@ static int __cli_remove(struct pakfire_transaction* transaction, int argc, char* int r; // Add the remaining command line options as packages - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { r = pakfire_transaction_request(transaction, PAKFIRE_JOB_ERASE, argv[i], config->job_flags); if (r) { diff --git a/src/cli/lib/repo.c b/src/cli/lib/repo.c index b594fa050..6caff547f 100644 --- a/src/cli/lib/repo.c +++ b/src/cli/lib/repo.c @@ -30,5 +30,5 @@ int cli_repo(struct pakfire* pakfire, int argc, char* argv[]) { { NULL }, }; - return command_dispatch(pakfire, commands, argc, argv); + return command_dispatch(pakfire, commands, 1, argc, argv); } diff --git a/src/cli/lib/repo_compose.c b/src/cli/lib/repo_compose.c index 464920f9a..a824c30ba 100644 --- a/src/cli/lib/repo_compose.c +++ b/src/cli/lib/repo_compose.c @@ -124,11 +124,6 @@ int cli_repo_compose(struct pakfire* pakfire, int argc, char* argv[]) { // XXX check if we have enough arguments - if (optind >= argc) { - fprintf(stderr, "Not enough arguments\n"); - return 1; - } - // Set path config.path = argv[optind++]; argc--; config.files = argv + optind; diff --git a/src/cli/lib/requires.c b/src/cli/lib/requires.c index 7cd23dedd..3a1aaf7c2 100644 --- a/src/cli/lib/requires.c +++ b/src/cli/lib/requires.c @@ -33,7 +33,7 @@ int cli_requires(struct pakfire* pakfire, int argc, char* argv[]) { goto ERROR; // Perform search - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { r = pakfire_whatrequires(pakfire, argv[i], 0, list); if (r) goto ERROR; diff --git a/src/cli/lib/search.c b/src/cli/lib/search.c index 5119ccc80..faf6adb9c 100644 --- a/src/cli/lib/search.c +++ b/src/cli/lib/search.c @@ -33,7 +33,7 @@ int cli_search(struct pakfire* pakfire, int argc, char* argv[]) { goto ERROR; // Perform search - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { r = pakfire_search(pakfire, argv[i], 0, list); if (r) goto ERROR; diff --git a/src/cli/lib/update.c b/src/cli/lib/update.c index a3fef3397..b4a8dbb85 100644 --- a/src/cli/lib/update.c +++ b/src/cli/lib/update.c @@ -114,9 +114,9 @@ static int __cli_update(struct pakfire_transaction* transaction, int argc, char* } // Did the user pass any packages? - if (optind < argc) { + if (argc) { // Add the remaining command line options as packages - for (int i = optind + 1; i < argc; i++) { + for (int i = 0; i < argc; i++) { r = pakfire_transaction_request(transaction, PAKFIRE_JOB_UPDATE, argv[i], 0); if (r) { fprintf(stderr, "Could not find '%s': %m\n", argv[i]); diff --git a/src/cli/pakfire-builder.c b/src/cli/pakfire-builder.c index 5888fd60d..5e3f8f55c 100644 --- a/src/cli/pakfire-builder.c +++ b/src/cli/pakfire-builder.c @@ -18,8 +18,7 @@ # # #############################################################################*/ -#include -#include +#include #include #include #include @@ -59,117 +58,94 @@ struct config { unsigned int num_disable_repos; }; -static int cli_main(struct pakfire* pakfire, int argc, char* argv[]) { - static const struct command commands[] = { - { "build", 0, cli_build }, - { "clean", 0, cli_clean }, - { "dist", 0, cli_dist }, - { "image", 0, cli_image }, - { "info", 0, cli_info }, - { "provides", 0, cli_provides }, - { "repo", 0, cli_repo }, - { "repolist", 0, cli_repolist }, - { "requires", 0, cli_requires }, - { "shell", 0, cli_shell }, - { "search", 0, cli_search }, - { NULL }, - }; - - return command_dispatch(pakfire, commands, argc, argv); -} +const char* argp_program_version = PACKAGE_VERSION; -static void help(void) { - printf( - "%s [OPTIONS...] COMMAND\n\n" - "Options:\n" - " -a --arch Run in different architecture\n" - " --distro Selects the distribution to compile for\n" - " --debug Run in debug mode\n" - " -h --help Show help\n" - " --version Show version\n" - "\n" - "Commands:\n" - " search Search for packages\n" - " provides Shows which package provides a dependency\n" - " requires Shows which package requries a dependency\n" - " repolist Show all configured repositories\n" - " clean Cleans up no longer needed resources\n", - program_invocation_short_name - ); - - exit(0); -} +enum { + OPT_ARCH = 1, + OPT_DEBUG = 2, + OPT_DISTRO = 3, -static int parse_argv(struct config* config, int argc, char* argv[]) { - enum { - ARG_DEBUG, - ARG_DISTRO, - ARG_VERSION, + OPT_ENABLE_REPO = 4, + OPT_DISABLE_REPO = 5, +}; - // Repos - ARG_ENABLE_REPO, - ARG_DISABLE_REPO, - }; +static struct argp_option options[] = { + { "arch", OPT_ARCH, "ARCH", 0, "Choose an architecture", 0 }, + { "debug", OPT_DEBUG, NULL, 0, "Run in debug mode", 0 }, + { "distro", OPT_DISTRO, "DISTRO", 0, "Build for this distribution", 0 }, + { NULL }, +}; - static const struct option options[] = { - { "arch", required_argument, NULL, 'a' }, - { "debug", no_argument, NULL, ARG_DEBUG }, - { "distro", required_argument, NULL, ARG_DISTRO }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - - // Repos - { "enable-repo", required_argument, NULL, ARG_ENABLE_REPO }, - { "disable-repo", required_argument, NULL, ARG_DISABLE_REPO }, - { NULL }, - }; - int c; +static const struct command commands[] = { + { "build", 0, cli_build }, + { "clean", 0, cli_clean }, + { "dist", 0, cli_dist }, + { "image", 0, cli_image }, + { "info", 0, cli_info }, + { "provides", 0, cli_provides }, + { "repo", 0, cli_repo }, + { "repolist", 0, cli_repolist }, + { "requires", 0, cli_requires }, + { "shell", 0, cli_shell }, + { "search", 0, cli_search }, + { NULL }, +}; - for (;;) { - c = getopt_long(argc, argv, "ah", options, NULL); - if (c < 0) +const char* doc = + "build [OPTIONS...] MAKEFILES...\n" + "clean\n" + "dist MAKEFILES...\n" + "image ...\n" + "provides PATTERN\n" + "repo compose PATH PACKAGES...\n" + "repolist\n" + "requires PATTERN\n" + "shell [OPTIONS...]\n" + "search [OPTIONS...] PATTERN"; + +static error_t parse(int key, char* arg, struct argp_state* state) { + struct config* config = state->input; + + switch (key) { + case OPT_ARCH: + // XXX Check if the architecture is supported + config->arch = arg; break; - switch (c) { - case 'a': - config->arch = optarg; - break; - - case 'h': - help(); + case OPT_DEBUG: + config->flags |= PAKFIRE_FLAGS_DEBUG; + break; - case ARG_DEBUG: - config->flags |= PAKFIRE_FLAGS_DEBUG; - break; + case OPT_DISTRO: + config->distro = arg; + break; - case ARG_DISTRO: - config->distro = optarg; - break; + // Enable/Disable Repositories - case ARG_VERSION: - cli_version(); - exit(0); + case OPT_ENABLE_REPO: + if (config->num_enable_repos >= MAX_REPOS) + return -ENOBUFS; - case ARG_ENABLE_REPO: - if (config->num_enable_repos >= MAX_REPOS) - return -ENOBUFS; + config->enable_repos[config->num_enable_repos++] = arg; + break; - config->enable_repos[config->num_enable_repos++] = optarg; - break; + case OPT_DISABLE_REPO: + if (config->num_disable_repos >= MAX_REPOS) + return -ENOBUFS; - case ARG_DISABLE_REPO: - if (config->num_disable_repos >= MAX_REPOS) - return -ENOBUFS; + config->disable_repos[config->num_disable_repos++] = arg; + break; - config->disable_repos[config->num_disable_repos++] = optarg; - break; + // Show help if no arguments have been passed + case ARGP_KEY_NO_ARGS: + argp_usage(state); - case '?': - break; + // Stop parsing when we find the first argument + case ARGP_KEY_ARG: + break; - default: - assert_unreachable(); - } + default: + return ARGP_ERR_UNKNOWN; } return 0; @@ -257,18 +233,24 @@ ERROR: } int main(int argc, char* argv[]) { - struct pakfire* pakfire = NULL; - int r; - struct config config = { // XXX hard-coded distro .distro = "ipfire3", .arch = NULL, .flags = PAKFIRE_FLAGS_BUILD, }; + struct pakfire* pakfire = NULL; + int r; + + struct argp parser = { + .options = options, + .parser = parse, + .args_doc = doc, + }; + int arg_index = 0; - // Parse command line arguments - r = parse_argv(&config, argc, argv); + // Parse command line options + r = argp_parse(&parser, argc, argv, 0, &arg_index, &config); if (r) goto ERROR; @@ -278,7 +260,7 @@ int main(int argc, char* argv[]) { goto ERROR; // Run a command - r = cli_main(pakfire, argc, argv); + r = command_dispatch(pakfire, commands, arg_index - 1, argc, argv); ERROR: if (pakfire)