# # # patch "cmd.hh" # from [cead5ed02a100c1eb58e54d7b8fec151558e98cf] # to [f3ce02e4693b31c83b96a8f573d9650456484783] # # patch "cmd_diff_log.cc" # from [1d2faaa4a2ec5db9fb58ea9c3633c4bfb32c7ff1] # to [766a76697c3022279965f4cb07984e31f514ca9b] # # patch "cmd_files.cc" # from [17a12906b2273a4befe7c3abce3620f82ce3a240] # to [12243e3b47340b2aa81cd8e26f40559fad0ab51d] # # patch "cmd_list.cc" # from [5c13ba291ec50d8ffccb2452076a6f1f2b7a85b8] # to [c806e8166080cea95a0fde0b7a2f143146369054] # # patch "cmd_merging.cc" # from [77ea3cfa91dd9467a850ca55244a3269c0276ac7] # to [f578cdba90e5b74d001e48bab8db95870c04fce9] # # patch "cmd_netsync.cc" # from [da73bea1f8913845ea04b53831c264f2895f1e2c] # to [32269e67f1a25f2f83643c80f28f8c4359d97aa9] # # patch "cmd_ws_commit.cc" # from [dc49953a038a915f99272fe0cbaa4cdf9c0ccbf8] # to [9096bec69ecf834fabaa72aea221d5d6d4204909] # # patch "monotone.cc" # from [16de226e91faac3897a8648d970499332195ca90] # to [9269ce199b4ac630d0f6d53ea272df7fe2787da9] # # patch "option.cc" # from [435ba9737c3a0f316d1a17ac96770c21bc8f1390] # to [50544b89a852d3b6c03a12c892797317d28f218e] # # patch "option.hh" # from [e9bf1b5a9fc5e9e4fd8b621cd88ef37f79bf68c6] # to [2c947db8d2d4182ebdb3a157b31e8271848a29cc] # # patch "unit_tests.cc" # from [6bdf8e577c7d3908b25a6d3b45a894a5f5b2529b] # to [5f4ecf5b5eb1281973649b1cfa39077880d5e422] # ============================================================ --- cmd.hh cead5ed02a100c1eb58e54d7b8fec151558e98cf +++ cmd.hh f3ce02e4693b31c83b96a8f573d9650456484783 @@ -123,7 +123,7 @@ namespace commands { struct cmd_ ## C : public command \ { \ cmd_ ## C() : command(#C, group, params, desc, true, \ - options::options_type() % opts) \ + options::options_type() | opts) \ {} \ virtual void exec(app_state & app, \ std::vector const & args); \ @@ -141,7 +141,7 @@ namespace commands { struct cmd_ ## C : public command \ { \ cmd_ ## C() : command(#C, group, "", desc, true, \ - options::options_type() % opts) \ + options::options_type() | opts) \ {} \ virtual void exec(app_state & app, \ std::vector const & args); \ @@ -160,7 +160,7 @@ namespace commands { struct cmd_ ## C : public command \ { \ cmd_ ## C() : command(#C, group, params, desc, false, \ - options::options_type() % opts) \ + options::options_type() | opts) \ {} \ virtual void exec(app_state & app, \ std::vector const & args); \ ============================================================ --- cmd_diff_log.cc 1d2faaa4a2ec5db9fb58ea9c3633c4bfb32c7ff1 +++ cmd_diff_log.cc 766a76697c3022279965f4cb07984e31f514ca9b @@ -333,8 +333,8 @@ CMD(diff, N_("informative"), N_("[PATH]. "If one revision is given, the diff between the workspace and\n" "that revision is shown. If two revisions are given, the diff between\n" "them is given. If no format is specified, unified is used by default."), - options::opts::revision % options::opts::depth % options::opts::exclude - % options::opts::diff_options) + options::opts::revision | options::opts::depth | options::opts::exclude + | options::opts::diff_options) { bool new_is_archived; ostringstream header; @@ -541,8 +541,8 @@ CMD(log, N_("informative"), N_("[FILE] . CMD(log, N_("informative"), N_("[FILE] ..."), N_("print history in reverse order (filtering by 'FILE'). If one or more\n" "revisions are given, use them as a starting point."), - options::opts::last % options::opts::next % options::opts::revision % options::opts::brief - % options::opts::diffs % options::opts::no_merges % options::opts::no_files) + options::opts::last | options::opts::next | options::opts::revision | options::opts::brief + | options::opts::diffs | options::opts::no_merges | options::opts::no_files) { if (app.opts.revision_selectors.size() == 0) app.require_workspace("try passing a --revision to start at"); ============================================================ --- cmd_files.cc 17a12906b2273a4befe7c3abce3620f82ce3a240 +++ cmd_files.cc 12243e3b47340b2aa81cd8e26f40559fad0ab51d @@ -113,7 +113,7 @@ CMD(annotate, N_("informative"), N_("PAT CMD(annotate, N_("informative"), N_("PATH"), N_("print annotated copy of the file from REVISION"), - options::opts::revision % options::opts::brief) + options::opts::revision | options::opts::brief) { revision_id rid; ============================================================ --- cmd_list.cc 5c13ba291ec50d8ffccb2452076a6f1f2b7a85b8 +++ cmd_list.cc c806e8166080cea95a0fde0b7a2f143146369054 @@ -470,7 +470,7 @@ CMD(list, N_("informative"), N_("show database objects, or the current workspace manifest, \n" "or known, unknown, intentionally ignored, missing, or \n" "changed-state files"), - options::opts::depth % options::opts::exclude) + options::opts::depth | options::opts::exclude) { if (args.size() == 0) throw usage(name); ============================================================ --- cmd_merging.cc 77ea3cfa91dd9467a850ca55244a3269c0276ac7 +++ cmd_merging.cc f578cdba90e5b74d001e48bab8db95870c04fce9 @@ -76,7 +76,7 @@ CMD(update, N_("workspace"), "", "different revision, preserving uncommitted changes as it does so.\n" "If a revision is given, update the workspace to that revision.\n" "If not, update the workspace to the head of the branch."), - options::opts::branch % options::opts::revision) + options::opts::branch | options::opts::revision) { if (args.size() > 0) throw usage(name); @@ -318,7 +318,7 @@ CMD(merge, N_("tree"), "", N_("merge unm // (Possibility: append the --message/--message-file text to the synthetic // log message constructed in merge_two().) CMD(merge, N_("tree"), "", N_("merge unmerged heads of branch"), - options::opts::branch % options::opts::date % options::opts::author) + options::opts::branch | options::opts::date | options::opts::author) { typedef std::pair revpair; typedef set::const_iterator rid_set_iter; @@ -418,7 +418,7 @@ CMD(propagate, N_("tree"), N_("SOURCE-BR CMD(propagate, N_("tree"), N_("SOURCE-BRANCH DEST-BRANCH"), N_("merge from one branch to another asymmetrically"), - options::opts::date % options::opts::author % options::opts::message % options::opts::msgfile) + options::opts::date | options::opts::author | options::opts::message | options::opts::msgfile) { if (args.size() != 2) throw usage(name); @@ -429,7 +429,7 @@ CMD(merge_into_dir, N_("tree"), N_("SOUR CMD(merge_into_dir, N_("tree"), N_("SOURCE-BRANCH DEST-BRANCH DIR"), N_("merge one branch into a subdirectory in another branch"), - options::opts::date % options::opts::author % options::opts::message % options::opts::msgfile) + options::opts::date | options::opts::author | options::opts::message | options::opts::msgfile) { // This is a special merge operator, but very useful for people // maintaining "slightly disparate but related" trees. It does a one-way @@ -589,7 +589,7 @@ CMD(explicit_merge, N_("tree"), N_("LEFT-REVISION RIGHT-REVISION DEST-BRANCH"), N_("merge two explicitly given revisions, " "placing result in given branch"), - options::opts::date % options::opts::author) + options::opts::date | options::opts::author) { revision_id left, right; string branch; @@ -614,7 +614,7 @@ CMD(show_conflicts, N_("informative"), N CMD(show_conflicts, N_("informative"), N_("REV REV"), N_("Show what conflicts would need to be resolved " "to merge the given revisions."), - options::opts::branch % options::opts::date % options::opts::author) + options::opts::branch | options::opts::date | options::opts::author) { if (args.size() != 2) throw usage(name); @@ -665,7 +665,7 @@ CMD(pluck, N_("workspace"), N_("[-r FROM "\n" "If two revisions are given, applies the changes made to get from the\n" "first revision to the second."), - options::opts::revision % options::opts::depth % options::opts::exclude) + options::opts::revision | options::opts::depth | options::opts::exclude) { // Work out our arguments revision_id from_rid, to_rid; ============================================================ --- cmd_netsync.cc da73bea1f8913845ea04b53831c264f2895f1e2c +++ cmd_netsync.cc 32269e67f1a25f2f83643c80f28f8c4359d97aa9 @@ -118,7 +118,7 @@ CMD(push, N_("network"), N_("[ADDRESS[:P CMD(push, N_("network"), N_("[ADDRESS[:PORTNUMBER] [PATTERN]]"), N_("push branches matching PATTERN to netsync server at ADDRESS"), - options::opts::set_default % options::opts::exclude % options::opts::key_to_push) + options::opts::set_default | options::opts::exclude | options::opts::key_to_push) { utf8 addr, include_pattern, exclude_pattern; process_netsync_args(name, args, addr, include_pattern, exclude_pattern, @@ -131,7 +131,7 @@ CMD(pull, N_("network"), N_("[ADDRESS[:P CMD(pull, N_("network"), N_("[ADDRESS[:PORTNUMBER] [PATTERN]]"), N_("pull branches matching PATTERN from netsync server at ADDRESS"), - options::opts::set_default % options::opts::exclude) + options::opts::set_default | options::opts::exclude) { utf8 addr, include_pattern, exclude_pattern; process_netsync_args(name, args, addr, include_pattern, exclude_pattern, @@ -146,7 +146,7 @@ CMD(sync, N_("network"), N_("[ADDRESS[:P CMD(sync, N_("network"), N_("[ADDRESS[:PORTNUMBER] [PATTERN]]"), N_("sync branches matching PATTERN with netsync server at ADDRESS"), - options::opts::set_default % options::opts::exclude % options::opts::key_to_push) + options::opts::set_default | options::opts::exclude | options::opts::key_to_push) { utf8 addr, include_pattern, exclude_pattern; process_netsync_args(name, args, addr, include_pattern, exclude_pattern, @@ -190,8 +190,8 @@ CMD_NO_WORKSPACE(serve, N_("network"), N CMD_NO_WORKSPACE(serve, N_("network"), N_("PATTERN ..."), N_("serve the branches specified by PATTERNs to connecting clients"), - options::opts::bind % options::opts::pidfile % options::opts::exclude % - options::opts::bind_stdio % options::opts::no_transport_auth) + options::opts::bind | options::opts::pidfile | options::opts::exclude | + options::opts::bind_stdio | options::opts::no_transport_auth) { if (args.size() < 1) throw usage(name); ============================================================ --- cmd_ws_commit.cc dc49953a038a915f99272fe0cbaa4cdf9c0ccbf8 +++ cmd_ws_commit.cc 9096bec69ecf834fabaa72aea221d5d6d4204909 @@ -65,7 +65,7 @@ CMD(revert, N_("workspace"), N_("[PATH]. CMD(revert, N_("workspace"), N_("[PATH]..."), N_("revert file(s), dir(s) or entire workspace (\".\")"), - options::opts::depth % options::opts::exclude % options::opts::missing) + options::opts::depth | options::opts::exclude | options::opts::missing) { temp_node_id_source nis; roster_t old_roster, new_roster; @@ -275,7 +275,7 @@ CMD(drop, N_("workspace"), N_("[PATH]... CMD(drop, N_("workspace"), N_("[PATH]..."), N_("drop files from workspace"), - options::opts::execute % options::opts::missing % options::opts::recursive) + options::opts::execute | options::opts::missing | options::opts::recursive) { if (!app.opts.missing && (args.size() < 1)) throw usage(name); @@ -354,7 +354,7 @@ CMD(status, N_("informative"), N_("[PATH } CMD(status, N_("informative"), N_("[PATH]..."), N_("show status of workspace"), - options::opts::depth % options::opts::exclude) + options::opts::depth | options::opts::exclude) { roster_t old_roster, new_roster; cset included, excluded; @@ -424,7 +424,7 @@ CMD(checkout, N_("tree"), N_("[DIRECTORY "If a revision is given, that's the one that will be checked out.\n" "Otherwise, it will be the head of the branch (given or implicit).\n" "If no directory is given, the branch name will be used as directory"), - options::opts::branch % options::opts::revision) + options::opts::branch | options::opts::revision) { revision_id ident; system_path dir; @@ -661,8 +661,8 @@ CMD(commit, N_("workspace"), N_("[PATH]. CMD(commit, N_("workspace"), N_("[PATH]..."), N_("commit workspace to database"), - options::opts::branch % options::opts::message % options::opts::msgfile % options::opts::date - % options::opts::author % options::opts::depth % options::opts::exclude) + options::opts::branch | options::opts::message | options::opts::msgfile | options::opts::date + | options::opts::author | options::opts::depth | options::opts::exclude) { utf8 log_message(""); bool log_message_given; ============================================================ --- monotone.cc 16de226e91faac3897a8648d970499332195ca90 +++ monotone.cc 9269ce199b4ac630d0f6d53ea272df7fe2787da9 @@ -128,25 +128,26 @@ string read_options(options & opts, vect // read command-line options and return the command name string read_options(options & opts, vector args) { - option::concrete_option_set optset = options::opts::all_options().instantiate(&opts); + option::concrete_option_set optset = + options::opts::all_options().instantiate(&opts); optset.from_command_line(args); // consume the command, and perform completion if necessary string cmd; if (!opts.args.empty()) cmd = commands::complete_command(idx(opts.args, 0)()); + optset.reset(); - options::options_type cmdopts = commands::command_options(cmd); - optset = options::opts::globals().instantiate(&opts) % cmdopts.instantiate(&opts); - // reparse options, now that we know what command-specific // options are allowed. - opts = options(); + options::options_type cmdopts = commands::command_options(cmd); + optset = (options::opts::globals() | cmdopts).instantiate(&opts); optset.from_command_line(args, false); if (!opts.args.empty()) opts.args.erase(opts.args.begin()); + return cmd; } ============================================================ --- option.cc 435ba9737c3a0f316d1a17ac96770c21bc8f1390 +++ option.cc 50544b89a852d3b6c03a12c892797317d28f218e @@ -15,24 +15,30 @@ option_error::option_error(std::string c option_error::option_error(std::string const & str) : std::invalid_argument((F("option error: %s") % str).str()) {} + unknown_option::unknown_option(std::string const & opt) : option_error((F("unknown option '%s'") % opt).str()) {} + missing_arg::missing_arg(std::string const & opt) : option_error((F("missing argument to option '%s'") % opt).str()) {} + extra_arg::extra_arg(std::string const & opt) : option_error((F("option '%s' does not take an argument") % opt).str()) {} + bad_arg::bad_arg(std::string const & opt, std::string const & arg) : option_error((F("bad argument '%s' to option '%s'") % arg % opt).str()) {} + bad_arg::bad_arg(std::string const & opt, std::string const & arg, std::string const & reason) : option_error((F("bad argument '%s' to option '%s': %s") % arg % opt % reason).str()) {} + bad_arg_internal::bad_arg_internal(string const & str) : reason(str) {} @@ -48,12 +54,22 @@ void splitname(string const & from, stri n = from.substr(comma+1, 1); else n = ""; + + // "o" is equivalent to ",o"; it gives an option + // with only a short name + if (name.size() == 1) + { + I(n.empty()); + n = name; + name = ""; + } } concrete_option::concrete_option() : has_arg(false) {} + concrete_option::concrete_option(std::string const & names, std::string const & desc, bool arg, @@ -62,6 +78,7 @@ concrete_option::concrete_option(std::st { description = desc; splitname(names, longname, shortname); + I(!description.empty() || !longname.empty() || !shortname.empty()); has_arg = arg; setter = set; resetter = reset; @@ -69,18 +86,32 @@ bool concrete_option::operator<(concrete bool concrete_option::operator<(concrete_option const & other) const { - return longname < other.longname && (shortname.empty() || shortname != other.shortname); + if (longname != other.longname) + return longname < other.longname; + if (shortname != other.shortname) + return shortname < other.shortname; + return description < other.description; } +concrete_option_set +operator | (concrete_option const & a, concrete_option const & b) +{ + return concrete_option_set(a) | b; +} + concrete_option_set::concrete_option_set() {} + concrete_option_set::concrete_option_set(std::set const & other) : options(other) {} + concrete_option_set::concrete_option_set(concrete_option const & opt) { options.insert(opt); } + +// essentially the opposite of std::bind1st class discard_argument : public boost::function { boost::function functor; @@ -91,6 +122,7 @@ class discard_argument : public boost::f void operator()(std::string const &) { return functor(); } }; + concrete_option_set & concrete_option_set::operator()(string const & names, string const & desc, @@ -100,6 +132,7 @@ concrete_option_set::operator()(string c options.insert(concrete_option(names, desc, false, discard_argument(set), reset)); return *this; } + concrete_option_set & concrete_option_set::operator()(string const & names, string const & desc, @@ -109,22 +142,17 @@ concrete_option_set::operator()(string c options.insert(concrete_option(names, desc, true, set, reset)); return *this; } -concrete_option_set & -concrete_option_set::operator % (concrete_option_set const & other) + +concrete_option_set +concrete_option_set::operator | (concrete_option_set const & other) const { - std::set combined; + concrete_option_set combined; std::set_union(options.begin(), options.end(), other.options.begin(), other.options.end(), - std::inserter(combined, combined.begin())); - options = combined; - return *this; + std::inserter(combined.options, combined.options.begin())); + return combined; } -concrete_option_set & -concrete_option_set::operator % (concrete_option const & opt) -{ - options.insert(opt); - return *this; -} + void concrete_option_set::reset() const { for (std::set::const_iterator i = options.begin(); @@ -211,6 +239,7 @@ void concrete_option_set::from_command_l arguments.push_back(argv[i]); from_command_line(arguments, true); } + static concrete_option const & getopt(map const & by_name, string const & name) { @@ -233,6 +262,7 @@ void concrete_option_set::from_command_l if (!i->shortname.empty()) by_name.insert(make_pair(i->shortname, *i)); } + bool seen_dashdash = false; for (unsigned int i = 0; i < args.size(); ++i) { @@ -325,9 +355,7 @@ void concrete_option_set::from_command_l try { if (o.setter) - { - o.setter(arg); - } + o.setter(arg); } catch (boost::bad_lexical_cast) { @@ -365,6 +393,8 @@ static vector wordwrap(string st return out; } +// Get the non-description part of the usage string, +// looks like "--long [ -s ] ". static string usagestr(option::concrete_option const & opt) { string out; @@ -386,8 +416,6 @@ std::string concrete_option_set::get_usa std::string concrete_option_set::get_usage_str() const { - // combine the name strings like "--long [ -s ]" - map to_display; unsigned int namelen = 0; // the longest option name string for (std::set::const_iterator i = options.begin(); i != options.end(); ++i) ============================================================ --- option.hh e9bf1b5a9fc5e9e4fd8b621cd88ef37f79bf68c6 +++ option.hh 2c947db8d2d4182ebdb3a157b31e8271848a29cc @@ -11,6 +11,7 @@ namespace option { #include namespace option { + // Base for errors thrown by this code. struct option_error : public std::invalid_argument { option_error(std::string const & str); @@ -23,10 +24,13 @@ namespace option { { missing_arg(std::string const & opt); }; + // -ofoo or --opt=foo when the option doesn't take an argument struct extra_arg : public option_error { extra_arg(std::string const & opt); }; + // thrown by from_command_line when setting an option fails + // by either boost::bad_lexical_cast or bad_arg_internal struct bad_arg : public option_error { bad_arg(std::string const & opt, std::string const & arg); @@ -34,13 +38,18 @@ namespace option { std::string const & arg, std::string const & reason); }; + // from_command_line() catches this and boost::bad_lexical_cast + // and converts them to bad_arg exceptions struct bad_arg_internal { std::string reason; bad_arg_internal(std::string const & str = ""); }; + // Split a "long,s" option name into long and short names. void splitname(std::string const & from, std::string & name, std::string & n); + + // An option that can be set and reset. struct concrete_option { std::string description; @@ -59,12 +68,18 @@ namespace option { bool operator<(concrete_option const & other) const; }; + + // A group of options, which can be set from a command line + // and can produce a usage string. struct concrete_option_set { std::set options; concrete_option_set(); concrete_option_set(std::set const & other); concrete_option_set(concrete_option const & opt); + + // for building a concret_option_set directly (as done in unit_tests.cc), + // rather than using intermediate machinery like in options* concrete_option_set & operator()(std::string const & names, std::string const & desc, @@ -75,16 +90,19 @@ namespace option { std::string const & desc, boost::function set, boost::function reset() = 0); - concrete_option_set & operator % (concrete_option_set const & other); - concrete_option_set & operator % (concrete_option const & opt); + + concrete_option_set operator | (concrete_option_set const & other) const; void reset() const; std::string get_usage_str() const; void from_command_line(std::vector & args, bool allow_xargs = true); void from_command_line(int argc, char const * const * argv); }; + concrete_option_set + operator | (concrete_option const & a, concrete_option const & b); + // because std::bind1st can't handle producing a nullary functor template - struct binder_only : boost::function + struct binder_only { T * obj; boost::function fun; @@ -101,6 +119,9 @@ namespace option { { return binder_only(f, o); } + + // Options that need to be attached to some other object + // in order for set and reset to be meaningful. template struct option { @@ -116,6 +137,7 @@ namespace option { void(T::*set)(std::string), void(T::*reset)()) { + I(!name.empty() || !desc.empty()); description = desc; names = name; has_arg = arg; @@ -139,9 +161,14 @@ namespace option { bool operator<(option const & other) const { - return names < other.names; + if (names != other.names) + return names < other.names; + return description < other.description; } }; + + // A group of unattached options, which can be given an object + // to attach themselves to. template struct option_set { @@ -171,25 +198,22 @@ namespace option { out.insert(i->instantiate(obj)); return out; } - option_set & operator % (option_set const & other) + option_set operator | (option_set const & other) const { - std::set > combined; + option_set combined; std::set_union(options.begin(), options.end(), other.options.begin(), other.options.end(), - std::inserter(combined, combined.begin())); - options = combined; - return *this; + std::inserter(combined.options, combined.options.begin())); + return combined; } - option_set & operator % (option_set const & (*fun)()) - { - return *this % fun(); - } - option_set & operator % (option_set & (*fun)()) - { - return *this % fun(); - } bool empty() const {return options.empty();} }; + template + option_set + operator | (option const & a, option const & b) + { + return option_set(a) | b; + } } ============================================================ --- unit_tests.cc 6bdf8e577c7d3908b25a6d3b45a894a5f5b2529b +++ unit_tests.cc 5f4ecf5b5eb1281973649b1cfa39077880d5e422 @@ -175,8 +175,8 @@ test_suite * init_unit_test_suite(int ar ("log", "write verbose debug log to this file" " (default is unit_tests.log)", function(var(log) = _1)) - ("", "", function(bind(&vector::push_back, - &tests, _1))); + ("--", "", function(bind(&vector::push_back, + &tests, _1))); os.from_command_line(argc, argv);