15804991 option mangling and verification should be part of the API, not the CLI client s12b18
authorErik Trauschke <Erik.Trauschke@oracle.com>
Mon, 11 Mar 2013 15:15:21 -0700
changeset 2875 09e276ba70c6
parent 2874 5a3a050c4d97
child 2876 fd422f3cc04f
15804991 option mangling and verification should be part of the API, not the CLI client
src/client.py
src/modules/client/api_errors.py
src/modules/client/options.py
src/modules/misc.py
src/pkg/manifests/package:pkg.p5m
src/po/POTFILES.in
--- a/src/client.py	Fri Mar 08 10:05:28 2013 -0800
+++ b/src/client.py	Mon Mar 11 15:15:21 2013 -0700
@@ -21,7 +21,7 @@
 #
 
 #
-# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
 #
 
 #
@@ -72,6 +72,7 @@
         import pkg.client.progress as progress
         import pkg.client.linkedimage as li
         import pkg.client.publisher as publisher
+        import pkg.client.options as options
         import pkg.fmri as fmri
         import pkg.misc as misc
         import pkg.pipeutils as pipeutils
@@ -625,16 +626,16 @@
         elif global_settings.client_output_quiet:
                 progresstracker = progress.QuietProgressTracker()
         elif global_settings.client_output_progfd:
-		# This logic handles linked images: for linked children
-		# we elide the progress output.
+                # This logic handles linked images: for linked children
+                # we elide the progress output.
                 output_file = os.fdopen(global_settings.client_output_progfd,
-		    "w")
+                    "w")
                 child_tracker = progress.LinkedChildProgressTracker(
                     output_file=output_file)
-		dot_tracker = progress.DotProgressTracker(
-		    output_file=output_file)
-		progresstracker = progress.MultiProgressTracker(
-		    [child_tracker, dot_tracker])
+                dot_tracker = progress.DotProgressTracker(
+                    output_file=output_file)
+                progresstracker = progress.MultiProgressTracker(
+                    [child_tracker, dot_tracker])
         else:
                 try:
                         progresstracker = progress.FancyUNIXProgressTracker()
@@ -1039,12 +1040,12 @@
                     bool_str(plan.update_boot_archive)))
 
         # Right-justify all status strings based on length of longest string.
-	if status:
-		rjust_status = max(len(s[0]) for s in status)
-		rjust_value = max(len(s[1]) for s in status)
-		for s in status:
-			logger.info("%s %s" % (s[0].rjust(rjust_status),
-			    s[1].rjust(rjust_value)))
+        if status:
+                rjust_status = max(len(s[0]) for s in status)
+                rjust_value = max(len(s[1]) for s in status)
+                for s in status:
+                        logger.info("%s %s" % (s[0].rjust(rjust_status),
+                            s[1].rjust(rjust_value)))
                 # Ensure there is a blank line between status information and
                 # remainder.
                 logger.info("")
@@ -1804,627 +1805,6 @@
 
         return ret_code
 
-def opts_err_opt1_req_opt2(opt1, opt2, op):
-        txt = _("%(opt1)s may only be used in combination with %(opt2)s") % \
-            {"opt1": opt1, "opt2": opt2}
-        usage(txt, cmd=op)
-
-def opts_err_incompat(opt1, opt2, op):
-        txt = _("the %(opt1)s and %(opt2)s options may not be combined") % \
-            {"opt1": opt1, "opt2": opt2}
-        usage(txt, cmd=op)
-
-def opts_err_repeated(opt1, op):
-        txt = _("option '%s' repeated") % (opt1)
-        usage(txt, cmd=op)
-
-def opts_table_cb_beopts(op, api_inst, opts, opts_new):
-
-        # synthesize require_new_be and deny_new_be into new_be
-        del opts_new["require_new_be"]
-        del opts_new["deny_new_be"]
-        opts_new["new_be"] = None
-
-        if (opts["be_name"] or opts["require_new_be"]) and opts["deny_new_be"]:
-                opts_err_incompat("--require-new-be", "--deny-new-be", op)
-
-        # create a new key called "backup_be" in the options array
-        if opts["require_new_be"] or opts["be_name"]:
-                opts_new["new_be"] = True
-        if opts["deny_new_be"]:
-                opts_new["new_be"] = False
-
-        # synthesize require_backup_be and no_backup_be into backup_be
-        del opts_new["require_backup_be"]
-        del opts_new["no_backup_be"]
-        opts_new["backup_be"] = None
-
-        if (opts["require_backup_be"] or opts["backup_be_name"]) and \
-            opts["no_backup_be"]:
-                opts_err_incompat("--require-backup-be", "--no-backup-be", op)
-
-        if (opts["require_backup_be"] or opts["backup_be_name"]) and \
-            (opts["require_new_be"] or opts["be_name"]):
-                opts_err_incompat("--require-backup-be", "--require-new-be", op)
-
-        # create a new key called "backup_be" in the options array
-        if opts["require_backup_be"] or opts["backup_be_name"]:
-                opts_new["backup_be"] = True
-        if opts["no_backup_be"]:
-                opts_new["backup_be"] = False
-
-def opts_table_cb_li_ignore(op, api_inst, opts, opts_new):
-
-        # synthesize li_ignore_all and li_ignore_list into li_ignore
-        del opts_new["li_ignore_all"]
-        del opts_new["li_ignore_list"]
-        opts_new["li_ignore"] = None
-
-        # check if there's nothing to ignore
-        if not opts["li_ignore_all"] and not opts["li_ignore_list"]:
-                return
-
-        if opts["li_ignore_all"]:
-
-                # can't ignore all and specific images
-                if opts["li_ignore_list"]:
-                        opts_err_incompat("-I", "-i", op)
-
-                # can't ignore all and target anything.
-                if "li_target_all" in opts and opts["li_target_all"]:
-                        opts_err_incompat("-I", "-a", op)
-                if "li_target_list" in opts and opts["li_target_list"]:
-                        opts_err_incompat("-I", "-l", op)
-                if "li_name" in opts and opts["li_name"]:
-                        opts_err_incompat("-I", "-l", op)
-
-                opts_new["li_ignore"] = []
-                return
-
-        assert opts["li_ignore_list"]
-
-        # it doesn't make sense to specify images to ignore if the
-        # user is already specifying images to operate on.
-        if "li_target_all" in opts and opts["li_target_all"]:
-                opts_err_incompat("-i", "-a", op)
-        if "li_target_list" in opts and opts["li_target_list"]:
-                opts_err_incompat("-i", "-l", op)
-        if "li_name" in opts and opts["li_name"]:
-                opts_err_incompat("-i", "-l", op)
-
-        li_ignore = []
-        for li_name in opts["li_ignore_list"]:
-                # check for repeats
-                if li_name in li_ignore:
-                        opts_err_repeated("-i %s" % (li_name), op)
-                # add to ignore list
-                li_ignore.append(li_name)
-
-        opts_new["li_ignore"] = api_inst.parse_linked_name_list(li_ignore)
-
-def opts_table_cb_li_no_psync(op, api_inst, opts, opts_new):
-        # if a target child linked image was specified, the no-parent-sync
-        # option doesn't make sense since we know that both the parent and
-        # child image are accessible
-
-        if "li_target_all" not in opts:
-                # we don't accept linked image target options
-                assert "li_target_list" not in opts
-                return
-
-        if opts["li_target_all"] and not opts["li_parent_sync"]:
-                opts_err_incompat("-a", "--no-parent-sync", op)
-        if opts["li_target_list"] and not opts["li_parent_sync"]:
-                opts_err_incompat("-l", "--no-parent-sync", op)
-
-def opts_table_cb_li_props(op, api_inst, opts, opts_new):
-        """convert linked image prop list into a dictionary"""
-
-        opts_new["li_props"] = __parse_linked_props(opts["li_props"], op)
-
-def opts_table_cb_li_target(op, api_inst, opts, opts_new):
-        # figure out which option the user specified
-        if opts["li_target_all"] and opts["li_target_list"]:
-                opts_err_incompat("-a", "-l", op)
-        elif opts["li_target_all"]:
-                arg1 = "-a"
-        elif opts["li_target_list"]:
-                arg1 = "-l"
-        else:
-                return
-
-        if "be_activate" in opts and not opts["be_activate"]:
-                opts_err_incompat(arg1, "--no-be-activate", op)
-        if "be_name" in opts and opts["be_name"]:
-                opts_err_incompat(arg1, "--be-name", op)
-        if "deny_new_be" in opts and opts["deny_new_be"]:
-                opts_err_incompat(arg1, "--deny-new-be", op)
-        if "require_new_be" in opts and opts["require_new_be"]:
-                opts_err_incompat(arg1, "--require-new-be", op)
-        if "reject_pats" in opts and opts["reject_pats"]:
-                opts_err_incompat(arg1, "--reject", op)
-        if "origins" in opts and opts["origins"]:
-                opts_err_incompat(arg1, "-g", op)
-
-        # validate linked image name
-        li_target_list = []
-        for li_name in opts["li_target_list"]:
-                # check for repeats
-                if li_name in li_target_list:
-                        opts_err_repeated("-l %s" % (li_name), op)
-                # add to ignore list
-                li_target_list.append(li_name)
-
-        opts_new["li_target_list"] = \
-            api_inst.parse_linked_name_list(li_target_list)
-
-def opts_table_cb_li_target1(op, api_inst, opts, opts_new):
-        # figure out which option the user specified
-        if opts["li_name"]:
-                arg1 = "-l"
-        else:
-                return
-
-        if "be_activate" in opts and not opts["be_activate"]:
-                opts_err_incompat(arg1, "--no-be-activate", op)
-        if "be_name" in opts and opts["be_name"]:
-                opts_err_incompat(arg1, "--be-name", op)
-        if "deny_new_be" in opts and opts["deny_new_be"]:
-                opts_err_incompat(arg1, "--deny-new-be", op)
-        if "require_new_be" in opts and opts["require_new_be"]:
-                opts_err_incompat(arg1, "--require-new-be", op)
-        if "reject_pats" in opts and opts["reject_pats"]:
-                opts_err_incompat(arg1, "--require", op)
-        if "origins" in opts and opts["origins"]:
-                opts_err_incompat(arg1, "-g", op)
-
-def opts_table_cb_no_headers_vs_quiet(op, api_inst, opts, opts_new):
-        # check if we accept the -q option
-        if "quiet" not in opts:
-                return
-
-        # -q implies -H
-        if opts["quiet"]:
-                opts_new["omit_headers"] = True
-
-def opts_table_cb_q(op, api_inst, opts, opts_new):
-        # Be careful not to overwrite global_settings.client_output_quiet
-        # because it might be set "True" from elsewhere, e.g. in
-        # opts_table_cb_parsable.
-        if opts["quiet"] is True:
-                global_settings.client_output_quiet = True
-
-def opts_table_cb_v(op, api_inst, opts, opts_new):
-        global_settings.client_output_verbose = opts["verbose"]
-
-def opts_table_cb_nqv(op, api_inst, opts, opts_new):
-        if opts["verbose"] and opts["quiet"]:
-                opts_err_incompat("-v", "-q", op)
-
-def opts_table_cb_parsable(op, api_inst, opts, opts_new):
-        if opts["parsable_version"] and opts.get("verbose", False):
-                opts_err_incompat("--parsable", "-v", op)
-        if opts["parsable_version"]:
-                try:
-                        opts_new["parsable_version"] = int(
-                            opts["parsable_version"])
-                except ValueError:
-                        usage(_("--parsable expects an integer argument."),
-                            cmd=op)
-                global_settings.client_output_parsable_version = \
-                    opts_new["parsable_version"]
-                opts_new["quiet"] = True
-                global_settings.client_output_quiet = True
-
-def opts_table_cb_origins(op, api_inst, opts, opts_new):
-        origins = set()
-        for o in opts["origins"]:
-                origins.add(misc.parse_uri(o, cwd=orig_cwd))
-        opts_new["origins"] = origins
-
-def opts_table_cb_stage(op, api_inst, opts, opts_new):
-        if opts["stage"] == None:
-                opts_new["stage"] = API_STAGE_DEFAULT
-                return
-
-        if opts_new["stage"] not in api_stage_values:
-                usage(_("invalid operation stage: '%s'") % opts["stage"],
-                    cmd=op)
-
-def opts_cb_li_attach(op, api_inst, opts, opts_new):
-        if opts["attach_parent"] and opts["attach_child"]:
-                opts_err_incompat("-c", "-p", op)
-
-        if not opts["attach_parent"] and not opts["attach_child"]:
-                usage(_("either -c or -p must be specified"), cmd=op)
-
-        if opts["attach_child"]:
-                # if we're attaching a new child then that doesn't affect
-                # any other children, so ignoring them doesn't make sense.
-                if opts["li_ignore_all"]:
-                        opts_err_incompat("-c", "-I", op)
-                if opts["li_ignore_list"]:
-                        opts_err_incompat("-c", "-i", op)
-
-def opts_table_cb_md_only(op, api_inst, opts, opts_new):
-        # if the user didn't specify linked-md-only we're done
-        if not opts["li_md_only"]:
-                return
-
-        # li_md_only implies no li_pkg_updates
-        if "li_pkg_updates" in opts:
-                opts_new["li_pkg_updates"] = False
-
-        #
-        # if li_md_only is false that means we're not updating any packages
-        # within the current image so there are a ton of options that no
-        # longer apply to the current operation, and hence are incompatible
-        # with li_md_only.
-        #
-        arg1 = "--linked-md-only"
-        if "be_name" in opts and opts["be_name"]:
-                opts_err_incompat(arg1, "--be-name", op)
-        if "deny_new_be" in opts and opts["deny_new_be"]:
-                opts_err_incompat(arg1, "--deny-new-be", op)
-        if "require_new_be" in opts and opts["require_new_be"]:
-                opts_err_incompat(arg1, "--require-new-be", op)
-        if "li_parent_sync" in opts and not opts["li_parent_sync"]:
-                opts_err_incompat(arg1, "--no-parent-sync", op)
-        if "reject_pats" in opts and opts["reject_pats"]:
-                opts_err_incompat(arg1, "--reject", op)
-
-def opts_cb_list(op, api_inst, opts, opts_new):
-        if opts_new["origins"] and opts_new["list_upgradable"]:
-                opts_err_incompat("-g", "-u", op)
-
-        if opts_new["origins"] and not opts_new["list_newest"]:
-                # Use of -g implies -a unless -n is provided.
-                opts_new["list_installed_newest"] = True
-
-        if opts_new["list_all"] and not opts_new["list_installed_newest"]:
-                opts_err_opt1_req_opt2("-f", "-a", op)
-
-        if opts_new["list_installed_newest"] and opts_new["list_newest"]:
-                opts_err_incompat("-a", "-n", op)
-
-        if opts_new["list_installed_newest"] and opts_new["list_upgradable"]:
-                opts_err_incompat("-a", "-u", op)
-
-        if opts_new["summary"] and opts_new["verbose"]:
-                opts_err_incompat("-s", "-v", op)
-
-def opts_cb_int(k, op, api_inst, opts, opts_new, minimum=None):
-        if k not in opts:
-                usage(_("missing required parameter: %s" % k), cmd=op)
-                return
-
-        # get the original argument value
-        v = opts[k]
-
-        # make sure it is an integer
-        try:
-                v = int(v)
-        except (ValueError, TypeError):
-                # not a valid integer
-                err = _("invalid '%(opt)s' value: %(val)s") % \
-                    {"opt": k, "val": v}
-                usage(err, cmd=op)
-
-        # check the minimum bounds
-        if minimum is not None and v < minimum:
-                err = _("'%(opt)s' must be >= %(minimum)d") % \
-                    {"opt": k, "minimum": minimum}
-                usage(err, cmd=op)
-
-        # update the new options array to make the value an integer
-        opts_new[k] = v
-
-def opts_cb_fd(k, op, api_inst, opts, opts_new):
-        opts_cb_int(k, op, api_inst, opts, opts_new, minimum=0)
-
-        err = _("invalid '%(opt)s' value: %(optval)s") % \
-            {"opt": k, "optval": opts_new[k]}
-        try:
-                os.fstat(opts_new[k])
-        except OSError:
-                # not a valid file descriptor
-                usage(err, cmd=op)
-
-def opts_cb_remote(op, api_inst, opts, opts_new):
-        opts_cb_fd("ctlfd", op, api_inst, opts, opts_new)
-        opts_cb_fd("progfd", op, api_inst, opts, opts_new)
-
-        # move progfd from opts_new into a global
-        global_settings.client_output_progfd = opts_new["progfd"]
-        del opts_new["progfd"]
-
-def opts_table_cb_concurrency(op, api_inst, opts, opts_new):
-        if opts["concurrency"] is None:
-                # remove concurrency from parameters dict
-                del opts_new["concurrency"]
-                return
-
-        # make sure we have an integer
-        opts_cb_int("concurrency", op, api_inst, opts, opts_new)
-
-        # update global concurrency setting
-        global_settings.client_concurrency = opts_new["concurrency"]
-
-        # remove concurrency from parameters dict
-        del opts_new["concurrency"]
-
-#
-# options common to multiple pkg(1) subcommands.  The format for specifying
-# options is a list which can contain:
-#
-# - Function pointers which define callbacks that are invoked after all
-#   options (aside from extra pargs) have been parsed.  These callbacks can
-#   verify the the contents and combinations of different options.
-#
-# - Tuples formatted as:
-#       (s, l, k, v)
-#   where the values are:
-#       s: a short option, ex: -f
-#       l: a long option, ex: --foo
-#       k: the key value for the options dictionary
-#       v: the default value. valid values are: True/False, None, [], 0
-#
-opts_table_beopts = [
-    opts_table_cb_beopts,
-    ("",  "backup-be-name=",   "backup_be_name",     None),
-    ("",  "be-name=",          "be_name",            None),
-    ("",  "deny-new-be",       "deny_new_be",        False),
-    ("",  "no-backup-be",      "no_backup_be",       False),
-    ("",  "no-be-activate",    "be_activate",        True),
-    ("",  "require-backup-be", "require_backup_be",  False),
-    ("",  "require-new-be",    "require_new_be",     False),
-]
-
-opts_table_concurrency = [
-    opts_table_cb_concurrency,
-    ("C", "concurrency=",      "concurrency",        None),
-]
-
-opts_table_force = [
-    ("f", "",                "force",                False),
-]
-
-opts_table_li_ignore = [
-    opts_table_cb_li_ignore,
-    ("I", "",                "li_ignore_all",        False),
-    ("i", "",                "li_ignore_list",       []),
-]
-
-opts_table_li_md_only = [
-    opts_table_cb_md_only,
-    ("",  "linked-md-only",    "li_md_only",         False),
-]
-
-opts_table_li_no_pkg_updates = [
-    ("",  "no-pkg-updates",  "li_pkg_updates",       True),
-]
-
-opts_table_li_no_psync = [
-    opts_table_cb_li_no_psync,
-    ("",  "no-parent-sync",  "li_parent_sync",       True),
-]
-
-opts_table_li_props = [
-    opts_table_cb_li_props,
-    ("", "prop-linked",      "li_props",             []),
-]
-
-opts_table_li_target = [
-    opts_table_cb_li_target,
-    ("a", "",                "li_target_all",        False),
-    ("l", "",                "li_target_list",       []),
-]
-
-opts_table_li_target1 = [
-    opts_table_cb_li_target1,
-    ("l", "",                "li_name",              None),
-]
-
-opts_table_licenses = [
-    ("",  "accept",          "accept",               False),
-    ("",  "licenses",        "show_licenses",        False),
-]
-
-opts_table_no_headers = [
-    opts_table_cb_no_headers_vs_quiet,
-    ("H", "",                "omit_headers",         False),
-]
-
-opts_table_no_index = [
-    ("",  "no-index",        "update_index",         True),
-]
-
-opts_table_no_refresh = [
-    ("",  "no-refresh",      "refresh_catalogs",     True),
-]
-
-opts_table_reject = [
-    ("", "reject=",          "reject_pats",          []),
-]
-
-opts_table_verbose = [
-    opts_table_cb_v,
-    ("v", "",                "verbose",              0),
-]
-
-opts_table_quiet = [
-    opts_table_cb_q,
-    ("q", "",                "quiet",                False),
-]
-
-opts_table_parsable = [
-    opts_table_cb_parsable,
-    ("", "parsable=",        "parsable_version",     None),
-]
-
-opts_table_nqv = \
-    opts_table_quiet + \
-    opts_table_verbose + \
-    [
-    opts_table_cb_nqv,
-    ("n", "",                "noexecute",            False),
-]
-
-opts_table_origins = [
-    opts_table_cb_origins,
-    ("g", "",                "origins",              []),
-]
-
-opts_table_stage = [
-    opts_table_cb_stage,
-    ("",  "stage",           "stage",                None),
-]
-
-#
-# Options for pkg(1) subcommands.  Built by combining the option tables above,
-# with some optional subcommand unique options defined below.
-#
-opts_install = \
-    opts_table_beopts + \
-    opts_table_concurrency + \
-    opts_table_li_ignore + \
-    opts_table_li_no_psync + \
-    opts_table_licenses + \
-    opts_table_reject + \
-    opts_table_no_index + \
-    opts_table_no_refresh + \
-    opts_table_nqv + \
-    opts_table_parsable + \
-    opts_table_origins + \
-    []
-
-# "update" cmd inherits all "install" cmd options
-opts_update = \
-    opts_install + \
-    opts_table_force + \
-    opts_table_stage + \
-    []
-
-# "attach-linked" cmd inherits all "install" cmd options
-opts_attach_linked = \
-    opts_install + \
-    opts_table_force + \
-    opts_table_li_md_only + \
-    opts_table_li_no_pkg_updates + \
-    opts_table_li_props + \
-    [
-    opts_cb_li_attach,
-    ("",  "allow-relink",   "allow_relink",         False),
-    ("c", "",               "attach_child",         False),
-    ("p", "",               "attach_parent",        False),
-]
-
-opts_list_mediator = \
-    opts_table_no_headers + \
-    [
-    ("a", "",                "list_available",      False),
-    ("F", "output-format",   "output_format",       None)
-]
-
-opts_revert = \
-    opts_table_beopts + \
-    opts_table_nqv + \
-    opts_table_parsable + \
-    [
-    ("",  "tagged",          "tagged",               False),
-]
-
-opts_set_mediator = \
-    opts_table_beopts + \
-    opts_table_no_index + \
-    opts_table_nqv + \
-    opts_table_parsable + \
-    [
-    ("I", "implementation",  "med_implementation",   None),
-    ("V", "version",         "med_version",          None)
-]
-
-opts_unset_mediator = \
-    opts_table_beopts + \
-    opts_table_no_index + \
-    opts_table_nqv + \
-    opts_table_parsable + \
-    [
-    ("I", "",               "med_implementation",   False),
-    ("V", "",               "med_version",          False)
-]
-
-# "set-property-linked" cmd inherits all "install" cmd options
-opts_set_property_linked = \
-    opts_install + \
-    opts_table_li_md_only + \
-    opts_table_li_no_pkg_updates + \
-    opts_table_li_target1 + \
-    []
-
-# "sync-linked" cmd inherits all "install" cmd options
-opts_sync_linked = \
-    opts_install + \
-    opts_table_li_md_only + \
-    opts_table_li_no_pkg_updates + \
-    opts_table_li_target + \
-    opts_table_stage + \
-    []
-
-opts_uninstall = \
-    opts_table_beopts + \
-    opts_table_concurrency + \
-    opts_table_li_ignore + \
-    opts_table_no_index + \
-    opts_table_nqv + \
-    opts_table_parsable + \
-    opts_table_stage
-
-opts_audit_linked = \
-    opts_table_li_no_psync + \
-    opts_table_li_target + \
-    opts_table_no_headers + \
-    opts_table_quiet + \
-    []
-
-opts_detach_linked = \
-    opts_table_force + \
-    opts_table_li_target + \
-    opts_table_nqv + \
-    []
-
-opts_list_linked = \
-    opts_table_li_ignore + \
-    opts_table_no_headers + \
-    []
-
-opts_list_property_linked = \
-    opts_table_li_target1 + \
-    opts_table_no_headers + \
-    []
-
-opts_list_inventory = \
-    opts_table_li_no_psync + \
-    opts_table_no_refresh + \
-    opts_table_no_headers + \
-    opts_table_origins + \
-    opts_table_verbose + \
-    [
-    opts_cb_list,
-    ("a", "",               "list_installed_newest", False),
-    ("f", "",               "list_all",              False),
-    ("n", "",               "list_newest",           False),
-    ("s", "",               "summary",               False),
-    ("u", "",               "list_upgradable",       False),
-]
-
-opts_remote = [
-    opts_cb_remote,
-    ("",  "ctlfd",           "ctlfd",                None),
-    ("",  "progfd",          "progfd",               None),
-]
-
-
 class RemoteDispatch(object):
         """RPC Server Class which invoked by the PipedRPCServer when a RPC
         request is recieved."""
@@ -5583,8 +4963,8 @@
                     update_index=update_index)
 
         # sync the requested child image(s)
-	api_inst.progresstracker.set_major_phase(
-	    api_inst.progresstracker.PHASE_UTILITY)
+        api_inst.progresstracker.set_major_phase(
+            api_inst.progresstracker.PHASE_UTILITY)
         rvdict = api_inst.sync_linked_children(li_target_list,
             noexecute=noexecute, accept=accept, show_licenses=show_licenses,
             refresh_catalogs=refresh_catalogs, update_index=update_index,
@@ -5654,8 +5034,8 @@
                     reject_list=reject_pats, update_index=update_index)
 
         # attach the requested child image
-	api_inst.progresstracker.set_major_phase(
-	    api_inst.progresstracker.PHASE_UTILITY)
+        api_inst.progresstracker.set_major_phase(
+            api_inst.progresstracker.PHASE_UTILITY)
         (rv, err, p_dict) = api_inst.attach_linked_child(lin, li_path, li_props,
             accept=accept, allow_relink=allow_relink, force=force,
             li_md_only=li_md_only, li_pkg_updates=li_pkg_updates,
@@ -5687,8 +5067,8 @@
                 return __api_op(op, api_inst, _noexecute=noexecute,
                     _quiet=quiet, _verbose=verbose, force=force)
 
-	api_inst.progresstracker.set_major_phase(
-	    api_inst.progresstracker.PHASE_UTILITY)
+        api_inst.progresstracker.set_major_phase(
+            api_inst.progresstracker.PHASE_UTILITY)
         rvdict = api_inst.detach_linked_children(li_target_list, force=force,
             noexecute=noexecute)
 
@@ -6264,79 +5644,196 @@
 orig_cwd = None
 
 #
+# Mapping of the internal option name to short and long CLI options.
+#
+# {option_name: (short, long)}
+#
+#
+
+opts_mapping = {
+    "backup_be_name" :    ("",  "backup-be-name"),
+    "be_name" :           ("",  "be-name"),
+    "deny_new_be" :       ("",  "deny-new-be"),
+    "no_backup_be" :      ("",  "no-backup-be"),
+    "be_activate" :       ("",  "no-be-activate"),
+    "require_backup_be" : ("",  "require-backup-be"),
+    "require_new_be" :    ("",  "require-new-be"),
+
+    "concurrency" :       ("C", "concurrency"),
+
+    "force" :             ("f", ""),
+
+    "li_ignore_all" :     ("I", ""),
+    "li_ignore_list" :    ("i", ""),
+    "li_md_only" :        ("",  "linked-md-only"),
+
+    "li_pkg_updates" :    ("",  "no-pkg-updates"),
+
+    "li_parent_sync" :    ("",  "no-parent-sync"),
+
+    "li_props" :          ("",  "prop-linked"),
+
+    "li_target_all" :     ("a", ""),
+    "li_target_list" :    ("l", ""),
+
+    "li_name" :           ("l",  ""),
+
+    "accept" :            ("",  "accept"),
+    "show_licenses" :     ("",  "licenses"),
+
+    "omit_headers" :      ("H",  ""),
+
+    "update_index" :      ("",  "no-index"),
+
+    "refresh_catalogs" :  ("",  "no-refresh"),
+
+    "reject_pats" :       ("",  "reject"),
+
+    "verbose" :           ("v",  ""),
+
+    "quiet" :             ("q",  ""),
+
+    "parsable_version" :  ("",  "parsable"),
+
+    "noexecute" :         ("n",  ""),
+
+    "origins" :           ("g",  ""),
+
+    "stage" :             ("",  "stage"),
+
+    "allow_relink" :      ("",  "allow-relink"),
+    "attach_child" :      ("c",  ""),
+    "attach_parent" :     ("p",  ""),
+
+    "list_available" :    ("a",  ""),
+    "output_format" :     ("F",  "output-format"),
+
+    "tagged" :            ("",  "tagged"),
+
+    # These options are used in set-mediator and unset-mediator but
+    # the long options are only valid in set_mediator (as per the previous
+    # implementation). However, the long options are not documented in the
+    # manpage for set-mediator either, so I think we're good.
+    "med_implementation" : ("I",  "implementation"),
+    "med_version" :        ("V",  "version"),
+
+    "list_installed_newest" : ("a",  ""),
+    "list_all" :              ("f",  ""),
+    "list_newest" :           ("n",  ""),
+    "summary" :               ("s",  ""),
+    "list_upgradable" :       ("u",  ""),
+
+    "ctlfd" :                 ("",  "ctlfd"),
+    "progfd" :                ("",  "progfd"),
+}
+
+#
 # cmds dictionary is used to dispatch subcommands.  The format of this
 # dictionary is:
 #
-#       "subcommand-name" : (
-#               subcommand-cb,
-#               subcommand-opts-table,
-#               arguments-allowed
-#       )
+#       "subcommand-name" : subcommand-cb
 #
 #       subcommand-cb: the callback function invoked for this subcommand
-#       subcommand-opts-table: an arguments options table that is passed to
-#               the common options processing function misc.opts_parse().
-#               if None then misc.opts_parse() is not invoked.
-#
-#       arguments-allowed (optional): the number of additional arguments
-#               allowed to this function, which is also passed to
-#               misc.opts_parse()
 #
 # placeholders in this lookup table for image-create, help and version
 # which don't have dedicated methods
 #
 cmds = {
-    "add-property-value"    : (property_add_value, None),
-    "attach-linked"         : (attach_linked, opts_attach_linked, 2),
-    "avoid"                 : (avoid, None),
-    "audit-linked"          : (audit_linked, opts_audit_linked),
-    "change-facet"          : (change_facet, opts_install, -1),
-    "change-variant"        : (change_variant, opts_install, -1),
-    "contents"              : (list_contents, None),
-    "detach-linked"         : (detach_linked, opts_detach_linked),
-    "facet"                 : (facet_list, None),
-    "fix"                   : (fix_image, None),
-    "freeze"                : (freeze, None),
-    "help"                  : (None, None),
-    "history"               : (history_list, None),
-    "image-create"          : (None, None),
-    "info"                  : (info, None),
-    "install"               : (install, opts_install, -1),
-    "list"                  : (list_inventory, opts_list_inventory, -1),
-    "list-linked"           : (list_linked, opts_list_linked),
-    "mediator"              : (list_mediators, opts_list_mediator, -1),
-    "property"              : (property_list, None),
-    "property-linked"       : (list_property_linked,
-                                  opts_list_property_linked, -1),
-    "pubcheck-linked"       : (pubcheck_linked, []),
-    "publisher"             : (publisher_list, None),
-    "purge-history"         : (history_purge, None),
-    "rebuild-index"         : (rebuild_index, None),
-    "refresh"               : (publisher_refresh, None),
-    "remote"                : (remote, opts_remote, 0),
-    "remove-property-value" : (property_remove_value, None),
-    "revert"                : (revert, opts_revert, -1),
-    "search"                : (search, None),
-    "set-mediator"          : (set_mediator, opts_set_mediator, -1),
-    "set-property"          : (property_set, None),
-    "set-property-linked"   : (set_property_linked,
-                                  opts_set_property_linked, -1),
-    "set-publisher"         : (publisher_set, None),
-    "sync-linked"           : (sync_linked, opts_sync_linked),
-    "unavoid"               : (unavoid, None),
-    "unfreeze"              : (unfreeze, None),
-    "uninstall"             : (uninstall, opts_uninstall, -1),
-    "unset-property"        : (property_unset, None),
-    "update-format"         : (update_format, None),
-    "unset-mediator"        : (unset_mediator, opts_unset_mediator, -1),
-    "unset-publisher"       : (publisher_unset, None),
-    "update"                : (update, opts_update, -1),
-    "update-format"         : (update_format, None),
-    "variant"               : (variant_list, None),
-    "verify"                : (verify_image, None),
-    "version"               : (None, None),
+    "add-property-value"    : [property_add_value],
+    "attach-linked"         : [attach_linked, 2],
+    "avoid"                 : [avoid],
+    "audit-linked"          : [audit_linked, 0],
+    "authority"             : [publisher_list],
+    "change-facet"          : [change_facet],
+    "change-variant"        : [change_variant],
+    "contents"              : [list_contents],
+    "detach-linked"         : [detach_linked, 0],
+    "facet"                 : [facet_list],
+    "fix"                   : [fix_image],
+    "freeze"                : [freeze],
+    "help"                  : [None],
+    "history"               : [history_list],
+    "image-create"          : [None],
+    "info"                  : [info],
+    "install"               : [install],
+    "list"                  : [list_inventory],
+    "list-linked"           : [list_linked, 0],
+    "mediator"              : [list_mediators],
+    "property"              : [property_list],
+    "property-linked"       : [list_property_linked],
+    "pubcheck-linked"       : [pubcheck_linked, 0],
+    "publisher"             : [publisher_list],
+    "purge-history"         : [history_purge],
+    "rebuild-index"         : [rebuild_index],
+    "refresh"               : [publisher_refresh],
+    "remote"                : [remote, 0],
+    "remove-property-value" : [property_remove_value],
+    "revert"                : [revert],
+    "search"                : [search],
+    "set-authority"         : [publisher_set],
+    "set-mediator"          : [set_mediator],
+    "set-property"          : [property_set],
+    "set-property-linked"   : [set_property_linked],
+    "set-publisher"         : [publisher_set],
+    "sync-linked"           : [sync_linked, 0],
+    "unavoid"               : [unavoid],
+    "unfreeze"              : [unfreeze],
+    "uninstall"             : [uninstall],
+    "unset-authority"       : [publisher_unset],
+    "unset-property"        : [property_unset],
+    "update-format"         : [update_format],
+    "unset-mediator"        : [unset_mediator],
+    "unset-publisher"       : [publisher_unset],
+    "update"                : [update],
+    "update-format"         : [update_format],
+    "variant"               : [variant_list],
+    "verify"                : [verify_image],
+    "version"               : [None],
 }
 
+# These tables are an addendum to the the pkg_op_opts/opts_* lists in
+# modules/client/options.py. They contain all the options for functions which
+# are not represented in options.py but go through common option processing.
+# This list should get shortened and eventually removed by moving more/all
+# functions out of client.py.
+
+def opts_cb_remote(api_inst, opts, opts_new):
+        options.opts_cb_fd("ctlfd", api_inst, opts, opts_new)
+        options.opts_cb_fd("progfd", api_inst, opts, opts_new)
+
+        # move progfd from opts_new into a global
+        global_settings.client_output_progfd = opts_new["progfd"]
+        del opts_new["progfd"]
+
+opts_remote = [
+    opts_cb_remote,
+    ("ctlfd",                None),
+    ("progfd",               None),
+]
+
+opts_list_mediator = \
+    options.opts_table_no_headers + \
+    [
+    ("list_available",      False),
+    ("output_format",       None)
+]
+opts_unset_mediator = \
+    options.opts_table_beopts + \
+    options.opts_table_no_index + \
+    options.opts_table_nqv + \
+    options.opts_table_parsable + \
+    [
+    ("med_implementation",   False),
+    ("med_version",          False)
+]
+
+cmd_opts = {
+    "mediator"              : opts_list_mediator,
+    "unset-mediator"        : opts_unset_mediator,
+    "remote"                : opts_remote,
+}
+
+
 def main_func():
         global_settings.client_name = PKG_CLIENT_NAME
 
@@ -6469,28 +5966,101 @@
         img = api_inst.img
 
         # Find subcommand and execute operation.
-        pargs_limit = 0
         func = cmds[subcommand][0]
-        opts_cmd = cmds[subcommand][1]
-        if len(cmds[subcommand]) > 2:
-                pargs_limit = cmds[subcommand][2]
-        try:
-                if opts_cmd == None:
+        pargs_limit = None
+        if len(cmds[subcommand]) > 1:
+                pargs_limit = cmds[subcommand][1]
+
+        pkg_timer.record("client startup", logger=logger)
+
+        # Get the available options for the requested operation to create the
+        # getopt parsing strings.
+        valid_opts = options.get_pkg_opts(subcommand, add_table=cmd_opts)
+        if not valid_opts:
+                # if there are no options for an op, it has its own processing
+                try:
                         return func(api_inst, pargs)
-
-                opts, pargs = misc.opts_parse(subcommand, api_inst, pargs,
-                    opts_cmd, pargs_limit, usage)
-
-                # Reset the progress tracker here, because we may have
-                # to switch to a different tracker due to the options parse.
-                _api_inst.progresstracker = get_tracker()
-
-                pkg_timer.record("client startup", logger=logger)
-                return func(op=subcommand, api_inst=api_inst,
-                    pargs=pargs, **opts)
-
-        except getopt.GetoptError, e:
-                usage(_("illegal option -- %s") % e.opt, cmd=subcommand)
+                except getopt.GetoptError, e:
+                        usage(_("illegal option -- %s") % e.opt,
+                            cmd=subcommand)
+
+        try:
+                # Parse CLI arguments into dictionary containing corresponding
+                # options and values.
+                opt_dict, pargs = misc.opts_parse(subcommand, pargs, valid_opts,
+                    opts_mapping, usage)
+
+                if pargs_limit is not None and len(pargs) > pargs_limit:
+                        usage(_("illegal argument -- %s") % pargs[pargs_limit],
+                            cmd=subcommand)
+
+                opts = options.opts_assemble(subcommand, api_inst, opt_dict,
+                    add_table=cmd_opts, cwd=orig_cwd)
+
+        except api_errors.InvalidOptionError, e:
+
+                # We can't use the string representation of the exception since
+                # it references internal option names. We substitute the CLI
+                # options and create a new exception to make sure the messages
+                # are correct.
+
+                # Convert the internal options to CLI options. We make sure that
+                # when there is a short and a long version for the same option
+                # we print both to avoid confusion.
+                def get_cli_opt(option):
+                        out = ""
+                        try:
+                                s, l = opts_mapping[option]
+                                if l and not s:
+                                        return "--%s" % l
+                                elif s and not l:
+                                        return "-%s" % s
+                                else:
+                                        return("-%s/--%s" % (s,l))
+                        except KeyError:
+                                # ignore if we can't find a match
+                                # (happens for repeated arguments)
+                                return option
+
+                cli_opts = []
+                opt_def = []
+
+                for o in e.options:
+                        cli_opts.append(get_cli_opt(o))
+
+                        # collect the default value (see comment below)
+                        opt_def.append(options.get_pkg_opts_defaults(subcommand,
+                            o, add_table=cmd_opts))
+
+                # Prepare for headache:
+                # If we have an option 'b' which is set to True by default it
+                # will be toggled to False if the users specifies the according
+                # option on the CLI.
+                # If we now have an option 'a' which requires option 'b' to be
+                # set, we can't say "'a' requires 'b'" because the user can only
+                # specify 'not b'. So the correct message would be:
+                # "'a' is incompatible with 'not b'".
+                # We can get there by just changing the type of the exception
+                # for all cases where the default value of one of the options is
+                # True.
+                if e.err_type == api_errors.InvalidOptionError.REQUIRED:
+                        if len(opt_def) == 2 and (opt_def[0] or opt_def[1]):
+                                e.err_type = \
+                                    api_errors.InvalidOptionError.INCOMPAT
+
+                # This new exception will have the CLI options, so can be passed
+                # directly to usage().
+                new_e = api_errors.InvalidOptionError(err_type=e.err_type,
+                    options=cli_opts, msg=e.msg)
+
+                usage(str(new_e), cmd=subcommand)
+
+        # Reset the progress tracker here, because we may have
+        # to switch to a different tracker due to the options parse.
+        _api_inst.progresstracker = get_tracker()
+
+        return func(op=subcommand, api_inst=api_inst,
+            pargs=pargs, **opts)
 
 #
 # Establish a specific exit status which means: "python barfed an exception"
--- a/src/modules/client/api_errors.py	Fri Mar 08 10:05:28 2013 -0800
+++ b/src/modules/client/api_errors.py	Mon Mar 11 15:15:21 2013 -0700
@@ -177,7 +177,7 @@
                     "needed": bytes_to_str(self.needed),
                     "use": self.use
                     }
-                    
+
 
 class VersionException(ApiException):
         def __init__(self, expected_version, received_version):
@@ -1422,7 +1422,7 @@
 
         def __init__(self, v):
                 self.version = v
-        
+
         def __str__(self):
                 return _("%s is not a supported version for creating a "
                     "syspub response.") % self.version
@@ -3002,11 +3002,12 @@
         """Used to indicate an issue with verifying options passed to a certain
         operation."""
 
-        GENERIC  = "generic"      # generic option violation
-        REPEATED = "repeated"     # option repetition is not allowed
-        INCOMPAT = "incompat"     # option 'a' can not be specified with option 'b'
-        REQUIRED = "required"     # option 'a' requires option 'b'
-        XOR      = "xor"          # either option 'a' or option 'b' must be specified
+        GENERIC    = "generic"      # generic option violation
+        OPT_REPEAT = "opt_repeat"   # option repetition is not allowed
+        ARG_REPEAT = "arg_repeat"   # argument repetition is not allowed
+        INCOMPAT   = "incompat"     # option 'a' can not be specified with option 'b'
+        REQUIRED   = "required"     # option 'a' requires option 'b'
+        XOR        = "xor"          # either option 'a' or option 'b' must be specified
 
 	def __init__(self, err_type=GENERIC, options=[], msg=None):
 
@@ -3024,19 +3025,24 @@
                                 self.msg += " ".join(self.options)
                         return self.msg
 
-       		if self.err_type == self.REPEATED:
+       		if self.err_type == self.OPT_REPEAT:
                         assert len(self.options) == 1
-                        return _("Option '%(option)s' repeated ") % {
-                            "option" : options[0]}
+                        return _("Option '%(option)s' may not be repeated.") % {
+                            "option" : self.options[0]}
+                elif self.err_type == self.ARG_REPEAT:
+                        assert len(self.options) == 2
+                        return _("Argument '%(op1)s' for option '%(op2)s' may "
+                            "not be repeated.") % {"op1" : self.options[0],
+                            "op2" : self.options[1]}
                 elif self.err_type == self.INCOMPAT:
                         assert len(self.options) == 2
                         return _("The '%(op1)s' and '%(op2)s' option may "
-                            "not be combined") % {"op1" : self.options[0],
+                            "not be combined.") % {"op1" : self.options[0],
                             "op2" : self.options[1]}
                 elif self.err_type == self.REQUIRED:
                         assert len(self.options) == 2
                         return _("'%(op1)s' may only be used with "
-                            "'%(op2)s'") % {"op1" : self.options[0],
+                            "'%(op2)s'.") % {"op1" : self.options[0],
                             "op2" : self.options[1]}
                 elif self.err_type == self.XOR:
                         assert len(self.options) == 2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modules/client/options.py	Mon Mar 11 15:15:21 2013 -0700
@@ -0,0 +1,834 @@
+#!/usr/bin/python
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+
+import os
+
+import pkg.client.pkgdefs as pkgdefs
+import pkg.client.linkedimage as li
+import pkg.misc as misc
+
+from pkg.client.api_errors import InvalidOptionError
+from pkg.client import global_settings
+
+_orig_cwd = None
+
+# List of available options for common option processing.
+ACCEPT                = "accept"
+ALLOW_RELINK          = "allow_relink"
+ATTACH_CHILD          = "attach_child"
+ATTACH_PARENT         = "attach_parent"
+BACKUP_BE             = "backup_be"
+BACKUP_BE_NAME        = "backup_be_name"
+BE_ACTIVATE           = "be_activate"
+BE_NAME               = "be_name"
+CONCURRENCY           = "concurrency"
+DENY_NEW_BE           = "deny_new_be"
+FORCE                 = "force"
+LI_IGNORE             = "li_ignore"
+LI_IGNORE_ALL         = "li_ignore_all"
+LI_IGNORE_LIST        = "li_ignore_list"
+LI_MD_ONLY            = "li_md_only"
+LI_NAME               = "li_name"
+LI_PARENT_SYNC        = "li_parent_sync"
+LI_PKG_UPDATES        = "li_pkg_updates"
+LI_PROPS              = "li_props"
+LI_TARGET_ALL         = "li_target_all"
+LI_TARGET_LIST        = "li_target_list"
+LIST_ALL              = "list_all"
+LIST_INSTALLED_NEWEST = "list_installed_newest"
+LIST_NEWEST           = "list_newest"
+LIST_UPGRADABLE       = "list_upgradable"
+MED_IMPLEMENTATION    = "med_implementation"
+MED_VERSION           = "med_version"
+NEW_BE                = "new_be"
+NO_BACKUP_BE          = "no_backup_be"
+NOEXECUTE             = "noexecute"
+OMIT_HEADERS          = "omit_headers"
+ORIGINS               = "origins"
+PARSABLE_VERSION      = "parsable_version"
+QUIET                 = "quiet"
+REFRESH_CATALOGS      = "refresh_catalogs"
+REJECT_PATS           = "reject_pats"
+REQUIRE_BACKUP_BE     = "require_backup_be"
+REQUIRE_NEW_BE        = "require_new_be"
+SHOW_LICENSES         = "show_licenses"
+STAGE                 = "stage"
+SUMMARY               = "summary"
+TAGGED                = "tagged"
+UPDATE_INDEX          = "update_index"
+VERBOSE               = "verbose"
+
+
+
+def opts_table_cb_beopts(api_inst, opts, opts_new):
+
+        # synthesize require_new_be and deny_new_be into new_be
+        del opts_new[REQUIRE_NEW_BE]
+        del opts_new[DENY_NEW_BE]
+        opts_new[NEW_BE] = None
+
+        if (opts[BE_NAME] or opts[REQUIRE_NEW_BE]) and opts[DENY_NEW_BE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [REQUIRE_NEW_BE, DENY_NEW_BE])
+
+        # create a new key called BACKUP_BE in the options array
+        if opts[REQUIRE_NEW_BE] or opts[BE_NAME]:
+                opts_new[NEW_BE] = True
+        if opts[DENY_NEW_BE]:
+                opts_new[NEW_BE] = False
+
+        # synthesize require_backup_be and no_backup_be into backup_be
+        del opts_new[REQUIRE_BACKUP_BE]
+        del opts_new[NO_BACKUP_BE]
+        opts_new[BACKUP_BE] = None
+
+        if (opts[REQUIRE_BACKUP_BE] or opts[BACKUP_BE_NAME]) and \
+            opts[NO_BACKUP_BE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [REQUIRE_BACKUP_BE, NO_BACKUP_BE])
+
+        if (opts[REQUIRE_BACKUP_BE] or opts[BACKUP_BE_NAME]) and \
+            (opts[REQUIRE_NEW_BE] or opts[BE_NAME]):
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [REQUIRE_BACKUP_BE, REQUIRE_NEW_BE])
+
+        # create a new key called BACKUP_BE in the options array
+        if opts[REQUIRE_BACKUP_BE] or opts[BACKUP_BE_NAME]:
+                opts_new[BACKUP_BE] = True
+        if opts[NO_BACKUP_BE]:
+                opts_new[BACKUP_BE] = False
+
+def opts_table_cb_li_ignore(api_inst, opts, opts_new):
+
+        # synthesize li_ignore_all and li_ignore_list into li_ignore
+        del opts_new[LI_IGNORE_ALL]
+        del opts_new[LI_IGNORE_LIST]
+        opts_new[LI_IGNORE] = None
+
+        # check if there's nothing to ignore
+        if not opts[LI_IGNORE_ALL] and not opts[LI_IGNORE_LIST]:
+                return
+
+        if opts[LI_IGNORE_ALL]:
+
+                # can't ignore all and specific images
+                if opts[LI_IGNORE_LIST]:
+                        raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                            [LI_IGNORE_ALL, LI_IGNORE_LIST])
+
+                # can't ignore all and target anything.
+                if LI_TARGET_ALL in opts and opts[LI_TARGET_ALL]:
+                        raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                            [LI_IGNORE_ALL, LI_TARGET_ALL])
+                if LI_TARGET_LIST in opts and opts[LI_TARGET_LIST]:
+                        raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                            [LI_IGNORE_ALL, LI_TARGET_LIST])
+                if LI_NAME in opts and opts[LI_NAME]:
+                        raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                            [LI_IGNORE_ALL, LI_NAME])
+                opts_new[LI_IGNORE] = []
+                return
+
+        assert opts[LI_IGNORE_LIST]
+
+        # it doesn't make sense to specify images to ignore if the
+        # user is already specifying images to operate on.
+        if LI_TARGET_ALL in opts and opts[LI_TARGET_ALL]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [LI_IGNORE_LIST, LI_TARGET_ALL])
+        if LI_TARGET_LIST in opts and opts[LI_TARGET_LIST]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [LI_IGNORE_LIST, LI_TARGET_LIST])
+        if LI_NAME in opts and opts[LI_NAME]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [LI_IGNORE_LIST, LI_NAME])
+
+        li_ignore = []
+        for li_name in opts[LI_IGNORE_LIST]:
+                # check for repeats
+                if li_name in li_ignore:
+                        raise InvalidOptionError(
+                            InvalidOptionError.ARG_REPEAT, [li_name,
+                            LI_IGNORE_LIST])
+                # add to ignore list
+                li_ignore.append(li_name)
+
+        opts_new[LI_IGNORE] = api_inst.parse_linked_name_list(li_ignore)
+
+def opts_table_cb_li_no_psync(api_inst, opts, opts_new):
+        # if a target child linked image was specified, the no-parent-sync
+        # option doesn't make sense since we know that both the parent and
+        # child image are accessible
+
+        if LI_TARGET_ALL not in opts:
+                # we don't accept linked image target options
+                assert LI_TARGET_LIST not in opts
+                return
+
+        if opts[LI_TARGET_ALL] and not opts[LI_PARENT_SYNC]:
+                raise InvalidOptionError(InvalidOptionError.REQUIRED,
+                    [LI_TARGET_ALL, LI_PARENT_SYNC])
+
+        if opts[LI_TARGET_LIST] and not opts[LI_PARENT_SYNC]:
+                raise InvalidOptionError(InvalidOptionError.REQUIRED,
+                    [LI_TARGET_LIST, LI_PARENT_SYNC])
+
+
+def __parse_linked_props(args):
+        """"Parse linked image property options that were specified on the
+        command line into a dictionary.  Make sure duplicate properties were
+        not specified."""
+
+        linked_props = dict()
+        for pv in args:
+                try:
+                        p, v = pv.split("=", 1)
+                except ValueError:
+                        raise InvalidOptionError(msg=_("linked image "
+                            "property arguments must be of the form "
+                            "'<name>=<value>'."))
+
+                if p not in li.prop_values:
+                        raise InvalidOptionError(msg=_("invalid linked "
+                        "image property: '%s'.") % p)
+
+                if p in linked_props:
+                        raise InvalidOptionError(msg=_("linked image "
+                            "property specified multiple times: '%s'.") % p)
+
+                linked_props[p] = v
+
+        return linked_props
+
+def opts_table_cb_li_props(api_inst, opts, opts_new):
+        """convert linked image prop list into a dictionary"""
+
+        opts_new[LI_PROPS] = __parse_linked_props(opts[LI_PROPS])
+
+def opts_table_cb_li_target(api_inst, opts, opts_new):
+        # figure out which option the user specified
+        if opts[LI_TARGET_ALL] and opts[LI_TARGET_LIST]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [LI_TARGET_ALL, LI_TARGET_LIST])
+        elif opts[LI_TARGET_ALL]:
+                arg1 = LI_TARGET_ALL
+        elif opts[LI_TARGET_LIST]:
+                arg1 = LI_TARGET_LIST
+        else:
+                return
+
+        if BE_ACTIVATE in opts and not opts[BE_ACTIVATE]:
+                raise InvalidOptionError(InvalidOptionError.REQUIRED,
+                    [arg1, BE_ACTIVATE])
+        if BE_NAME in opts and opts[BE_NAME]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, BE_NAME])
+        if DENY_NEW_BE in opts and opts[DENY_NEW_BE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, DENY_NEW_BE])
+        if REQUIRE_NEW_BE in opts and opts[REQUIRE_NEW_BE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, REQUIRE_NEW_BE])
+        if REJECT_PATS in opts and opts[REJECT_PATS]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, REJECT_PATS])
+        if ORIGINS in opts and opts[ORIGINS]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, ORIGINS])
+
+        # validate linked image name
+        li_target_list = []
+        for li_name in opts[LI_TARGET_LIST]:
+                # check for repeats
+                if li_name in li_target_list:
+                        raise InvalidOptionError(
+                            InvalidOptionError.ARG_REPEAT, [li_name,
+                            LI_TARGET_LIST])
+                # add to ignore list
+                li_target_list.append(li_name)
+
+        opts_new[LI_TARGET_LIST] = \
+            api_inst.parse_linked_name_list(li_target_list)
+
+def opts_table_cb_li_target1(api_inst, opts, opts_new):
+        # figure out which option the user specified
+        if opts[LI_NAME]:
+                arg1 = LI_NAME
+        else:
+                return
+
+        if BE_ACTIVATE in opts and not opts[BE_ACTIVATE]:
+                raise InvalidOptionError(InvalidOptionError.REQUIRED,
+                    [arg1, BE_ACTIVATE])
+        if BE_NAME in opts and opts[BE_NAME]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, BE_NAME])
+        if DENY_NEW_BE in opts and opts[DENY_NEW_BE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, DENY_NEW_BE])
+        if REQUIRE_NEW_BE in opts and opts[REQUIRE_NEW_BE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, REQUIRE_NEW_BE])
+        if REJECT_PATS in opts and opts[REJECT_PATS]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, REJECT_PATS])
+        if ORIGINS in opts and opts[ORIGINS]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, ORIGINS])
+
+def opts_table_cb_no_headers_vs_quiet(api_inst, opts, opts_new):
+        # check if we accept the -q option
+        if QUIET not in opts:
+                return
+
+        # -q implies -H
+        if opts[QUIET]:
+                opts_new[OMIT_HEADERS] = True
+
+def opts_table_cb_q(api_inst, opts, opts_new):
+        # Be careful not to overwrite global_settings.client_output_quiet
+        # because it might be set "True" from elsewhere, e.g. in
+        # opts_table_cb_parsable.
+        if opts[QUIET] is True:
+                global_settings.client_output_quiet = True
+
+def opts_table_cb_v(api_inst, opts, opts_new):
+        global_settings.client_output_verbose = opts[VERBOSE]
+
+def opts_table_cb_nqv(api_inst, opts, opts_new):
+        if opts[VERBOSE] and opts[QUIET]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [VERBOSE, QUIET])
+
+def opts_table_cb_parsable(api_inst, opts, opts_new):
+        if opts[PARSABLE_VERSION] and opts.get(VERBOSE, False):
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [VERBOSE, PARSABLE_VERSION])
+        if opts[PARSABLE_VERSION]:
+                try:
+                        opts_new[PARSABLE_VERSION] = int(
+                            opts[PARSABLE_VERSION])
+                except ValueError:
+                        raise InvalidOptionError(
+                            options=[PARSABLE_VERSION],
+                            msg=_("integer argument expected"))
+
+                global_settings.client_output_parsable_version = \
+                    opts_new[PARSABLE_VERSION]
+                opts_new[QUIET] = True
+                global_settings.client_output_quiet = True
+
+def opts_table_cb_origins(api_inst, opts, opts_new):
+        origins = set()
+        for o in opts[ORIGINS]:
+                origins.add(misc.parse_uri(o, cwd=_orig_cwd))
+        opts_new[ORIGINS] = origins
+
+def opts_table_cb_stage(api_inst, opts, opts_new):
+        if opts[STAGE] == None:
+                opts_new[STAGE] = pkgdefs.API_STAGE_DEFAULT
+                return
+
+        if opts_new[STAGE] not in pkgdefs.api_stage_values:
+                raise InvalidOptionError(msg=_("invalid operation stage: "
+                    "'%s'") % opts[STAGE])
+
+def opts_cb_li_attach(api_inst, opts, opts_new):
+        if opts[ATTACH_PARENT] and opts[ATTACH_CHILD]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [ATTACH_PARENT, ATTACH_CHILD])
+
+        if not opts[ATTACH_PARENT] and not opts[ATTACH_CHILD]:
+                raise InvalidOptionError(InvalidOptionError.XOR,
+                    [ATTACH_PARENT, ATTACH_CHILD])
+
+        if opts[ATTACH_CHILD]:
+                # if we're attaching a new child then that doesn't affect
+                # any other children, so ignoring them doesn't make sense.
+                if opts[LI_IGNORE_ALL]:
+                        raise InvalidOptionError(
+                            InvalidOptionError.INCOMPAT,
+                            [ATTACH_CHILD, LI_IGNORE_ALL])
+                if opts[LI_IGNORE_LIST]:
+                        raise InvalidOptionError(
+                            InvalidOptionError.INCOMPAT,
+                            [ATTACH_CHILD, LI_IGNORE_LIST])
+
+def opts_table_cb_md_only(api_inst, opts, opts_new):
+        # if the user didn't specify linked-md-only we're done
+        if not opts[LI_MD_ONLY]:
+                return
+
+        # li_md_only implies no li_pkg_updates
+        if LI_PKG_UPDATES in opts:
+                opts_new[LI_PKG_UPDATES] = False
+
+        #
+        # if li_md_only is false that means we're not updating any packages
+        # within the current image so there are a ton of options that no
+        # longer apply to the current operation, and hence are incompatible
+        # with li_md_only.
+        #
+        arg1 = LI_MD_ONLY
+        if BE_NAME in opts and opts[BE_NAME]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, BE_NAME])
+        if DENY_NEW_BE in opts and opts[DENY_NEW_BE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, DENY_NEW_BE])
+        if REQUIRE_NEW_BE in opts and opts[REQUIRE_NEW_BE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, REQUIRE_NEW_BE])
+        if LI_PARENT_SYNC in opts and not opts[LI_PARENT_SYNC]:
+                raise InvalidOptionError(InvalidOptionError.REQUIRED,
+                    [arg1, LI_PARENT_SYNC])
+        if REJECT_PATS in opts and opts[REJECT_PATS]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [arg1, REJECT_PATS])
+
+def opts_cb_list(api_inst, opts, opts_new):
+        if opts_new[ORIGINS] and opts_new[LIST_UPGRADABLE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [ORIGINS, LIST_UPGRADABLE])
+
+        if opts_new[ORIGINS] and not opts_new[LIST_NEWEST]:
+                # Use of -g implies -a unless -n is provided.
+                opts_new[LIST_INSTALLED_NEWEST] = True
+
+        if opts_new[LIST_ALL] and not opts_new[LIST_INSTALLED_NEWEST]:
+                raise InvalidOptionError(InvalidOptionError.REQUIRED,
+                    [LIST_ALL, LIST_INSTALLED_NEWEST])
+
+        if opts_new[LIST_INSTALLED_NEWEST] and opts_new[LIST_NEWEST]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [LIST_INSTALLED_NEWEST, LIST_NEWEST])
+
+        if opts_new[LIST_INSTALLED_NEWEST] and opts_new[LIST_UPGRADABLE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [LIST_INSTALLED_NEWEST, LIST_UPGRADABLE])
+
+        if opts_new[SUMMARY] and opts_new[VERBOSE]:
+                raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+                    [SUMMARY, VERBOSE])
+
+def opts_cb_int(k, api_inst, opts, opts_new, minimum=None):
+
+        if k not in opts or opts[k] == None:
+                err = _("missing required parameter")
+                raise InvalidOptionError(msg=err, options=[k])
+
+        # get the original argument value
+        v = opts[k]
+
+        # make sure it is an integer
+        try:
+                v = int(v)
+        except (ValueError, TypeError):
+                # not a valid integer
+                err = _("value '%s' invalid") % (v)
+                raise InvalidOptionError(msg=err, options=[k])
+
+        # check the minimum bounds
+        if minimum is not None and v < minimum:
+                err = _("value must be >= %d") % (minimum)
+                raise InvalidOptionError(msg=err, options=[k])
+
+        # update the new options array to make the value an integer
+        opts_new[k] = v
+
+def opts_cb_fd(k, api_inst, opts, opts_new):
+        opts_cb_int(k, api_inst, opts, opts_new, minimum=0)
+
+        err = _("value '%s' invalid") % (opts_new[k])
+        try:
+                os.fstat(opts_new[k])
+        except OSError:
+                # not a valid file descriptor
+                raise InvalidOptionError(msg=err, options=[k])
+
+def opts_table_cb_concurrency(api_inst, opts, opts_new):
+        if opts[CONCURRENCY] is None:
+                # remove concurrency from parameters dict
+                del opts_new[CONCURRENCY]
+                return
+
+        # make sure we have an integer
+        opts_cb_int(CONCURRENCY, api_inst, opts, opts_new)
+
+        # update global concurrency setting
+        global_settings.client_concurrency = opts_new[CONCURRENCY]
+
+        # remove concurrency from parameters dict
+        del opts_new[CONCURRENCY]
+
+#
+# options common to multiple pkg(1) operations.  The format for specifying
+# options is a list which can contain:
+#
+# - Tuples formatted as:
+#       (k, v)
+#   where the values are:
+#       k: the key value for the options dictionary
+#       v: the default value. valid values are: True/False, None, [], 0
+#
+
+
+opts_table_beopts = [
+    opts_table_cb_beopts,
+    (BACKUP_BE_NAME,     None),
+    (BE_NAME,            None),
+    (DENY_NEW_BE,        False),
+    (NO_BACKUP_BE,       False),
+    (BE_ACTIVATE,        True),
+    (REQUIRE_BACKUP_BE,  False),
+    (REQUIRE_NEW_BE,     False),
+]
+
+opts_table_concurrency = [
+    opts_table_cb_concurrency,
+    (CONCURRENCY,        None),
+]
+
+opts_table_force = [
+    (FORCE,                False),
+]
+
+opts_table_li_ignore = [
+    opts_table_cb_li_ignore,
+    (LI_IGNORE_ALL,        False),
+    (LI_IGNORE_LIST,       []),
+]
+
+opts_table_li_md_only = [
+    opts_table_cb_md_only,
+    (LI_MD_ONLY,         False),
+]
+
+opts_table_li_no_pkg_updates = [
+    (LI_PKG_UPDATES,       True),
+]
+
+opts_table_li_no_psync = [
+    opts_table_cb_li_no_psync,
+    (LI_PARENT_SYNC,       True),
+]
+
+opts_table_li_props = [
+    opts_table_cb_li_props,
+    (LI_PROPS,             []),
+]
+
+opts_table_li_target = [
+    opts_table_cb_li_target,
+    (LI_TARGET_ALL,        False),
+    (LI_TARGET_LIST,       []),
+]
+
+opts_table_li_target1 = [
+    opts_table_cb_li_target1,
+    (LI_NAME,              None),
+]
+
+opts_table_licenses = [
+    (ACCEPT,               False),
+    (SHOW_LICENSES,        False),
+]
+
+opts_table_no_headers = [
+    opts_table_cb_no_headers_vs_quiet,
+    (OMIT_HEADERS,         False),
+]
+
+opts_table_no_index = [
+    (UPDATE_INDEX,         True),
+]
+
+opts_table_no_refresh = [
+    (REFRESH_CATALOGS,     True),
+]
+
+opts_table_reject = [
+    (REJECT_PATS,          []),
+]
+
+opts_table_verbose = [
+    opts_table_cb_v,
+    (VERBOSE,              0),
+]
+
+opts_table_quiet = [
+    opts_table_cb_q,
+    (QUIET,                False),
+]
+
+opts_table_parsable = [
+    opts_table_cb_parsable,
+    (PARSABLE_VERSION,     None),
+]
+
+opts_table_nqv = \
+    opts_table_quiet + \
+    opts_table_verbose + \
+    [
+    opts_table_cb_nqv,
+    (NOEXECUTE,            False),
+]
+
+opts_table_origins = [
+    opts_table_cb_origins,
+    (ORIGINS,              []),
+]
+
+opts_table_stage = [
+    opts_table_cb_stage,
+    (STAGE,                None),
+]
+
+#
+# Options for pkg(1) subcommands.  Built by combining the option tables above,
+# with some optional subcommand unique options defined below.
+#
+opts_install = \
+    opts_table_beopts + \
+    opts_table_concurrency + \
+    opts_table_li_ignore + \
+    opts_table_li_no_psync + \
+    opts_table_licenses + \
+    opts_table_reject + \
+    opts_table_no_index + \
+    opts_table_no_refresh + \
+    opts_table_nqv + \
+    opts_table_parsable + \
+    opts_table_origins + \
+    []
+
+# "update" cmd inherits all "install" cmd options
+opts_update = \
+    opts_install + \
+    opts_table_force + \
+    opts_table_stage + \
+    []
+
+# "attach-linked" cmd inherits all "install" cmd options
+opts_attach_linked = \
+    opts_install + \
+    opts_table_force + \
+    opts_table_li_md_only + \
+    opts_table_li_no_pkg_updates + \
+    opts_table_li_props + \
+    [
+    opts_cb_li_attach,
+    (ALLOW_RELINK,         False),
+    (ATTACH_CHILD,         False),
+    (ATTACH_PARENT,        False),
+]
+
+opts_revert = \
+    opts_table_beopts + \
+    opts_table_nqv + \
+    opts_table_parsable + \
+    [
+    (TAGGED,               False),
+]
+
+opts_set_mediator = \
+    opts_table_beopts + \
+    opts_table_no_index + \
+    opts_table_nqv + \
+    opts_table_parsable + \
+    [
+    (MED_IMPLEMENTATION,   None),
+    (MED_VERSION,          None)
+]
+
+# "set-property-linked" cmd inherits all "install" cmd options
+opts_set_property_linked = \
+    opts_install + \
+    opts_table_li_md_only + \
+    opts_table_li_no_pkg_updates + \
+    opts_table_li_target1 + \
+    []
+
+# "sync-linked" cmd inherits all "install" cmd options
+opts_sync_linked = \
+    opts_install + \
+    opts_table_li_md_only + \
+    opts_table_li_no_pkg_updates + \
+    opts_table_li_target + \
+    opts_table_stage + \
+    []
+
+opts_uninstall = \
+    opts_table_beopts + \
+    opts_table_concurrency + \
+    opts_table_li_ignore + \
+    opts_table_no_index + \
+    opts_table_nqv + \
+    opts_table_parsable + \
+    opts_table_stage
+
+opts_audit_linked = \
+    opts_table_li_no_psync + \
+    opts_table_li_target + \
+    opts_table_no_headers + \
+    opts_table_quiet + \
+    []
+
+opts_detach_linked = \
+    opts_table_force + \
+    opts_table_li_target + \
+    opts_table_nqv + \
+    []
+
+opts_list_linked = \
+    opts_table_li_ignore + \
+    opts_table_no_headers + \
+    []
+
+opts_list_property_linked = \
+    opts_table_li_target1 + \
+    opts_table_no_headers + \
+    []
+
+opts_list_inventory = \
+    opts_table_li_no_psync + \
+    opts_table_no_refresh + \
+    opts_table_no_headers + \
+    opts_table_origins + \
+    opts_table_verbose + \
+    [
+    opts_cb_list,
+    (LIST_INSTALLED_NEWEST, False),
+    (LIST_ALL,              False),
+    (LIST_NEWEST,           False),
+    (SUMMARY,               False),
+    (LIST_UPGRADABLE,       False),
+]
+
+pkg_op_opts = {
+
+    pkgdefs.PKG_OP_ATTACH         : opts_attach_linked,
+    pkgdefs.PKG_OP_AUDIT_LINKED   : opts_audit_linked,
+    pkgdefs.PKG_OP_CHANGE_FACET   : opts_install,
+    pkgdefs.PKG_OP_CHANGE_VARIANT : opts_install,
+    pkgdefs.PKG_OP_DETACH         : opts_detach_linked,
+    pkgdefs.PKG_OP_INSTALL        : opts_install,
+    pkgdefs.PKG_OP_LIST           : opts_list_inventory,
+    pkgdefs.PKG_OP_LIST_LINKED    : opts_list_linked,
+    pkgdefs.PKG_OP_PROP_LINKED    : opts_list_property_linked,
+    pkgdefs.PKG_OP_PUBCHECK       : [],
+    pkgdefs.PKG_OP_REVERT         : opts_revert,
+    pkgdefs.PKG_OP_SET_MEDIATOR   : opts_set_mediator,
+    pkgdefs.PKG_OP_SET_PROP_LINKED: opts_set_property_linked,
+    pkgdefs.PKG_OP_SYNC           : opts_sync_linked,
+    pkgdefs.PKG_OP_UNINSTALL      : opts_uninstall,
+    pkgdefs.PKG_OP_UPDATE         : opts_update
+}
+
+def get_pkg_opts(op, add_table=None):
+        """Get the available options for a particular operation specified by
+        'op'. If the client uses custom pkg_op_opts tables they can be specified
+        by 'add_table'."""
+
+        popts = pkg_op_opts.copy()
+        if add_table is not None:
+                popts.update(add_table)
+
+        try:
+                opts = popts[op]
+        except KeyError:
+                opts = None
+        return opts
+
+def get_pkg_opts_defaults(op, opt, add_table=None):
+        """ Get the default value for a certain option 'opt' of a certain
+        operation 'op'. This is useful for clients which toggle boolean options.
+        """
+        popts = get_pkg_opts(op, add_table)
+
+        for o in popts:
+                if type(o) != tuple:
+                        continue
+                opt_name, default = o
+                if opt_name == opt:
+                        return default
+
+def opts_assemble(op, api_inst, opts, add_table=None, cwd=None):
+        """Assembly of the options for a specific operation. Options are read in
+        from a dict (see explanation below) and sanity tested.
+
+        This is the common interface to supply options to the functions of the
+        API.
+
+        'op' is the operation for which the options need to be assembled and
+        verified. The currently supported operations are listed in
+        pkgdefs.pkg_op_values.
+
+        'api_inst' is a reference to the API instance, required for some of the
+        verification steps.
+
+        'opts' is the raw options table to be processed. It needs to be a dict
+        in the format: { option_name: argument, ... }
+        """
+
+        global _orig_cwd
+
+        if cwd is not None:
+                _orig_cwd = cwd
+        else:
+                _orig_cwd = None
+
+        popts = get_pkg_opts(op, add_table)
+
+        rv = {}
+        callbacks = []
+
+        for o in popts:
+                if type(o) != tuple:
+                        callbacks.append(o)
+                        continue
+
+                avail_opt, default = o
+                # for options not given we substitue the default value
+                if avail_opt not in opts:
+                        rv[avail_opt] = default
+                        continue
+
+                if type(default) == int:
+                        assert type(opts[avail_opt]) == int, opts[avail_opt]
+                elif type(default) == list:
+                        assert type(opts[avail_opt]) == list, opts[avail_opt]
+                elif type(default) == bool:
+                        assert type(opts[avail_opt]) == bool, opts[avail_opt]
+
+                rv[avail_opt] = opts[avail_opt]
+
+        rv_updated = rv.copy()
+
+        # run the option verification callbacks
+        for cb in callbacks:
+                cb(api_inst, rv, rv_updated)
+
+        return rv_updated
+
--- a/src/modules/misc.py	Fri Mar 08 10:05:28 2013 -0800
+++ b/src/modules/misc.py	Mon Mar 11 15:15:21 2013 -0700
@@ -20,7 +20,7 @@
 # CDDL HEADER END
 #
 
-# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
 
 """
 Misc utility functions used by the packaging system.
@@ -449,7 +449,7 @@
 def bytes_to_str(nbytes, fmt="%(num).2f %(unit)s"):
         """Returns a human-formatted string representing the number of bytes
         in the largest unit possible.
-        
+
         If provided, 'fmt' should be a string which can be formatted
         with a dictionary containing a float 'num' and strings 'unit' and
         'shortunit'.  The default format prints, for example, '3.23 MB' """
@@ -1064,7 +1064,7 @@
         return DEFAULT_TEMP_PATH
 
 def get_temp_root_path():
-        """Return the directory path where the temporary directories or 
+        """Return the directory path where the temporary directories or
         files should be created. If the environment has set TMPDIR
         or TEMP or TMP then return the corresponding value else return the
         default value."""
@@ -1075,7 +1075,7 @@
                 if env_val:
                         return env_val
 
-        return DEFAULT_TEMP_PATH 
+        return DEFAULT_TEMP_PATH
 
 def parse_uri(uri, cwd=None):
         """Parse the repository location provided and attempt to transform it
@@ -1203,30 +1203,27 @@
                 for name in filenames:
                         path = os.path.join(dirpath, name)
                         portable.chown(path, uid, gid)
-def opts_parse(op, api_inst, args, table, pargs_limit, usage_cb):
+
+
+def opts_parse(op, args, opts_table, opts_mapping, usage_cb=None):
         """Generic table-based options parsing function.  Returns a tuple
-        consisting of a dictionary of parsed options and the remaining
-        unparsed options.
+        consisting of a list of parsed options in the form (option, argument)
+        and the remaining unparsed options. The parsed-option list may contain
+        duplicates if an option is passed multiple times.
 
         'op' is the operation being performed.
 
-        'api_inst' is an image api object that is passed to options handling
-        callbacks (passed in via 'table').
-
         'args' is the arguments that should be parsed.
 
-        'table' is a list of options and callbacks.Each entry is either a
-        a tuple or a callback function.
-
-        tuples in 'table' specify allowable options and have the following
-        format:
-
-                (<short opt>, <long opt>, <key>, <default value>)
-
-        An example of a short opt is "f", which maps to a "-f" option.  An
-        example of a long opt is "foo", which maps to a "--foo" option.  Key
-        is the value of this option in the parsed option dictionary.  The
-        default value not only represents the default value assigned to the
+        'opts_table' is a list of options the operation supports.
+        The format of the list entries should be a tuple containing the
+        option and its default value:
+            (option, default_value)
+        It is valid to have other entries in the list when they are required
+        for additional option processing elsewhere. These are ignore here. If
+        the list entry is a tuple it must conform to the format oulined above.
+
+        The default value not only represents the default value assigned to the
         option, but it also implicitly determines how the option is parsed.  If
         the default value is True or False, the option doesn't take any
         arguments, can only be specified once, and if specified it inverts the
@@ -1239,140 +1236,98 @@
         specified multiple times, and if specified its value will be a list
         with all the specified argument values.
 
-        callbacks in 'table' specify callback functions that are invoked after
-        all options have been parsed.  Callback functions must have the
-        following signature:
-                callback(api_inst, opts, opts_new)
-
-        The opts parameter is a dictionary containing all the raw, parsed
-        options.  Callbacks should never update the contents of this
-        dictionary.  The opts_new parameter is a dictionary which is initially
-        a copy of the opts dictionary.  This is the dictionary that will be
-        returned to the caller of opts_parse().  If a callback function wants
-        to update the arguments dictionary that will be returned to the
-        caller, they should make all their updates to the opts_new dictionary.
-
-        'pargs_limit' specified how to handle extra arguments not parsed by
-        getops.  A value of -1 indicates that we allow an unlimited number of
-        extra arguments.  A value of 0 or greater indicates the number of
-        allowed additional unparsed options.
+        'opts_mapping' is a dict containing a mapping between the option name
+        and the short and long CLI specifier for that option in the form
+        { option : (short, long), ... }
+
+        An example of a short opt is "f", which maps to a "-f" option.  An
+        example of a long opt is "foo", which maps to a "--foo" option.  Option
+        is the value of this option in the parsed option dictionary.
 
         'usage_cb' is a function pointer that should display usage information
         and will be invoked if invalid arguments are detected."""
 
-
-        assert type(table) == list
-
-        # return dictionary
-        rv = dict()
-
-        # option string passed to getopt
+        # list for getopt long options
+        opts_l_list = []
+        # getopt str for short options
         opts_s_str = ""
-        # long options list passed to getopt
-        opts_l_list = list()
 
         # dict to map options returned by getopt to keys
         opts_keys = dict()
 
-        # sanity checking to make sure each option is unique
-        opts_s_set = set()
-        opts_l_set = set()
-        opts_seen = dict()
-
-        # callbacks to invoke after processing options
-        callbacks = []
-
-        # process each option entry
-        for entry in table:
-                # check for a callback
+        for entry in opts_table:
+                # option table contains functions for verification, ignore here
                 if type(entry) != tuple:
-                        callbacks.append(entry)
                         continue
-
-                # decode the table entry
-                # s: a short option, ex: -f
-                # l: a long option, ex: --foo
-                # k: the key value for the options dictionary
-                # v: the default value
-                (s, l, k, v) = entry
-
+                opt, default, = entry
+                assert opt in opts_mapping
+                sopt, lopt = opts_mapping[opt]
                 # make sure an option was specified
-                assert s or l
-                # sanity check the default value
-                assert (v == None) or (v == []) or \
-                    (type(v) == bool) or (type(v) == int)
-                # make sure each key is unique
-                assert k not in rv
-                # initialize the default return dictionary entry.
-                rv[k] = v
-                if l:
-                        # make sure each option is unique
-                        assert set([l]) not in opts_l_set
-                        opts_l_set |= set([l])
-
-                        if type(v) == bool:
-                                v = not v
-                                opts_l_list.append("%s" % l)
-                        elif type(v) == int:
-                                opts_l_list.append("%s" % l)
+                assert sopt or lopt
+                if lopt != "":
+                        if default is None or type(default) == list:
+                                opts_l_list.append("%s=" % lopt)
                         else:
-                                opts_l_list.append("%s=" % l)
-                        opts_keys["--%s" % l] = k
-                if s:
-                        # make sure each option is unique
-                        assert set([s]) not in opts_s_set
-                        opts_s_set |= set([s])
-
-                        if type(v) == bool:
-                                v = not v
-                                opts_s_str += "%s" % s
-                        elif type(v) == int:
-                                opts_s_str += "%s" % s
+                                opts_l_list.append("%s" % lopt)
+                        opts_keys["--%s" % lopt] = opt
+                if sopt != "":
+                        if default is None or type(default) == list:
+                                opts_s_str += "%s:" % sopt
                         else:
-                                opts_s_str += "%s:" % s
-                        opts_keys["-%s" % s] = k
-
-        # parse options
+                                opts_s_str += "%s" % sopt
+                        opts_keys["-%s" % sopt] = opt
+
+        # Parse options.
         try:
                 opts, pargs = getopt.getopt(args, opts_s_str, opts_l_list)
         except getopt.GetoptError, e:
                 usage_cb(_("illegal option -- %s") % e.opt, cmd=op)
 
-        if (pargs_limit >= 0) and (pargs_limit < len(pargs)):
-                usage_cb(_("illegal argument -- %s") % pargs[pargs_limit],
-                    cmd=op)
-
-        # update options dictionary with the specified options
-        for opt, arg in opts:
-                k = opts_keys[opt]
-                v = rv[k]
-
-                # check for duplicate options
-                if k in opts_seen and (type(v) != list and type(v) != int):
-                        if opt == opts_seen[k]:
-                                usage_cb(_("option '%s' repeated") % opt,
-                                    cmd=op)
-                        usage_cb(_("'%(optA)s' and '%(optB)s' have the same "
-                            "meaning") % {"optA": opts_seen[k], "optB": opt},
-                            cmd=op)
-                opts_seen[k] = opt
-
-                # update the return dict value
-                if type(v) == bool:
-                        rv[k] = not rv[k]
-                elif type(v) == list:
-                        rv[k].append(arg)
-                elif type(v) == int:
-                        rv[k] += 1
+        def get_default(option):
+                """Find the default value for a given option from opts_table."""
+                for x in opts_table:
+                        if type(x) != tuple:
+                                continue
+                        opt, default = x
+                        if option == opt:
+                                return default
+
+        # Assemble the options dictionary by passing in the right data types and
+        # take care of duplicates.
+        opt_dict = {}
+        for x in opts:
+                cli_opt, arg = x
+                opt = opts_keys[cli_opt]
+
+                # Determine required option type based on the default value.
+                default = get_default(opt)
+
+                # Handle duplicates for integer and list types.
+                if type(default) == int:
+                        if opt in opt_dict:
+                                opt_dict[opt] += 1
+                        else:
+                                opt_dict[opt] = 1
+                        continue
+                if type(default) == list:
+                        if opt in opt_dict:
+                                opt_dict[opt].append(arg)
+                        else:
+                                opt_dict[opt] = [arg]
+                        continue
+
+                # Boolean and string types can't be repeated.
+                if opt in opt_dict:
+                        raise api_errors.InvalidOptionError(
+                            api_errors.InvalidOptionError.OPT_REPEAT, [opt])
+
+                # For boolean options we have to toggle the default value.
+                if type(default) == bool:
+                        opt_dict[opt] = not default
                 else:
-                        rv[k] = arg
-
-        # invoke callbacks (cast to set() to eliminate dups)
-        rv_updated = rv.copy()
-        for cb in set(callbacks):
-                cb(op, api_inst, rv, rv_updated)
-
-        return (rv_updated, pargs)
+                        opt_dict[opt] = arg
+
+        return opt_dict, pargs
 
 def api_cmdpath():
         """Returns the path to the executable that is invoking the api client
--- a/src/pkg/manifests/package:pkg.p5m	Fri Mar 08 10:05:28 2013 -0800
+++ b/src/pkg/manifests/package:pkg.p5m	Mon Mar 11 15:15:21 2013 -0700
@@ -96,6 +96,7 @@
 file path=$(PYDIRVP)/pkg/client/linkedimage/common.py
 file path=$(PYDIRVP)/pkg/client/linkedimage/system.py
 file path=$(PYDIRVP)/pkg/client/linkedimage/zone.py
+file path=$(PYDIRVP)/pkg/client/options.py
 file path=$(PYDIRVP)/pkg/client/pkg_solver.py
 file path=$(PYDIRVP)/pkg/client/pkgdefs.py
 file path=$(PYDIRVP)/pkg/client/pkgplan.py
--- a/src/po/POTFILES.in	Fri Mar 08 10:05:28 2013 -0800
+++ b/src/po/POTFILES.in	Mon Mar 11 15:15:21 2013 -0700
@@ -35,6 +35,7 @@
 modules/client/imageconfig.py
 modules/client/imageplan.py
 modules/client/linkedimage/__init__.py
+modules/client/options.py
 modules/client/pkg_solver.py
 modules/client/progress.py
 modules/client/publisher.py