From 2c58968b94f348911372e8afb47626d33825757b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 12 Feb 2016 13:39:14 +0200 Subject: Add support for mixing command line options and arguments --- bpkg/bpkg.cli | 18 ++++++---------- bpkg/bpkg.cxx | 68 ++++++++++++++++++++++++++++++++++++++++++---------------- bpkg/buildfile | 10 ++++----- 3 files changed, 61 insertions(+), 35 deletions(-) diff --git a/bpkg/bpkg.cli b/bpkg/bpkg.cli index f299eaf..107ef0a 100644 --- a/bpkg/bpkg.cli +++ b/bpkg/bpkg.cli @@ -26,11 +26,11 @@ namespace bpkg packages, and repositories using a set of commands that are summarized below. - For a detailed description of any command or help topic, use the \cb{help} - command or see the corresponding man page (the man pages have the - \cb{bpkg-} prefix, for example \l{bpkg-help(1)}). Note that the common - options can also be specified as part of the command-specific options, for - convenience." + For a detailed description of any command or help topic, use the + \cb{help} command or see the corresponding man page (the man pages have + the \cb{bpkg-} prefix, for example \l{bpkg-help(1)}). Note also that + and can be specified in any order and + can be specified as part of ." } // For usage it's nice to see the list of commands on the first page. So @@ -152,9 +152,7 @@ namespace bpkg bool cfg-create|create { - "", - "\l{bpkg-cfg-create(1)} \- create configuration", - "" + "\l{bpkg-cfg-create(1)} \- create configuration" } bool cfg-add|add @@ -218,9 +216,7 @@ namespace bpkg bool pkg-fetch { - "/", - "\l{bpkg-pkg-fetch(1)} \- fetch package archive", - "" + "\l{bpkg-pkg-fetch(1)} \- fetch package archive" } bool pkg-unpack diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx index 35fb796..8d4d1fb 100644 --- a/bpkg/bpkg.cxx +++ b/bpkg/bpkg.cxx @@ -2,6 +2,7 @@ // copyright : Copyright (c) 2014-2016 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +#include // strcmp() #include #include @@ -41,18 +42,46 @@ using namespace std; using namespace bpkg; -// Initialize the command option class O with the common options -// and then parse the rest of the arguments. Once this is done, -// use the "final" values of the common options to do global -// initializations (verbosity level, etc). +// Initialize the command option class O with the common options and then +// parse the rest of the command line placing non-option arguments to args. +// Once this is done, use the "final" values of the common options to do +// global initializations (verbosity level, etc). // template static O -parse (const common_options& co, cli::scanner& s) +parse (const common_options& co, cli::scanner& scan, strings& args) { O o; static_cast (o) = co; - o.parse (s); + + // We want to be able to specify options and arguments in any order (it is + // really handy to just add -v at the end of the command line). + // + for (bool opt (true); scan.more (); ) + { + if (opt) + { + // If we see first "--", then we are done parsing options. + // + if (strcmp (scan.peek (), "--") == 0) + { + scan.next (); + opt = false; + continue; + } + + // Parse the next chunk of options until we reach an argument (or eos). + // + o.parse (scan); + + if (!scan.more ()) + break; + + // Fall through. + } + + args.push_back (scan.next ()); + } // Global initializations. // @@ -70,12 +99,12 @@ try { using namespace cli; - argv_file_scanner args (argc, argv, "--options-file"); + argv_file_scanner scan (argc, argv, "--options-file"); // First parse common options and --version/--help. // options o; - o.parse (args, unknown_mode::stop); + o.parse (scan, unknown_mode::stop); if (o.version ()) { @@ -87,19 +116,22 @@ try return 0; } - if (o.help ()) - return help (help_options (), "", nullptr); + strings argsv; // To be filled by parse() above. + vector_scanner args (argsv); const common_options& co (o); + if (o.help ()) + return help (parse (co, scan, argsv), "", nullptr); + // The next argument should be a command. // - if (!args.more ()) + if (!scan.more ()) fail << "bpkg command expected" << info << "run 'bpkg help' for more information"; int cmd_argc (2); - char* cmd_argv[] {argv[0], const_cast (args.next ())}; + char* cmd_argv[] {argv[0], const_cast (scan.next ())}; commands cmd; cmd.parse (cmd_argc, cmd_argv, true, unknown_mode::stop); @@ -116,7 +148,7 @@ try if (h) { - ho = parse (co, args); + ho = parse (co, scan, argsv); if (args.more ()) { @@ -158,9 +190,9 @@ try // if (h) // r = help (ho, "pkg-verify", print_bpkg_pkg_verify_usage); // else - // r = pkg_verify (parse (co, args), args); + // r = pkg_verify (parse (co, scan, argsv), args); // - // return 0; + // break; // } // #define COMMAND_IMPL(NP, SP, CMD) \ @@ -169,7 +201,7 @@ try if (h) \ r = help (ho, SP#CMD, print_bpkg_##NP##CMD##_usage); \ else \ - r = NP##CMD (parse (co, args), args); \ + r = NP##CMD (parse (co, scan, argsv), args); \ \ break; \ } @@ -234,9 +266,7 @@ catch (const failed&) } catch (const cli::exception& e) { - *diag_stream << "error: "; - e.print (*diag_stream); - *diag_stream << endl; + error << e; return 1; } /* diff --git a/bpkg/buildfile b/bpkg/buildfile index 4effcaf..50a68e6 100644 --- a/bpkg/buildfile +++ b/bpkg/buildfile @@ -101,11 +101,11 @@ if! $cli.loaded # Option length must be the same to get commands/topics/options aligned. # -cli.options += -I $src_root --include-with-brackets --include-prefix bpkg \ ---guard-prefix BPKG --cxx-prologue "#include " \ ---cli-namespace bpkg::cli --generate-file-scanner --generate-specifier \ ---generate-parse --ansi-color --page-usage 'bpkg::print_$name$_' \ ---include-base-last --option-length 23 +cli.options += -I $src_root --include-with-brackets --include-prefix bpkg \ +--guard-prefix BPKG --cxx-prologue "#include " \ +--cli-namespace bpkg::cli --generate-vector-scanner --generate-file-scanner \ +--generate-specifier --generate-parse --page-usage 'bpkg::print_$name$_' \ +--ansi-color --include-base-last --option-length 23 cli.cxx{common-options}: cli.options += --short-usage --long-usage # Both. cli.cxx{bpkg-options}: cli.options += --short-usage --suppress-undocumented -- cgit v1.1