# Copyright 1999-2017 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

from __future__ import print_function

import argparse
import locale
import platform
import sys

import portage
portage.proxy.lazyimport.lazyimport(globals(),
	'logging',
	'portage.dep:Atom',
	'portage.util:writemsg_level',
	'textwrap',
	'_emerge.actions:load_emerge_config,run_action,' + \
		'validate_ebuild_environment',
	'_emerge.help:help@emerge_help',
	'_emerge.is_valid_package_atom:insert_category_into_atom'
)
from portage import os
from portage.sync import _SUBMODULE_PATH_MAP

if sys.hexversion >= 0x3000000:
	long = int

options=[
"--alphabetical",
"--ask-enter-invalid",
"--buildpkgonly",
"--changed-use",
"--changelog",    "--columns",
"--debug",
"--digest",
"--emptytree",
"--verbose-conflicts",
"--fetchonly",    "--fetch-all-uri",
"--ignore-default-opts",
"--noconfmem",
"--newrepo",
"--newuse",
"--nodeps",       "--noreplace",
"--nospinner",    "--oneshot",
"--onlydeps",     "--pretend",
"--quiet-repo-display",
"--quiet-unmerge-warn",
"--resume",
"--searchdesc",
"--skipfirst",
"--tree",
"--unordered-display",
"--update",
]

shortmapping={
"1":"--oneshot",
"B":"--buildpkgonly",
"c":"--depclean",
"C":"--unmerge",
"d":"--debug",
"e":"--emptytree",
"f":"--fetchonly", "F":"--fetch-all-uri",
"h":"--help",
"l":"--changelog",
"n":"--noreplace", "N":"--newuse",
"o":"--onlydeps",  "O":"--nodeps",
"p":"--pretend",   "P":"--prune",
"r":"--resume",
"s":"--search",    "S":"--searchdesc",
"t":"--tree",
"u":"--update",    "U":"--changed-use",
"V":"--version"
}

COWSAY_MOO = r"""

  Larry loves Gentoo (%s)

 _______________________
< Have you mooed today? >
 -----------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\\
                ||----w |
                ||     ||

"""

def multiple_actions(action1, action2):
	sys.stderr.write("\n!!! Multiple actions requested... Please choose one only.\n")
	sys.stderr.write("!!! '%s' or '%s'\n\n" % (action1, action2))
	sys.exit(1)

def insert_optional_args(args):
	"""
	Parse optional arguments and insert a value if one has
	not been provided. This is done before feeding the args
	to the optparse parser since that parser does not support
	this feature natively.
	"""

	class valid_integers(object):
		def __contains__(self, s):
			try:
				return int(s) >= 0
			except (ValueError, OverflowError):
				return False

	valid_integers = valid_integers()

	class valid_floats(object):
		def __contains__(self, s):
			try:
				return float(s) >= 0
			except (ValueError, OverflowError):
				return False

	valid_floats = valid_floats()

	y_or_n = ('y', 'n',)

	new_args = []

	default_arg_opts = {
		'--alert'                : y_or_n,
		'--ask'                  : y_or_n,
		'--autounmask'           : y_or_n,
		'--autounmask-continue'  : y_or_n,
		'--autounmask-only'      : y_or_n,
		'--autounmask-keep-masks': y_or_n,
		'--autounmask-unrestricted-atoms' : y_or_n,
		'--autounmask-write'     : y_or_n,
		'--binpkg-changed-deps'  : y_or_n,
		'--buildpkg'             : y_or_n,
		'--changed-deps'         : y_or_n,
		'--complete-graph'       : y_or_n,
		'--deep'       : valid_integers,
		'--depclean-lib-check'   : y_or_n,
		'--deselect'             : y_or_n,
		'--binpkg-respect-use'   : y_or_n,
		'--fail-clean'           : y_or_n,
		'--fuzzy-search'         : y_or_n,
		'--getbinpkg'            : y_or_n,
		'--getbinpkgonly'        : y_or_n,
		'--jobs'       : valid_integers,
		'--keep-going'           : y_or_n,
		'--load-average'         : valid_floats,
		'--onlydeps-with-rdeps'  : y_or_n,
		'--package-moves'        : y_or_n,
		'--quiet'                : y_or_n,
		'--quiet-build'          : y_or_n,
		'--quiet-fail'           : y_or_n,
		'--read-news'            : y_or_n,
		'--rebuild-if-new-slot': y_or_n,
		'--rebuild-if-new-rev'   : y_or_n,
		'--rebuild-if-new-ver'   : y_or_n,
		'--rebuild-if-unbuilt'   : y_or_n,
		'--rebuilt-binaries'     : y_or_n,
		'--root-deps'  : ('rdeps',),
		'--select'               : y_or_n,
		'--selective'            : y_or_n,
		"--use-ebuild-visibility": y_or_n,
		'--usepkg'               : y_or_n,
		'--usepkgonly'           : y_or_n,
		'--verbose'              : y_or_n,
		'--verbose-slot-rebuilds': y_or_n,
		'--with-test-deps'       : y_or_n,
	}

	short_arg_opts = {
		'D' : valid_integers,
		'j' : valid_integers,
	}

	# Don't make things like "-kn" expand to "-k n"
	# since existence of -n makes it too ambiguous.
	short_arg_opts_n = {
		'a' : y_or_n,
		'A' : y_or_n,
		'b' : y_or_n,
		'g' : y_or_n,
		'G' : y_or_n,
		'k' : y_or_n,
		'K' : y_or_n,
		'q' : y_or_n,
		'v' : y_or_n,
		'w' : y_or_n,
	}

	arg_stack = args[:]
	arg_stack.reverse()
	while arg_stack:
		arg = arg_stack.pop()

		default_arg_choices = default_arg_opts.get(arg)
		if default_arg_choices is not None:
			new_args.append(arg)
			if arg_stack and arg_stack[-1] in default_arg_choices:
				new_args.append(arg_stack.pop())
			else:
				# insert default argument
				new_args.append('True')
			continue

		if arg[:1] != "-" or arg[:2] == "--":
			new_args.append(arg)
			continue

		match = None
		for k, arg_choices in short_arg_opts.items():
			if k in arg:
				match = k
				break

		if match is None:
			for k, arg_choices in short_arg_opts_n.items():
				if k in arg:
					match = k
					break

		if match is None:
			new_args.append(arg)
			continue

		if len(arg) == 2:
			new_args.append(arg)
			if arg_stack and arg_stack[-1] in arg_choices:
				new_args.append(arg_stack.pop())
			else:
				# insert default argument
				new_args.append('True')
			continue

		# Insert an empty placeholder in order to
		# satisfy the requirements of optparse.

		new_args.append("-" + match)
		opt_arg = None
		saved_opts = None

		if arg[1:2] == match:
			if match not in short_arg_opts_n and arg[2:] in arg_choices:
				opt_arg = arg[2:]
			else:
				saved_opts = arg[2:]
				opt_arg = "True"
		else:
			saved_opts = arg[1:].replace(match, "")
			opt_arg = "True"

		if opt_arg is None and arg_stack and \
			arg_stack[-1] in arg_choices:
			opt_arg = arg_stack.pop()

		if opt_arg is None:
			new_args.append("True")
		else:
			new_args.append(opt_arg)

		if saved_opts is not None:
			# Recycle these on arg_stack since they
			# might contain another match.
			arg_stack.append("-" + saved_opts)

	return new_args

def _find_bad_atoms(atoms, less_strict=False):
	"""
	Declares all atoms as invalid that have an operator,
	a use dependency, a blocker or a repo spec.
	It accepts atoms with wildcards.
	In less_strict mode it accepts operators and repo specs.
	"""
	bad_atoms = []
	for x in ' '.join(atoms).split():
		atom = x
		if "/" not in x.split(":")[0]:
			x_cat = insert_category_into_atom(x, 'dummy-category')
			if x_cat is not None:
				atom = x_cat

		bad_atom = False
		try:
			atom = Atom(atom, allow_wildcard=True, allow_repo=less_strict)
		except portage.exception.InvalidAtom:
			bad_atom = True

		if bad_atom or (atom.operator and not less_strict) or atom.blocker or atom.use:
			bad_atoms.append(x)
	return bad_atoms


def parse_opts(tmpcmdline, silent=False):
	myaction=None
	myopts = {}
	myfiles=[]

	actions = frozenset([
		"clean", "check-news", "config", "depclean", "help",
		"info", "list-sets", "metadata", "moo",
		"prune", "rage-clean", "regen",  "search",
		"sync",  "unmerge", "version",
	])

	longopt_aliases = {"--cols":"--columns", "--skip-first":"--skipfirst"}
	y_or_n = ("y", "n")
	true_y_or_n = ("True", "y", "n")
	true_y = ("True", "y")
	argument_options = {

		"--alert": {
			"shortopt" : "-A",
			"help"    : "alert (terminal bell) on prompts",
			"choices" : true_y_or_n
		},

		"--ask": {
			"shortopt" : "-a",
			"help"    : "prompt before performing any actions",
			"choices" : true_y_or_n
		},

		"--autounmask": {
			"help"    : "automatically unmask packages",
			"choices" : true_y_or_n
		},

		"--autounmask-backtrack": {
			"help": ("continue backtracking when there are autounmask "
				"configuration changes"),
			"choices":("y", "n")
		},

		"--autounmask-continue": {
			"help"    : "write autounmask changes and continue",
			"choices" : true_y_or_n
		},

		"--autounmask-only": {
			"help"    : "only perform --autounmask",
			"choices" : true_y_or_n
		},

		"--autounmask-unrestricted-atoms": {
			"help"    : "write autounmask changes with >= atoms if possible",
			"choices" : true_y_or_n
		},

		"--autounmask-keep-masks": {
			"help"    : "don't add package.unmask entries",
			"choices" : true_y_or_n
		},

		"--autounmask-write": {
			"help"    : "write changes made by --autounmask to disk",
			"choices" : true_y_or_n
		},

		"--accept-properties": {
			"help":"temporarily override ACCEPT_PROPERTIES",
			"action":"store"
		},

		"--accept-restrict": {
			"help":"temporarily override ACCEPT_RESTRICT",
			"action":"store"
		},

		"--backtrack": {

			"help"   : "Specifies how many times to backtrack if dependency " + \
				"calculation fails ",

			"action" : "store"
		},

		"--binpkg-changed-deps": {
			"help"    : ("reject binary packages with outdated "
				"dependencies"),
			"choices" : true_y_or_n
		},

		"--buildpkg": {
			"shortopt" : "-b",
			"help"     : "build binary packages",
			"choices"  : true_y_or_n
		},

		"--buildpkg-exclude": {
			"help"   :"A space separated list of package atoms for which " + \
				"no binary packages should be built. This option overrides all " + \
				"possible ways to enable building of binary packages.",

			"action" : "append"
		},

		"--changed-deps": {
			"help"    : ("replace installed packages with "
				"outdated dependencies"),
			"choices" : true_y_or_n
		},

		"--config-root": {
			"help":"specify the location for portage configuration files",
			"action":"store"
		},
		"--color": {
			"help":"enable or disable color output",
			"choices":("y", "n")
		},

		"--complete-graph": {
			"help"    : "completely account for all known dependencies",
			"choices" : true_y_or_n
		},

		"--complete-graph-if-new-use": {
			"help"    : "trigger --complete-graph behavior if USE or IUSE will change for an installed package",
			"choices" : y_or_n
		},

		"--complete-graph-if-new-ver": {
			"help"    : "trigger --complete-graph behavior if an installed package version will change (upgrade or downgrade)",
			"choices" : y_or_n
		},

		"--deep": {

			"shortopt" : "-D",

			"help"   : "Specifies how deep to recurse into dependencies " + \
				"of packages given as arguments. If no argument is given, " + \
				"depth is unlimited. Default behavior is to skip " + \
				"dependencies of installed packages.",

			"action" : "store"
		},

		"--depclean-lib-check": {
			"help"    : "check for consumers of libraries before removing them",
			"choices" : true_y_or_n
		},

		"--deselect": {
			"help"    : "remove atoms/sets from the world file",
			"choices" : true_y_or_n
		},

		"--dynamic-deps": {
			"help": "substitute the dependencies of installed packages with the dependencies of unbuilt ebuilds",
			"choices": y_or_n
		},

		"--exclude": {
			"help"   :"A space separated list of package names or slot atoms. " + \
				"Emerge won't  install any ebuild or binary package that " + \
				"matches any of the given package atoms.",

			"action" : "append"
		},

		"--fail-clean": {
			"help"    : "clean temp files after build failure",
			"choices" : true_y_or_n
		},

		"--fuzzy-search": {
			"help": "Enable or disable fuzzy search",
			"choices": true_y_or_n
		},

		"--ignore-built-slot-operator-deps": {
			"help": "Ignore the slot/sub-slot := operator parts of dependencies that have "
				"been recorded when packages where built. This option is intended "
				"only for debugging purposes, and it only affects built packages "
				"that specify slot/sub-slot := operator dependencies using the "
				"experimental \"4-slot-abi\" EAPI.",
			"choices": y_or_n
		},

		"--ignore-soname-deps": {
			"help": "Ignore the soname dependencies of binary and "
				"installed packages. This option is enabled by "
				"default, since soname dependencies are relatively "
				"new, and the required metadata is not guaranteed to "
				"exist for binary and installed packages built with "
				"older versions of portage.",
			"choices": y_or_n
		},

		"--jobs": {

			"shortopt" : "-j",

			"help"   : "Specifies the number of packages to build " + \
				"simultaneously.",

			"action" : "store"
		},

		"--keep-going": {
			"help"    : "continue as much as possible after an error",
			"choices" : true_y_or_n
		},

		"--load-average": {

			"help"   :"Specifies that no new builds should be started " + \
				"if there are other builds running and the load average " + \
				"is at least LOAD (a floating-point number).",

			"action" : "store"
		},

		"--misspell-suggestions": {
			"help"    : "enable package name misspell suggestions",
			"choices" : ("y", "n")
		},

		"--with-bdeps": {
			"help":"include unnecessary build time dependencies",
			"choices":("y", "n")
		},
		"--with-bdeps-auto": {
			"help":("automatically enable --with-bdeps for installation"
				" actions, unless --usepkg is enabled"),
			"choices":("y", "n")
		},
		"--reinstall": {
			"help":"specify conditions to trigger package reinstallation",
			"choices":["changed-use"]
		},

		"--reinstall-atoms": {
			"help"   :"A space separated list of package names or slot atoms. " + \
				"Emerge will treat matching packages as if they are not " + \
				"installed, and reinstall them if necessary. Implies --deep.",

			"action" : "append",
		},

		"--binpkg-respect-use": {
			"help"    : "discard binary packages if their use flags \
				don't match the current configuration",
			"choices" : true_y_or_n
		},

		"--getbinpkg": {
			"shortopt" : "-g",
			"help"     : "fetch binary packages",
			"choices"  : true_y_or_n
		},

		"--getbinpkgonly": {
			"shortopt" : "-G",
			"help"     : "fetch binary packages only",
			"choices"  : true_y_or_n
		},

		"--usepkg-exclude": {
			"help"   :"A space separated list of package names or slot atoms. " + \
				"Emerge will ignore matching binary packages. ",

			"action" : "append",
		},

		"--onlydeps-with-rdeps": {
			"help"    : "modify interpretation of depedencies",
			"choices" : true_y_or_n
		},

		"--rebuild-exclude": {
			"help"   :"A space separated list of package names or slot atoms. " + \
				"Emerge will not rebuild these packages due to the " + \
				"--rebuild flag. ",

			"action" : "append",
		},

		"--rebuild-ignore": {
			"help"   :"A space separated list of package names or slot atoms. " + \
				"Emerge will not rebuild packages that depend on matching " + \
				"packages due to the --rebuild flag. ",

			"action" : "append",
		},

		"--package-moves": {
			"help"     : "perform package moves when necessary",
			"choices"  : true_y_or_n
		},

		"--prefix": {
			"help"     : "specify the installation prefix",
			"action"   : "store"
		},

		"--pkg-format": {
			"help"     : "format of result binary package",
			"action"   : "store",
		},

		"--quiet": {
			"shortopt" : "-q",
			"help"     : "reduced or condensed output",
			"choices"  : true_y_or_n
		},

		"--quiet-build": {
			"help"     : "redirect build output to logs",
			"choices"  : true_y_or_n,
		},

		"--quiet-fail": {
			"help"     : "suppresses display of the build log on stdout",
			"choices"  : true_y_or_n,
		},

		"--read-news": {
			"help"    : "offer to read unread news via eselect",
			"choices" : true_y_or_n
		},


		"--rebuild-if-new-slot": {
			"help"     : ("Automatically rebuild or reinstall packages when slot/sub-slot := "
				"operator dependencies can be satisfied by a newer slot, so that "
				"older packages slots will become eligible for removal by the "
				"--depclean action as soon as possible."),
			"choices"  : true_y_or_n
		},

		"--rebuild-if-new-rev": {
			"help"     : "Rebuild packages when dependencies that are " + \
				"used at both build-time and run-time are built, " + \
				"if the dependency is not already installed with the " + \
				"same version and revision.",
			"choices"  : true_y_or_n
		},

		"--rebuild-if-new-ver": {
			"help"     : "Rebuild packages when dependencies that are " + \
				"used at both build-time and run-time are built, " + \
				"if the dependency is not already installed with the " + \
				"same version. Revision numbers are ignored.",
			"choices"  : true_y_or_n
		},

		"--rebuild-if-unbuilt": {
			"help"     : "Rebuild packages when dependencies that are " + \
				"used at both build-time and run-time are built.",
			"choices"  : true_y_or_n
		},

		"--rebuilt-binaries": {
			"help"     : "replace installed packages with binary " + \
			             "packages that have been rebuilt",
			"choices"  : true_y_or_n
		},

		"--rebuilt-binaries-timestamp": {
			"help"   : "use only binaries that are newer than this " + \
			           "timestamp for --rebuilt-binaries",
			"action" : "store"
		},

		"--root": {
		 "help"   : "specify the target root filesystem for merging packages",
		 "action" : "store"
		},

		"--root-deps": {
			"help"    : "modify interpretation of depedencies",
			"choices" :("True", "rdeps")
		},

		"--search-index": {
			"help": "Enable or disable indexed search (enabled by default)",
			"choices": y_or_n
		},

		"--search-similarity": {
			"help": ("Set minimum similarity percentage for fuzzy seach "
				"(a floating-point number between 0 and 100)"),
			"action": "store"
		},

		"--select": {
			"shortopt" : "-w",
			"help"    : "add specified packages to the world set " + \
			            "(inverse of --oneshot)",
			"choices" : true_y_or_n
		},

		"--selective": {
			"help"    : "identical to --noreplace",
			"choices" : true_y_or_n
		},

		"--sync-submodule": {
			"help"    : ("Restrict sync to the specified submodule(s)."
				" (--sync action only)"),
			"choices" : tuple(_SUBMODULE_PATH_MAP),
			"action" : "append",
		},

		"--use-ebuild-visibility": {
			"help"     : "use unbuilt ebuild metadata for visibility checks on built packages",
			"choices"  : true_y_or_n
		},

		"--useoldpkg-atoms": {
			"help"   :"A space separated list of package names or slot atoms. " + \
				"Emerge will prefer matching binary packages over newer unbuilt packages. ",

			"action" : "append",
		},

		"--usepkg": {
			"shortopt" : "-k",
			"help"     : "use binary packages",
			"choices"  : true_y_or_n
		},

		"--usepkgonly": {
			"shortopt" : "-K",
			"help"     : "use only binary packages",
			"choices"  : true_y_or_n
		},

		"--verbose": {
			"shortopt" : "-v",
			"help"     : "verbose output",
			"choices"  : true_y_or_n
		},
		"--verbose-slot-rebuilds": {
			"help"     : "verbose slot rebuild output",
			"choices"  : true_y_or_n
		},
		"--with-test-deps": {
			"help"     : "pull in test deps for packages " + \
				"matched by arguments",
			"choices"  : true_y_or_n
		},
	}

	parser = argparse.ArgumentParser(add_help=False)

	for action_opt in actions:
		parser.add_argument("--" + action_opt, action="store_true",
			dest=action_opt.replace("-", "_"), default=False)
	for myopt in options:
		parser.add_argument(myopt, action="store_true",
			dest=myopt.lstrip("--").replace("-", "_"), default=False)
	for shortopt, longopt in shortmapping.items():
		parser.add_argument("-" + shortopt, action="store_true",
			dest=longopt.lstrip("--").replace("-", "_"), default=False)
	for myalias, myopt in longopt_aliases.items():
		parser.add_argument(myalias, action="store_true",
			dest=myopt.lstrip("--").replace("-", "_"), default=False)

	for myopt, kwargs in argument_options.items():
		shortopt = kwargs.pop("shortopt", None)
		args = [myopt]
		if shortopt is not None:
			args.append(shortopt)
		parser.add_argument(dest=myopt.lstrip("--").replace("-", "_"),
			*args, **kwargs)

	tmpcmdline = insert_optional_args(tmpcmdline)

	myoptions, myargs = parser.parse_known_args(args=tmpcmdline)

	if myoptions.alert in true_y:
		myoptions.alert = True
	else:
		myoptions.alert = None

	if myoptions.ask in true_y:
		myoptions.ask = True
	else:
		myoptions.ask = None

	if myoptions.autounmask in true_y:
		myoptions.autounmask = True

	if myoptions.autounmask_continue in true_y:
		myoptions.autounmask_continue = True

	if myoptions.autounmask_only in true_y:
		myoptions.autounmask_only = True
	else:
		myoptions.autounmask_only = None

	if myoptions.autounmask_unrestricted_atoms in true_y:
		myoptions.autounmask_unrestricted_atoms = True

	if myoptions.autounmask_keep_masks in true_y:
		myoptions.autounmask_keep_masks = True

	if myoptions.autounmask_write in true_y:
		myoptions.autounmask_write = True

	if myoptions.binpkg_changed_deps is not None:
		if myoptions.binpkg_changed_deps in true_y:
			myoptions.binpkg_changed_deps = 'y'
		else:
			myoptions.binpkg_changed_deps = 'n'

	if myoptions.buildpkg in true_y:
		myoptions.buildpkg = True

	if myoptions.buildpkg_exclude:
		bad_atoms = _find_bad_atoms(myoptions.buildpkg_exclude, less_strict=True)
		if bad_atoms and not silent:
			parser.error("Invalid Atom(s) in --buildpkg-exclude parameter: '%s'\n" % \
				(",".join(bad_atoms),))

	if myoptions.changed_deps is not None:
		if myoptions.changed_deps in true_y:
			myoptions.changed_deps = 'y'
		else:
			myoptions.changed_deps = 'n'

	if myoptions.changed_use is not False:
		myoptions.reinstall = "changed-use"
		myoptions.changed_use = False

	if myoptions.deselect in true_y:
		myoptions.deselect = True

	if myoptions.binpkg_respect_use is not None:
		if myoptions.binpkg_respect_use in true_y:
			myoptions.binpkg_respect_use = 'y'
		else:
			myoptions.binpkg_respect_use = 'n'

	if myoptions.complete_graph in true_y:
		myoptions.complete_graph = True
	else:
		myoptions.complete_graph = None

	if myoptions.depclean_lib_check in true_y:
		myoptions.depclean_lib_check = True

	if myoptions.exclude:
		bad_atoms = _find_bad_atoms(myoptions.exclude)
		if bad_atoms and not silent:
			parser.error("Invalid Atom(s) in --exclude parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
				(",".join(bad_atoms),))

	if myoptions.reinstall_atoms:
		bad_atoms = _find_bad_atoms(myoptions.reinstall_atoms)
		if bad_atoms and not silent:
			parser.error("Invalid Atom(s) in --reinstall-atoms parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
				(",".join(bad_atoms),))

	if myoptions.rebuild_exclude:
		bad_atoms = _find_bad_atoms(myoptions.rebuild_exclude)
		if bad_atoms and not silent:
			parser.error("Invalid Atom(s) in --rebuild-exclude parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
				(",".join(bad_atoms),))

	if myoptions.rebuild_ignore:
		bad_atoms = _find_bad_atoms(myoptions.rebuild_ignore)
		if bad_atoms and not silent:
			parser.error("Invalid Atom(s) in --rebuild-ignore parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
				(",".join(bad_atoms),))

	if myoptions.usepkg_exclude:
		bad_atoms = _find_bad_atoms(myoptions.usepkg_exclude)
		if bad_atoms and not silent:
			parser.error("Invalid Atom(s) in --usepkg-exclude parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
				(",".join(bad_atoms),))

	if myoptions.useoldpkg_atoms:
		bad_atoms = _find_bad_atoms(myoptions.useoldpkg_atoms)
		if bad_atoms and not silent:
			parser.error("Invalid Atom(s) in --useoldpkg-atoms parameter: '%s' (only package names and slot atoms (with wildcards) allowed)\n" % \
				(",".join(bad_atoms),))

	if myoptions.fail_clean in true_y:
		myoptions.fail_clean = True

	if myoptions.fuzzy_search in true_y:
		myoptions.fuzzy_search = True

	if myoptions.getbinpkg in true_y:
		myoptions.getbinpkg = True
	else:
		myoptions.getbinpkg = None

	if myoptions.getbinpkgonly in true_y:
		myoptions.getbinpkgonly = True
	else:
		myoptions.getbinpkgonly = None

	if myoptions.keep_going in true_y:
		myoptions.keep_going = True
	else:
		myoptions.keep_going = None

	if myoptions.package_moves in true_y:
		myoptions.package_moves = True

	if myoptions.quiet in true_y:
		myoptions.quiet = True
	else:
		myoptions.quiet = None

	if myoptions.quiet_build in true_y:
		myoptions.quiet_build = 'y'

	if myoptions.quiet_fail in true_y:
		myoptions.quiet_fail = 'y'

	if myoptions.read_news in true_y:
		myoptions.read_news = True
	else:
		myoptions.read_news = None


	if myoptions.rebuild_if_new_slot in true_y:
		myoptions.rebuild_if_new_slot = 'y'

	if myoptions.rebuild_if_new_ver in true_y:
		myoptions.rebuild_if_new_ver = True
	else:
		myoptions.rebuild_if_new_ver = None

	if myoptions.rebuild_if_new_rev in true_y:
		myoptions.rebuild_if_new_rev = True
		myoptions.rebuild_if_new_ver = None
	else:
		myoptions.rebuild_if_new_rev = None

	if myoptions.rebuild_if_unbuilt in true_y:
		myoptions.rebuild_if_unbuilt = True
		myoptions.rebuild_if_new_rev = None
		myoptions.rebuild_if_new_ver = None
	else:
		myoptions.rebuild_if_unbuilt = None

	if myoptions.rebuilt_binaries in true_y:
		myoptions.rebuilt_binaries = True

	if myoptions.root_deps in true_y:
		myoptions.root_deps = True

	if myoptions.select in true_y:
		myoptions.select = True
		myoptions.oneshot = False
	elif myoptions.select == "n":
		myoptions.oneshot = True

	if myoptions.selective in true_y:
		myoptions.selective = True

	if myoptions.backtrack is not None:

		try:
			backtrack = int(myoptions.backtrack)
		except (OverflowError, ValueError):
			backtrack = -1

		if backtrack < 0:
			backtrack = None
			if not silent:
				parser.error("Invalid --backtrack parameter: '%s'\n" % \
					(myoptions.backtrack,))

		myoptions.backtrack = backtrack

	if myoptions.deep is not None:
		deep = None
		if myoptions.deep == "True":
			deep = True
		else:
			try:
				deep = int(myoptions.deep)
			except (OverflowError, ValueError):
				deep = -1

		if deep is not True and deep < 0:
			deep = None
			if not silent:
				parser.error("Invalid --deep parameter: '%s'\n" % \
					(myoptions.deep,))

		myoptions.deep = deep

	if myoptions.jobs:
		jobs = None
		if myoptions.jobs == "True":
			jobs = True
		else:
			try:
				jobs = int(myoptions.jobs)
			except ValueError:
				jobs = -1

		if jobs is not True and \
			jobs < 1:
			jobs = None
			if not silent:
				parser.error("Invalid --jobs parameter: '%s'\n" % \
					(myoptions.jobs,))

		myoptions.jobs = jobs

	if myoptions.load_average == "True":
		myoptions.load_average = None

	if myoptions.load_average:
		try:
			load_average = float(myoptions.load_average)
		except ValueError:
			load_average = 0.0

		if load_average <= 0.0:
			load_average = None
			if not silent:
				parser.error("Invalid --load-average parameter: '%s'\n" % \
					(myoptions.load_average,))

		myoptions.load_average = load_average

	if myoptions.rebuilt_binaries_timestamp:
		try:
			rebuilt_binaries_timestamp = int(myoptions.rebuilt_binaries_timestamp)
		except ValueError:
			rebuilt_binaries_timestamp = -1

		if rebuilt_binaries_timestamp < 0:
			rebuilt_binaries_timestamp = 0
			if not silent:
				parser.error("Invalid --rebuilt-binaries-timestamp parameter: '%s'\n" % \
					(myoptions.rebuilt_binaries_timestamp,))

		myoptions.rebuilt_binaries_timestamp = rebuilt_binaries_timestamp

	if myoptions.search_similarity:
		try:
			search_similarity = float(myoptions.search_similarity)
		except ValueError:
			parser.error("Invalid --search-similarity parameter "
				"(not a number): '{}'\n".format(
				myoptions.search_similarity))

		if search_similarity < 0 or search_similarity > 100:
			parser.error("Invalid --search-similarity parameter "
				"(not between 0 and 100): '{}'\n".format(
				myoptions.search_similarity))

		myoptions.search_similarity = search_similarity

	if myoptions.use_ebuild_visibility in true_y:
		myoptions.use_ebuild_visibility = True
	else:
		# None or "n"
		pass

	if myoptions.usepkg in true_y:
		myoptions.usepkg = True
	else:
		myoptions.usepkg = None

	if myoptions.usepkgonly in true_y:
		myoptions.usepkgonly = True
	else:
		myoptions.usepkgonly = None

	if myoptions.verbose in true_y:
		myoptions.verbose = True
	else:
		myoptions.verbose = None

	if myoptions.with_test_deps in true_y:
		myoptions.with_test_deps = True
	else:
		myoptions.with_test_deps = None

	for myopt in options:
		v = getattr(myoptions, myopt.lstrip("--").replace("-", "_"))
		if v:
			myopts[myopt] = True

	for myopt in argument_options:
		v = getattr(myoptions, myopt.lstrip("--").replace("-", "_"), None)
		if v is not None:
			myopts[myopt] = v

	if myoptions.searchdesc:
		myoptions.search = True

	for action_opt in actions:
		v = getattr(myoptions, action_opt.replace("-", "_"))
		if v:
			if myaction:
				multiple_actions(myaction, action_opt)
				sys.exit(1)
			myaction = action_opt

	if myaction is None and myoptions.deselect is True:
		myaction = 'deselect'

	myfiles += myargs

	return myaction, myopts, myfiles

def profile_check(trees, myaction):
	if myaction in ("help", "info", "search", "sync", "version"):
		return os.EX_OK
	for root_trees in trees.values():
		if root_trees["root_config"].settings.profiles:
			continue
		# generate some profile related warning messages
		validate_ebuild_environment(trees)
		msg = ("Your current profile is invalid. If you have just changed "
			"your profile configuration, you should revert back to the "
			"previous configuration. Allowed actions are limited to "
			"--help, --info, --search, --sync, and --version.")
		writemsg_level("".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)),
			level=logging.ERROR, noiselevel=-1)
		return 1
	return os.EX_OK

def emerge_main(args=None):
	"""
	@param args: command arguments (default: sys.argv[1:])
	@type args: list
	"""
	if args is None:
		args = sys.argv[1:]

	args = portage._decode_argv(args)

	# Use system locale.
	try:
		locale.setlocale(locale.LC_ALL, "")
	except locale.Error as e:
		writemsg_level("setlocale: %s\n" % e, level=logging.WARN)

	# Disable color until we're sure that it should be enabled (after
	# EMERGE_DEFAULT_OPTS has been parsed).
	portage.output.havecolor = 0

	# This first pass is just for options that need to be known as early as
	# possible, such as --config-root.  They will be parsed again later,
	# together with EMERGE_DEFAULT_OPTS (which may vary depending on the
	# the value of --config-root).
	myaction, myopts, myfiles = parse_opts(args, silent=True)
	if "--debug" in myopts:
		os.environ["PORTAGE_DEBUG"] = "1"
	if "--config-root" in myopts:
		os.environ["PORTAGE_CONFIGROOT"] = myopts["--config-root"]
	if "--root" in myopts:
		os.environ["ROOT"] = myopts["--root"]
	if "--prefix" in myopts:
		os.environ["EPREFIX"] = myopts["--prefix"]
	if "--accept-properties" in myopts:
		os.environ["ACCEPT_PROPERTIES"] = myopts["--accept-properties"]
	if "--accept-restrict" in myopts:
		os.environ["ACCEPT_RESTRICT"] = myopts["--accept-restrict"]

	# optimize --help (no need to load config / EMERGE_DEFAULT_OPTS)
	if myaction == "help":
		emerge_help()
		return os.EX_OK
	elif myaction == "moo":
		print(COWSAY_MOO % platform.system())
		return os.EX_OK
	elif myaction == "sync":
		# need to set this to True now in order for the repository config
		# loading to allow new repos with non-existent directories
		portage._sync_mode = True

	# Verify that /dev/null exists and is a device file as a cheap early
	# filter for obviously broken /dev/s.
	try:
		if os.stat(os.devnull).st_rdev == 0:
			writemsg_level("Failed to validate a sane '/dev'.\n"
				  "'/dev/null' is not a device file.\n",
				  level=logging.ERROR, noiselevel=-1)
			return 1
	except OSError:
		writemsg_level("Failed to validate a sane '/dev'.\n"
				 "'/dev/null' does not exist.\n",
				 level=logging.ERROR, noiselevel=-1)
		return 1

	# Verify that BASH process substitution works as another cheap early
	# filter. Process substitution uses '/dev/fd'.
	with open(os.devnull, 'r+b') as dev_null:
		fd_pipes = {
			0: dev_null.fileno(),
			1: dev_null.fileno(),
			2: dev_null.fileno(),
		}
		if portage.process.spawn_bash("[[ $(< <(echo foo) ) == foo ]]",
			fd_pipes=fd_pipes) != 0:
			writemsg_level("Failed to validate a sane '/dev'.\n"
				"bash process substitution doesn't work; this may be an "
				"indication of a broken '/dev/fd'.\n",
				level=logging.ERROR, noiselevel=-1)
			return 1

	# Portage needs to ensure a sane umask for the files it creates.
	os.umask(0o22)
	emerge_config = load_emerge_config(
		action=myaction, args=myfiles, opts=myopts)

	# Make locale variables from configuration files (make.defaults, make.conf) affect locale of emerge process.
	for locale_var_name in ("LANGUAGE", "LC_ALL", "LC_ADDRESS", "LC_COLLATE", "LC_CTYPE",
		"LC_IDENTIFICATION", "LC_MEASUREMENT", "LC_MESSAGES", "LC_MONETARY",
		"LC_NAME", "LC_NUMERIC", "LC_PAPER", "LC_TELEPHONE", "LC_TIME", "LANG"):
		locale_var_value = emerge_config.running_config.settings.get(locale_var_name)
		if locale_var_value is not None:
			os.environ.setdefault(locale_var_name, locale_var_value)
	try:
		locale.setlocale(locale.LC_ALL, "")
	except locale.Error as e:
		writemsg_level("setlocale: %s\n" % e, level=logging.WARN)

	rval = profile_check(emerge_config.trees, emerge_config.action)
	if rval != os.EX_OK:
		return rval

	tmpcmdline = []
	if "--ignore-default-opts" not in myopts:
		tmpcmdline.extend(portage.util.shlex_split(
			emerge_config.target_config.settings.get(
			"EMERGE_DEFAULT_OPTS", "")))
	tmpcmdline.extend(args)
	emerge_config.action, emerge_config.opts, emerge_config.args = \
		parse_opts(tmpcmdline)

	try:
		return run_action(emerge_config)
	finally:
		# Call destructors for our portdbapi instances.
		for x in emerge_config.trees.values():
			if "porttree" in x.lazy_items:
				continue
			x["porttree"].dbapi.close_caches()
