# from [76426a7e832c27b6d305e07899f0029f6ac93b4d]
# to [4ec223067e7788e1fcba8bf6fff383c7fceee3ce]
#
# patch "sanity.cc"
# from [037dbbf47c45e082f21ef1ab46401721f474037b]
# to [6ea2aba0cee2ff0feaf6eacb93327e340fcc982b]
#
# old_revision [c6e6127af40ffbf3b22328502bbd191b36619d9f]
#
# patch "NEWS"
# from [932917f030a51f7103624d6f166b07e29bc01ba9]
# to [e2a0ba136a8de81edae9c558e68162a6021f0858]
#
# patch "option.cc"
# from [9d9d1a6485da53fdf268ed04955fc564c2276bcb]
# to [4ec223067e7788e1fcba8bf6fff383c7fceee3ce]
---
+++
@@ -0,0 +1,622 @@
+// Copyright (C) 2006 Timothy Brownawell
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#include "base.hh"
+#include "file_io.hh"
+#include "option.hh"
+#include "sanity.hh"
+
+using std::map;
+using std::pair;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace option {
+
+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, arg_type const & arg)
+ : option_error((F("bad argument '%s' to option '%s'") % arg() % opt).str())
+{}
+
+bad_arg::bad_arg(std::string const & opt,
+ arg_type 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)
+{}
+
+
+
+void splitname(char const * f, string & name, string & n, string & cancel)
+{
+ string from(f);
+ if (from.find("/") != string::npos)
+ {
+ string::size_type slash = from.find("/");
+ cancel = from.substr(slash+1);
+ from.erase(slash);
+ }
+ // from looks like "foo" or "foo,f"
+ string::size_type comma = from.find(',');
+ name = from.substr(0, comma);
+ if (comma != string::npos)
+ 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(char const * names,
+ char const * desc,
+ bool arg,
+ boost::function set,
+ boost::function reset,
+ bool hide,
+ char const * deprecate)
+{
+ description = desc;
+ splitname(names, longname, shortname, cancelname);
+ I((desc && desc[0]) || !longname.empty() || !shortname.empty());
+ // not sure how to display if it can only be reset (and what would that mean?)
+ I((!longname.empty() || !shortname.empty()) || cancelname.empty());
+ // If an option has a name (ie, can be set), it must have a setter function
+ I(set || (longname.empty() && shortname.empty()));
+ // If an option can be canceled, it must have a resetter function
+ I(reset || cancelname.empty());
+ has_arg = arg;
+ setter = set;
+ resetter = reset;
+ hidden = hide;
+ deprecated = deprecate;
+}
+
+bool concrete_option::operator<(concrete_option const & other) const
+{
+ if (longname != other.longname)
+ return longname < other.longname;
+ if (shortname != other.shortname)
+ return shortname < other.shortname;
+ if (cancelname != other.cancelname)
+ return cancelname < other.cancelname;
+ 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
+{
+ boost::function functor;
+ public:
+ discard_argument(boost::function const & from)
+ : functor(from)
+ {}
+ void operator()(std::string const &)
+ { return functor(); }
+};
+
+concrete_option_set &
+concrete_option_set::operator()(char const * names,
+ char const * desc,
+ boost::function set,
+ boost::function reset,
+ bool hide,
+ char const * deprecate)
+{
+ options.insert(concrete_option(names, desc, false, discard_argument(set),
+ reset, hide, deprecate));
+ return *this;
+}
+
+concrete_option_set &
+concrete_option_set::operator()(char const * names,
+ char const * desc,
+ boost::function set,
+ boost::function reset,
+ bool hide,
+ char const * deprecate)
+{
+ options.insert(concrete_option(names, desc, true, set, reset, hide, deprecate));
+ return *this;
+}
+
+concrete_option_set
+concrete_option_set::operator | (concrete_option_set const & other) const
+{
+ concrete_option_set combined;
+ std::set_union(options.begin(), options.end(),
+ other.options.begin(), other.options.end(),
+ std::inserter(combined.options, combined.options.begin()));
+ return combined;
+}
+
+void concrete_option_set::reset() const
+{
+ for (std::set::const_iterator i = options.begin();
+ i != options.end(); ++i)
+ {
+ if (i->resetter)
+ i->resetter();
+ }
+}
+
+static void
+tokenize_for_command_line(string const & from, args_vector & to)
+{
+ // Unfortunately, the tokenizer in basic_io is too format-specific
+ to.clear();
+ enum quote_type {none, one, two};
+ string cur;
+ quote_type type = none;
+ bool have_tok(false);
+
+ for (string::const_iterator i = from.begin(); i != from.end(); ++i)
+ {
+ if (*i == '\'')
+ {
+ if (type == none)
+ type = one;
+ else if (type == one)
+ type = none;
+ else
+ {
+ cur += *i;
+ have_tok = true;
+ }
+ }
+ else if (*i == '"')
+ {
+ if (type == none)
+ type = two;
+ else if (type == two)
+ type = none;
+ else
+ {
+ cur += *i;
+ have_tok = true;
+ }
+ }
+ else if (*i == '\\')
+ {
+ if (type != one)
+ ++i;
+ E(i != from.end(), origin::user, F("Invalid escape in --xargs file"));
+ cur += *i;
+ have_tok = true;
+ }
+ else if (string(" \n\t").find(*i) != string::npos)
+ {
+ if (type == none)
+ {
+ if (have_tok)
+ to.push_back(arg_type(cur, origin::user));
+ cur.clear();
+ have_tok = false;
+ }
+ else
+ {
+ cur += *i;
+ have_tok = true;
+ }
+ }
+ else
+ {
+ cur += *i;
+ have_tok = true;
+ }
+ }
+ if (have_tok)
+ to.push_back(arg_type(cur, origin::user));
+}
+
+void concrete_option_set::from_command_line(int argc,
+ char const * const * argv)
+{
+ args_vector arguments;
+ for (int i = 1; i < argc; ++i)
+ arguments.push_back(arg_type(argv[i], origin::user));
+ from_command_line(arguments);
+}
+
+static concrete_option const &
+getopt(map const & by_name, string & name)
+{
+ // try to match the option name as a whole first, so if the user
+ // specified "--foo" and we have "--foo" and "--foo-bar", don't
+ // display both choices
+ map::const_iterator i = by_name.find(name);
+ if (i != by_name.end())
+ return i->second;
+
+ if (name.size() == 0)
+ throw unknown_option(name);
+
+ // try to find the option by partial name
+ vector candidates;
+ for (i = by_name.begin(); i != by_name.end(); ++i)
+ {
+ if (i->first.find(name) == 0)
+ candidates.push_back(i->first);
+ }
+
+ if (candidates.size() == 0)
+ throw unknown_option(name);
+
+ if (candidates.size() == 1)
+ {
+ i = by_name.find(candidates[0]);
+ I(i != by_name.end());
+ L(FL("expanding option '%s' to '%s'") % name % candidates[0]);
+ name = candidates[0];
+ return i->second;
+ }
+
+ string err = (F("option '%s' has multiple ambiguous expansions:")
+ % name).str();
+
+ for (vector::const_iterator j = candidates.begin();
+ j != candidates.end(); ++j)
+ {
+ i = by_name.find(*j);
+ I(i != by_name.end());
+
+ if (*j == "--")
+ continue;
+
+ if (*j == i->second.shortname)
+ err += "\n-" + *j + " (" + i->second.description + ")";
+ else if (*j == i->second.cancelname)
+ err += "\n--" + *j + " (" + (F("negation of --%s") % i->second.longname).str() + ")";
+ else
+ err += "\n--" + *j + " (" + i->second.description + ")";
+ }
+
+ E(false, origin::user, i18n_format(err));
+}
+
+// helper for get_by_name
+// Make sure that either:
+// * There are no duplicate options, or
+// * If we're only parsing options (and not applying them), any duplicates
+// are consistent WRT whether they take an option
+typedef pair