--- /dev/null
+Improve error reporting in smart
+
+Add code to check proper command line arguments for various
+smart commands. Exit with error if erroneous/additional arguments
+are given in the command line.
+
+Upstream-Status: Pending
+
+Signed-off-by: Bogdan Marinescu <bogdan.a.marinescu@intel.com>
+
+diff --git a/smart/commands/channel.py b/smart/commands/channel.py
+index aa76f91..63fbb35 100644
+--- a/smart/commands/channel.py
++++ b/smart/commands/channel.py
+@@ -157,7 +157,17 @@ def main(ctrl, opts):
+ opts.show is None and opts.yaml is None):
+ iface.warning(_("Can't edit channels information."))
+ raise Error, _("Configuration is in readonly mode.")
+-
++
++ # Argument check
++ opts.check_args_of_option("set", -1)
++ opts.check_args_of_option("remove", -1)
++ opts.check_args_of_option("edit", 0)
++ opts.check_args_of_option("enable", -1)
++ opts.check_args_of_option("disable", -1)
++ opts.ensure_action("channel", ["add", "set", "remove", "remove-all",
++ "list", "show", "yaml", "enable", "disable"])
++ opts.check_remaining_args()
++
+ if opts.add is not None:
+ if not opts.add and opts.args == ["-"]:
+ newchannels = []
+diff --git a/smart/commands/check.py b/smart/commands/check.py
+index b08608a..506e852 100644
+--- a/smart/commands/check.py
++++ b/smart/commands/check.py
+@@ -72,6 +72,9 @@ def parse_options(argv):
+
+ def main(ctrl, opts, reloadchannels=True):
+
++ # Argument check
++ opts.check_args_of_option("channels", 1)
++
+ if sysconf.get("auto-update"):
+ from smart.commands import update
+ updateopts = update.parse_options([])
+diff --git a/smart/commands/config.py b/smart/commands/config.py
+index dd50dee..4fe4366 100644
+--- a/smart/commands/config.py
++++ b/smart/commands/config.py
+@@ -80,6 +80,12 @@ def main(ctrl, opts):
+ globals["false"] = False
+ globals["no"] = False
+
++ # Check arguments
++ opts.check_args_of_option("set", -1)
++ opts.check_args_of_option("remove", -1)
++ opts.ensure_action("config", ["set", "show", "yaml", "remove"])
++ opts.check_remaining_args()
++
+ if opts.set:
+ for opt in opts.set:
+ m = SETRE.match(opt)
+diff --git a/smart/commands/download.py b/smart/commands/download.py
+index 6837993..b853c61 100644
+--- a/smart/commands/download.py
++++ b/smart/commands/download.py
+@@ -81,6 +81,14 @@ def parse_options(argv):
+
+ def main(ctrl, opts):
+
++ # Argument check
++ opts.check_args_of_option("target", 1)
++ opts.check_args_of_option("output", 1)
++ opts.check_args_of_option("from_urls", -1)
++ opts.check_args_of_option("from_metalink", -1)
++ if not opts.args and not opts.from_metalink and not opts.from_urls:
++ raise Error, _("no package(s) given")
++
+ packages = []
+ if opts.args:
+ if sysconf.get("auto-update"):
+diff --git a/smart/commands/info.py b/smart/commands/info.py
+index 12f74f0..59fbe98 100644
+--- a/smart/commands/info.py
++++ b/smart/commands/info.py
+@@ -58,6 +58,10 @@ def parse_options(argv):
+
+ def main(ctrl, opts, reloadchannels=True):
+
++ # Argument check
++ if not opts.args:
++ raise Error, _("No package(s) given")
++
+ if sysconf.get("auto-update"):
+ from smart.commands import update
+ updateopts = update.parse_options([])
+diff --git a/smart/commands/install.py b/smart/commands/install.py
+index 8a45954..590222c 100644
+--- a/smart/commands/install.py
++++ b/smart/commands/install.py
+@@ -76,6 +76,10 @@ def parse_options(argv):
+
+ def main(ctrl, opts):
+
++ # Argument check
++ if not opts.args:
++ raise Error, _("no package(s) given")
++
+ if opts.explain:
+ sysconf.set("explain-changesets", True, soft=True)
+
+diff --git a/smart/commands/reinstall.py b/smart/commands/reinstall.py
+index e59d896..32da3e6 100644
+--- a/smart/commands/reinstall.py
++++ b/smart/commands/reinstall.py
+@@ -68,7 +68,11 @@ def parse_options(argv):
+ return opts
+
+ def main(ctrl, opts):
+-
++
++ # Argument check
++ if not opts.args:
++ raise Error, _("no package(s) given")
++
+ if opts.explain:
+ sysconf.set("explain-changesets", True, soft=True)
+
+diff --git a/smart/commands/remove.py b/smart/commands/remove.py
+index b4823a6..acd3bbd 100644
+--- a/smart/commands/remove.py
++++ b/smart/commands/remove.py
+@@ -74,6 +74,10 @@ def parse_options(argv):
+
+ def main(ctrl, opts):
+
++ # Argument check
++ if not opts.args:
++ raise Error, _("no package(s) given")
++
+ if opts.explain:
+ sysconf.set("explain-changesets", True, soft=True)
+
+diff --git a/smart/commands/search.py b/smart/commands/search.py
+index 0d0b573..44806b8 100644
+--- a/smart/commands/search.py
++++ b/smart/commands/search.py
+@@ -44,6 +44,8 @@ def option_parser():
+ def parse_options(argv):
+ opts = query.parse_options(argv, usage=USAGE, \
+ description=DESCRIPTION, examples=EXAMPLES)
++ if not argv:
++ raise Error, _("Search expression not specified")
+ opts.name = opts.args
+ opts.summary = opts.args
+ opts.description = opts.args
+diff --git a/smart/commands/upgrade.py b/smart/commands/upgrade.py
+index ec86290..7e290d8 100644
+--- a/smart/commands/upgrade.py
++++ b/smart/commands/upgrade.py
+@@ -91,6 +91,9 @@ def parse_options(argv):
+
+ def main(ctrl, opts):
+
++ # Argument check
++ opts.check_args_of_option("flag", 1)
++
+ if opts.explain:
+ sysconf.set("explain-changesets", True, soft=True)
+
+diff --git a/smart/util/optparse.py b/smart/util/optparse.py
+index 4a3d3a8..279b0bf 100644
+--- a/smart/util/optparse.py
++++ b/smart/util/optparse.py
+@@ -70,6 +70,8 @@ import sys, os
+ import types
+ import textwrap
+ from gettext import gettext as _
++from smart import Error
++import re
+
+ def _repr(self):
+ return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self)
+@@ -708,6 +710,12 @@ class Option:
+ self.action, self.dest, opt, value, values, parser)
+
+ def take_action(self, action, dest, opt, value, values, parser):
++ # Keep all the options in the command line in the '_given_opts' array
++ # This will be used later to validate the command line
++ given_opts = getattr(parser.values, "_given_opts", [])
++ user_opt = re.sub(r"^\-*", "", opt).replace("-", "_")
++ given_opts.append(user_opt)
++ setattr(parser.values, "_given_opts", given_opts)
+ if action == "store":
+ setattr(values, dest, value)
+ elif action == "store_const":
+@@ -819,6 +827,54 @@ class Values:
+ setattr(self, attr, value)
+ return getattr(self, attr)
+
++ # Check if the given option has the specified number of arguments
++ # Raise an error if the option has an invalid number of arguments
++ # A negative number for 'nargs' means "at least |nargs| arguments are needed"
++ def check_args_of_option(self, opt, nargs, err=None):
++ given_opts = getattr(self, "_given_opts", [])
++ if not opt in given_opts:
++ return
++ values = getattr(self, opt, [])
++ if type(values) != type([]):
++ return
++ if nargs < 0:
++ nargs = -nargs
++ if len(values) >= nargs:
++ return
++ if not err:
++ if nargs == 1:
++ err = _("Option '%s' requires at least one argument") % opt
++ else:
++ err = _("Option '%s' requires at least %d arguments") % (opt, nargs)
++ raise Error, err
++ elif nargs == 0:
++ if len( values ) == 0:
++ return
++ raise Error, err
++ else:
++ if len(values) == nargs:
++ return
++ if not err:
++ if nargs == 1:
++ err = _("Option '%s' requires one argument") % opt
++ else:
++ err = _("Option '%s' requires %d arguments") % (opt, nargs)
++ raise Error, err
++
++ # Check that at least one of the options in 'actlist' was given as an argument
++ # to the command 'cmdname'
++ def ensure_action(self, cmdname, actlist):
++ given_opts = getattr(self, "_given_opts", [])
++ for action in actlist:
++ if action in given_opts:
++ return
++ raise Error, _("No action specified for command '%s'") % cmdname
++
++ # Check if there are any other arguments left after parsing the command line and
++ # raise an error if such arguments are found
++ def check_remaining_args(self):
++ if self.args:
++ raise Error, _("Invalid argument(s) '%s'" % str(self.args))
+
+ class OptionContainer:
+