#!/usr/bin/python -O

import sys
import os
import re
import string
import readline
import getopt
from output import *
import portage
import portage_exception

# you might want to change this
# items that directly depend on these are considered as valid (needing fixing)
valid_marker = ['virtual/x11','x11-base/xorg-x11']

# we import portage later
sys.path.insert(0, "/usr/lib/portage/pym")

portdir = portage.settings["PORTDIR"]
portdb = portage.portdbapi(portdir)


def main():
	os.environ["PORTAGE_CALLER"]="repoman"
	args = sys.argv[1:]
	# generate
	if len(args) == 0:
		print 'usage: find_broken_modular_package.py CP [CP...]'
		sys.exit(1)
	else:
		for cpv in sys.argv[1:]:
			check_package(cpv)
	return

def check_package(cpv):
	#main()
	# line 267
	myfiles = [cpv]
	# line 3236
	mydepgraph=depgraph(myaction,myopts)
	# line 3255
	retval,favorites=mydepgraph.select_files(myfiles)
	# line 3140
	alldeps=mydepgraph.digraph.allnodes()        
	# custom
	reason=[]
	for i in alldeps:
		iv = i.split(' ')
		if 'blocks'==iv[0]:
			for v in valid_marker:
				if v in iv[3]:
					reason.append(iv[3])

	if len(reason) > 0:
		real = check_not_false_postive(cpv)
		if real == False:
			print 'BAD-FALSE-POSITIVE',cpv
		else:
			print 'BAD(DEP=(%s);MATCHES=(%s)) %s' % (string.join(real,','),string.join(reason,','),cpv)
	else:
		# don't print fixed stuff
		#print 'GOOD',cpv
		pass

# if this returns False, then you have a false positive
# otherwise it returns items in valid_marker triggered it
def check_not_false_postive(cp):
	cpv = portage.portdb.xmatch("bestmatch-visible", cp)
	aux = portdb.aux_get(cpv,['DEPEND','RDEPEND'])
	depend = aux[0]
	rdepend = aux[1]
	real = False
	for v in valid_marker:
		if v in depend or v in rdepend:
			if real == False:
				real = [v]
			else:
				real.append(v)
	return real

#---------- the following is for depgraph
def update_null_spinner():
	return
myaction = 'pretend'
myopts = ['--pretend']
myparams=["self","recurse"]
spinpos = 0
update_spinner = update_null_spinner
#---------- end of stuff for depgraph
# the following is lifted straight from emerge.py
# it's the entire depgraph class
# ---------------------------------
class depgraph:
	def __init__(self,myaction,myopts):
		global olddbapi
		self.pkgsettings = portage.config(clone=portage.settings)
		if not self.pkgsettings["ARCH"]:
			portage.writemsg(red("\a!!! ARCH is not set... Are you missing the /etc/make.profile symlink?\n"))
			portage.writemsg(red("\a!!! Is the symlink correct? Is your portage tree complete?\n\n"))
			sys.exit(9)
		self.applied_useflags = {}

		self.missingbins=[]
		self.myaction=myaction
		self.digraph=portage.digraph()
		self.orderedkeys=[]
		self.outdatedpackages=[]
		self.mydbapi={}
		self.mydbapi["/"] = portage.fakedbapi()
		if "empty" not in myparams or portage.root != "/":
			for pkg in portage.db["/"]["vartree"].getallcpv():
				self.mydbapi["/"].cpv_inject(pkg)
		if portage.root != "/":
			self.mydbapi[portage.root] = portage.fakedbapi()
			if "empty" not in myparams:
				for pkg in portage.db[portage.root]["vartree"].getallcpv():
					self.mydbapi[portage.root].cpv_inject(pkg)

		if "--usepkg" in myopts:
			portage.db["/"]["bintree"].populate(("--getbinpkg" in myopts), ("--getbinpkgonly" in myopts))

	def create(self,mybigkey,myparent=None,addme=1,myuse=None):
		"""creates the actual digraph of packages to merge.  return 1 on success, 0 on failure
		mybigkey = specification of package to merge; myparent = parent package (one depending on me);
		addme = should I be added to the tree? (for the --onlydeps mode)"""
		#stuff to add:
		#SLOT-aware emerge
		#IUSE-aware emerge
		#"no downgrade" emerge
		#print "mybigkey:",mybigkey

		jbigkey=string.join(mybigkey)
		if self.digraph.hasnode(jbigkey+" merge") or self.digraph.hasnode(jbigkey+" nomerge"):
			#this conditional is needed to prevent infinite recursion on already-processed deps
			return 1

		update_spinner()

		mytype,myroot,mykey=mybigkey
		# select the correct /var database that we'll be checking against
		vardbapi=portage.db[myroot]["vartree"].dbapi

		# if the package is already on the system, we add a "nomerge"
		# directive, otherwise we add a "merge" directive.
		if mytype=="blocks":
			# we've encountered a "blocks" node.  We will totally ignore this
			# node and not add it to our digraph if it doesn't apply to us.
			if addme and "--buildpkgonly" not in myopts and myparent and (self.mydbapi[myroot].match(mykey) or vardbapi.match(mykey)):
				mybigkey.append(myparent.split()[2])
				self.digraph.addnode(string.join(mybigkey),myparent)
			return 1

		if myuse == None:
			self.pkgsettings.setcpv(mykey)
			myuse = self.pkgsettings["USE"].split()
		self.applied_useflags[mykey] = myuse

		merging=1
		if addme:
		# this is where we add the node to the list of packages to merge
			if not myparent:
				# command-line specified or part of a world list...
				if ("self" not in myparams) or (("selective" in myparams) and vardbapi.cpv_exists(mykey)):
					# the package is on the system, so don't merge it.
					merging=0
			elif ("selective" in myparams) and vardbapi.cpv_exists(mykey):
				merging=0

			if (merging==0 and "--newuse" in myopts and vardbapi.cpv_exists(mykey)):
				old_use = vardbapi.aux_get(mykey, ["USE"])[0].split()
				if mytype == "binary":
					iuses = portage.db["/"]["bintree"].dbapi.aux_get(mykey, ["IUSE"])[0].split()
				else:
					iuses = portage.db["/"]["porttree"].dbapi.aux_get(mykey, ["IUSE"])[0].split()
				for x in iuses:
					if (old_use.count(x) and not myuse.count(x)) or (not old_use.count(x) and myuse.count(x)):
						merging=1
						break
		else:
			#onlydeps mode; don't merge
			merging=2
		if merging==1:
			mybigkey.append("merge")
		else:
			mybigkey.append("nomerge")

		# whatever the case, we need to add the node to our digraph so
		# that children can depend upon it.
		self.digraph.addnode(string.join(mybigkey),myparent)
		if ("deep" not in myparams) and (not merging):
			return 1
		elif "recurse" not in myparams:
			return 1

		edepend={}
		if mytype=="binary":
			mypkgparts=portage.catpkgsplit(mykey)
			tbz2name = string.split(mykey, "/")[1]+".tbz2"
			if tbz2name in portage.db[portage.root]["bintree"].invalids:
				sys.stderr.write("\nINVALID PACKAGE (is required to continue): "+str(mykey)+"\n")
				sys.exit(1)
			if portage.db[portage.root]["bintree"].isremote(mykey):
				edepend = portage.db[portage.root]["bintree"].remotepkgs[tbz2name]
				edepend["DEPEND"] =""
				edepend["RDEPEND"]=string.join(string.split(edepend["RDEPEND"])," ")
				edepend["PDEPEND"]=string.join(string.split(edepend["PDEPEND"])," ")
				edepend["SLOT"]   =string.strip(edepend["SLOT"])
				#portage.db[portage.root]["bintree"].gettbz2(mykey)
			else: # It's local.
				mytbz2=xpak.tbz2(portage.db[portage.root]["bintree"].getname(mykey))
				edepend["DEPEND"] =""
				edepend["RDEPEND"]=string.join(mytbz2.getelements("RDEPEND")," ")
				edepend["PDEPEND"]=string.join(mytbz2.getelements("PDEPEND")," ")
				edepend["SLOT"]   =mytbz2.getfile("SLOT",mypkgparts[2])
		elif mytype=="ebuild":
			try:
				mymeta = ["DEPEND","RDEPEND","PDEPEND"]
				myfoo = portage.portdb.aux_get(mykey, mymeta)
				for index in range(0,len(mymeta)):
					edepend[mymeta[index]] = myfoo[index]
				if "--buildpkgonly" in myopts:
					edepend["RDEPEND"] = ""
					edepend["PDEPEND"] = ""
			except (KeyError,IOError):
				print "emerge: create(): aux_get() error on",mykey+"; aborting..."
				sys.exit(1)
		mydep={}
		mp=string.join(mybigkey)

		if myroot=="/":
			mydep["/"]=edepend["DEPEND"]+" "+edepend["RDEPEND"]
			if not self.select_dep("/",mydep["/"],myparent=mp,myuse=myuse):
				return 0
		else:
			mydep["/"]=edepend["DEPEND"]
			mydep[myroot]=edepend["RDEPEND"]
			if not self.select_dep("/",mydep["/"],myparent=mp,myuse=myuse):
				return 0
			if not self.select_dep(myroot,mydep[myroot],myparent=mp,myuse=myuse):
				return 0

		if edepend.has_key("PDEPEND") and edepend["PDEPEND"]:
			# Post Depend -- Add to the list without a parent, as it depends
			# on a package being present AND must be built after that package.
			if not self.select_dep(myroot,edepend["PDEPEND"],myuse=myuse):
				return 0

		return 1

	def select_files(self,myfiles):
		"given a list of .tbz2s, .ebuilds and deps, create the appropriate depgraph and return a favorite list"
		myfavorites=[]
		for x in myfiles:
			ext = os.path.splitext(x)[1]
			if ext==".tbz2":
				if not os.path.exists(x):
					if os.path.exists(self.pkgsettings["PKGDIR"]+"/All/"+x):
						x=self.pkgsettings["PKGDIR"]+"/All/"+x
					elif os.path.exists(self.pkgsettings["PKGDIR"]+"/"+x):
						x=self.pkgsettings["PKGDIR"]+"/"+x
					else:
						print "\n\n!!! Binary package '"+str(x)+"' does not exist."
						print "!!! Please ensure the tbz2 exists as specified.\n"
						sys.exit(1)
				mytbz2=xpak.tbz2(x)
				mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.splitext(os.path.basename(x))[0]
				if os.path.realpath(portage.db["/"]["bintree"].getname(mykey)) != os.path.realpath(x):
					print red("\n*** You need to adjust PKGDIR to emerge this package.\n")
					sys.exit(1)
				if not self.create(["binary",portage.root,mykey],None,"--onlydeps" not in myopts):
					return (0,myfavorites)
				elif not "--oneshot" in myopts:
					myfavorites.append(mykey)
			elif ext==".ebuild":
				x = os.path.realpath(x)
				mykey=os.path.basename(os.path.normpath(x+"/../.."))+"/"+os.path.splitext(os.path.basename(x))[0]
				ebuild_path = portage.db["/"]["porttree"].dbapi.findname(mykey)
				if ebuild_path:
					if os.path.realpath(ebuild_path) != x:
						print red("\n*** You need to adjust PORTDIR or PORTDIR_OVERLAY to emerge this package.\n")
						sys.exit(1)
					if mykey not in portage.db["/"]["porttree"].dbapi.xmatch("match-visible", portage.dep_getkey(mykey)):
						print red("\n*** You are emerging a masked package. It is MUCH better to use")
						print red("*** /etc/portage/package.* to accomplish this. See portage(5) man")
						print red("*** page for details.")
						countdown(EMERGE_WARNING_DELAY, "Continuing...")
				else:
					print red("\n*** %s is not in a valid PORTDIR heirarchy or does not exist" % x)
					sys.exit(1)
				if not self.create(["ebuild",portage.root,mykey],None,"--onlydeps" not in myopts):
					return (0,myfavorites)
				elif not "--oneshot" in myopts:
					myfavorites.append(mykey)
			else:
				testkey = portage.dep_getkey(x)
				if testkey.startswith("null/"):
					testatom = x.replace(testkey[5:], "cat/"+testkey[5:])
				elif "/" not in x:
					testatom = "cat/"+x
				else:
					testatom = x
				if not portage.isvalidatom(testatom):
					print ("\n\n!!! '%s' is not a valid package atom." % x)
					print "!!! Please check ebuild(5) for full details."
					print "!!! (Did you specify a version but forget to prefix with '='?)"
					return (0,[])
				try:
					mykey=portage.dep_expand(x,mydb=portage.portdb)
				except ValueError, errpkgs:
					print "\n\n!!! The short ebuild name \"" + x + "\" is ambiguous.  Please specify"
					print "!!! one of the following fully-qualified ebuild names instead:\n"
					for i in errpkgs[0]:
						print "    " + green(i)
					print
					sys.exit(1)

				# select needs to return 0 on dep_check failure

				sys.stdout.flush()
				sys.stderr.flush()

				try:
					self.mysd = self.select_dep(portage.root,mykey,arg=x)
				except portage_exception.MissingSignature, e:
					portage.writemsg("\n\n!!! A missing gpg signature is preventing portage from calculating the\n")
					portage.writemsg("!!! required dependencies. This is a security feature enabled by the admin\n")
					portage.writemsg("!!! to aid in the detection of malicious intent.\n\n")
					portage.writemsg("!!! THIS IS A POSSIBLE INDICATION OF A TAMPERED FILES -- CHECK CAREFULLY.\n")
					portage.writemsg("!!! Affected file: %s\n" % (e))
					sys.exit(1)
				except portage_exception.InvalidSignature, e:
					portage.writemsg("\n\n!!! An invalid gpg signature is preventing portage from calculating the\n")
					portage.writemsg("!!! required dependencies. This is a security feature enabled by the admin\n")
					portage.writemsg("!!! to aid in the detection of malicious intent.\n\n")
					portage.writemsg("!!! THIS IS A POSSIBLE INDICATION OF A TAMPERED FILES -- CHECK CAREFULLY.\n")
					portage.writemsg("!!! Affected file: %s\n" % (e))
					sys.exit(1)
				except SystemExit, e:
					raise # Needed else can't exit
				except Exception, e:
					if "--debug" in myopts:
						raise
					print "\n\n!!! Problem in",mykey,"dependencies."
					print "!!!",str(e),e.__module__
					sys.exit(1)

				if not self.mysd:
					return (0,myfavorites)
				elif not "--oneshot" in myopts:
					myfavorites.append(portage.dep_getkey(mykey))

		missing=0
		if "--usepkgonly" in myopts:
			for x in self.digraph.dict.keys():
				xs=string.split(x," ")
				if (xs[0] != "binary") and (xs[3]=="merge"):
					if missing == 0:
						print
					missing += 1
					print "Missing binary for:",xs[2]

		# We're true here unless we are missing binaries.
		return (not missing,myfavorites)

	def is_newer_ver_installed(self,myroot,pkg,pkgver):
		"if there is a version of pkg installed newer than pkgver, return it"
		vardbapi=portage.db[myroot]["vartree"].dbapi

		matches=portage.db[myroot]["vartree"].dbapi.match(pkg)
		if matches:
			myslot=portage.db["/"]["porttree"].getslot(pkgver)
			for match in matches:
				if portage.pkgcmp(portage.catpkgsplit(pkgver)[1:], portage.catpkgsplit(match)[1:]) < 0:
					curslot=portage.db[myroot]["vartree"].getslot(match)
					if curslot == myslot:
						return match

	def select_dep(self,myroot,depstring,myparent=None,arg=None,myuse=None,raise_on_missing=False):
		"given a dependency string, create the appropriate depgraph and return 1 on success and 0 on failure"
		if "--debug" in myopts:
			print
			print "Parent:   ",myparent
			print "Depstring:",depstring
		if not arg:
			#processing dependencies
			mycheck=portage.dep_check(depstring,self.mydbapi[myroot],self.pkgsettings,myuse=myuse,use_binaries=("--usepkgonly" in myopts),myroot=myroot)

			if not mycheck[0]:
				mymerge=[]
			else:
				mymerge=mycheck[1]

		else:
			#we're processing a command-line argument; unconditionally merge it even if it's already merged
			mymerge=[depstring]

		# dep_check has been run so we can now add our parent to our
		# build state to update virtuals and other settings. This
		# happens after the package is added to the tree so that a
		# package can depend on a virtual which it satisfies.
		if myparent:
			myp = myparent.split()
			if myp[3]=="merge":
				self.mydbapi[myroot].cpv_inject(myp[2])
				if myp[0]=="binary":
					self.pkgsettings.setinst(myp[2],portage.db["/"]["bintree"].dbapi)
				else:
					self.pkgsettings.setinst(myp[2],portage.db[myroot]["porttree"].dbapi)

		if not mymerge:
			return 1

		if "--debug" in myopts:
			print "Candidates:",mymerge
		for x in mymerge:
			myk=None
			binpkguseflags=None
			if x[0]=="!":
				# if this package is myself, don't append it to block list.
				if "--debug" in myopts:
					print "Myparent",myparent
				if (myparent):
					if myparent.split()[2] in portage.portdb.xmatch("match-all", x[1:]):
						# myself, so exit.
						continue
				# adding block
				myk=["blocks",myroot,x[1:]]
			else:
				#We are not processing a blocker but a normal dependency
				myeb=None
				myeb_matches = portage.portdb.xmatch("match-visible",x)
				if ("--usepkgonly" not in myopts):
					myeb=portage.best(myeb_matches)

				myeb_pkg=None
				if ("--usepkg" in myopts):
					# The next line assumes the binarytree has been populated.
					# XXX: Need to work out how we use the binary tree with roots.
					myeb_pkg_matches=portage.db["/"]["bintree"].dbapi.match(x)
					if ("--usepkgonly" not in myopts):
						# Remove any binary package entries that are masked in the portage tree (#55871)
						for idx in range(len(myeb_pkg_matches)-1,-1,-1):
							if myeb_pkg_matches[idx] not in myeb_matches:
								del myeb_pkg_matches[idx]
					myeb_pkg = portage.best(myeb_pkg_matches)

				if not myeb_pkg:
					myeb_pkg = None
				elif ("--newuse" in myopts):
					iuses=string.split(portage.db["/"]["bintree"].dbapi.aux_get(myeb_pkg, ["IUSE"])[0])
					old_use=string.split(portage.db["/"]["bintree"].dbapi.aux_get(myeb_pkg, ["USE"])[0])
					self.pkgsettings.setcpv(myeb_pkg)
					now_use=string.split(self.pkgsettings["USE"])
					for x in iuses:
						if (old_use.count(x) and not now_use.count(x)) or (not old_use.count(x) and now_use.count(x)):
							myeb_pkg = None
							break

				if (not myeb) and (not myeb_pkg):
					if raise_on_missing:
						raise ValueError
					if not arg:
						xinfo='"'+x+'"'
					else:
						xinfo='"'+arg+'"'
					if myparent:
						xfrom = '(dependency required by '+green('"'+myparent.split()[2]+'"')+red(' ['+myparent.split()[0]+"])")
					alleb=portage.portdb.xmatch("match-all",x)
					if alleb:
						if "--usepkgonly" not in myopts:
							pass
							#print "\n!!! "+red("All ebuilds that could satisfy ")+green(xinfo)+red(" have been masked.")
							#print "!!! One of the following masked packages is required to complete your request:"
							#oldcomment = ""
							#for p in alleb:
							#	mreasons = portage.getmaskingstatus(p)
							#	print "- "+p+" (masked by: "+string.join(mreasons, ", ")+")"
							#	comment = portage.getmaskingreason(p)
							#	if comment and comment != oldcomment:
							#		print comment
							#		oldcomment = comment
							#print
							#print "For more information, see MASKED PACKAGES section in the emerge man page or "
							#print "refer to the Gentoo Handbook."
						else:
							pass
							#print "\n!!! "+red("There are no packages available to satisfy: ")+green(xinfo)
							#print "!!! Either add a suitable binary package or compile from an ebuild."
					else:
						print "\nemerge: there are no ebuilds to satisfy "+green(xinfo)+"."
					if myparent:
						print xfrom
					print
					return 0

				if "--debug" in myopts:
					print "ebuild:",myeb
					print "binpkg:",myeb_pkg

				if myeb and myeb_pkg:
					myeb_s     = portage.catpkgsplit(myeb)
					myeb_s     = [myeb_s[0]+"/"+myeb_s[1], myeb_s[2], myeb_s[3]]
					myeb_pkg_s = portage.catpkgsplit(myeb_pkg)
					myeb_pkg_s = [myeb_pkg_s[0]+"/"+myeb_pkg_s[1], myeb_pkg_s[2], myeb_pkg_s[3]]

					if portage.pkgcmp(myeb_s, myeb_pkg_s) == 0: # pkg is same version as ebuild
						myeb = None
					else:
						myeb_pkg = None

				if "--upgradeonly" in myopts:
					# Check that there isn't a newer version of this package already installed
					cand = None
					try:
						# XXX: This can throw an exception if the ebuild doesn't exist
						if myeb:
							cand=self.is_newer_ver_installed(myroot,x,myeb)
						elif myeb_pkg:
							cand=self.is_newer_ver_installed(myroot,x,myeb_pkg)
					except SystemExit, e:
						raise # Needed else can't exit
					except Exception, e:
						print "Warning: "+str(e)
					if cand:
						# --newuse can't work because the installed version isn't available
						# just pretend like the package doesn't matter
						# XXX: hidden assumption here that the higher versioned package
						# satisfies the atom that was given
						continue

				if myeb:
					myk=["ebuild",myroot,myeb]
				elif myeb_pkg:
					binpkguseflags=portage.db[portage.root]["bintree"].get_use(myeb_pkg)
					myk=["binary",myroot,myeb_pkg]
				else:
					sys.stderr.write("!!! Confused... Don't know what I'm using for dependency info. :(\n")
					sys.exit(1)

				#if "--usepkg" in myopts:
				#	#If we want to use packages, see if we have a pre-built one...
				#	mypk=portage.db["/"]["bintree"].dbapi.match(x)
				#	if myeb in mypk:
				#		#Use it only if it's exactly the version we want.
				#		myk=["binary",myroot,myeb]
				#	else:
				#		myk=["ebuild",myroot,myeb]
				#else:
				#	myk=["ebuild",myroot,myeb]
			if myparent:
				#we are a dependency, so we want to be unconditionally added
				if not self.create(myk,myparent,myuse=binpkguseflags):
					return 0
			else:
				#if mysource is not set, then we are a command-line dependency and should not be added
				#if --onlydeps is specified.
				if not self.create(myk,myparent,"--onlydeps" not in myopts,myuse=binpkguseflags):
					return 0

		if "--debug" in myopts:
			print "Exiting...",myparent
		return 1


	def altlist(self):
		mygraph=self.digraph.copy()
		dolist=["/"]
		retlist=[]
		for x in portage.db.keys():
			portage.db[x]["merge"]=[]
			if x not in dolist:
				dolist.append(x)
		while (not mygraph.empty()):
			mycurkey=mygraph.firstzero()
			if not mycurkey:
				print "!!! Error: circular dependencies:"
				print
				for x in mygraph.dict.keys():
					for y in mygraph.dict[x][1]:
						print y,"depends on",x
				print
				sys.exit(1)
			splitski=string.split(mycurkey)
			#I'm not sure of the significance of the following lines (vestigal?) so I'm commenting 'em out.
			#These lines remove already-merged things from our alt-list
			#if "--update" in myopts:
			#	if not portage.db["/"]["vartree"].exists_specific(splitski[2]):
			#		portage.db["/"]["merge"].append(splitski)
			#else:
			portage.db[splitski[1]]["merge"].append(splitski)
			mygraph.delnode(mycurkey)
		for x in dolist:
			for y in portage.db[x]["merge"]:
				retlist.append(y)
		return retlist

	def xcreate(self,mode="system"):
		global syslist
		world_problems = False
		if mode=="system":
			mylist=syslist
		else:
			#world mode
			worldlist=getlist("world")
			sysdict=genericdict(syslist)
			worlddict=genericdict(worldlist)

			for x in worlddict.keys():
				if not portage.isvalidatom(x):
					world_problems = True
				elif not portage.db["/"]["vartree"].dbapi.match(x):
					world_problems = True
				else:
					sysdict[x]=worlddict[x]

			mylist = sysdict.keys()

		newlist = []
		for atom in mylist:
			if portage.dep_getkey(atom).split("/")[-1] == "portage":
				newlist.insert(0, atom)
			else:
				newlist.append(atom)
		mylist = newlist
		
		missing_atoms = []
		for mydep in mylist:
			try:
				if not self.select_dep(portage.root, mydep, raise_on_missing=True):
					print "\n\n!!! Problem resolving dependencies for", mydep
					return 0
			except ValueError:
				if "--debug" in myopts:
					raise
				missing_atoms.append(mydep)

		if world_problems:
			print "\n!!! Problems have been detected with your world file"
			print "!!! Please run "+green("emaint --check world")+"\n"

		if missing_atoms and "--verbose" in myopts:
			print "\n!!! Packages for the following atoms are either all"
			print "!!! masked or don't exist:"
			print " ".join(missing_atoms) + "\n"

		return 1

	def match(self,mydep,myroot=portage.root,mykey=None):
		# support mutual exclusive deps
		mydep2=mydep
		if mydep2[0]=="!":
			mydep2=mydep[1:]

		if mydep[0]=="!":
			#add our blocker; it will be ignored later if necessary (if we are remerging the same pkg, for example)
			myk="blocks "+myroot+" "+mydep2
		else:
			myeb=portage.db[portage.root]["porttree"].dep_bestmatch(mydep2)
			if not myeb:
				if not mykey:
					print "\n!!! Error: couldn't find match for",mydep
				else:
					print "\n!!! Error: couldn't find match for",mydep,"in",mykey
				print
				sys.exit(1)

			if "--usepkg" in myopts:
				mypk=portage.db[portage.root]["bintree"].dep_bestmatch(mydep)
				if myeb==mypk:
					myk="binary "+portage.root+" "+mypk
				else:
					myk="ebuild "+myroot+" "+myeb
			else:
				myk="ebuild "+myroot+" "+myeb

		return myk

	def display(self,mylist):
		changelogs=[]
		p=[]
		totalsize=0

		if "--quiet" in myopts:
			def create_use_string(*args):
				return ""
		else:
			def create_use_string(name, cur_iuse, cur_use, old_iuse, old_use, is_new, all_flags=("--verbose" in myopts)):
				enabled = []
				disabled = []
				for flag in cur_iuse:
					if flag in cur_use:
						if is_new or flag in old_use and all_flags:
							enabled.append(red(flag))
						elif flag not in old_iuse:
							enabled.append(yellow(flag)+"%")
						elif flag not in old_use:
							enabled.append(green(flag)+"*")
					else:
						if is_new or flag in old_iuse and flag not in old_use and all_flags:
							disabled.append(blue("-"+flag))
						elif flag not in old_iuse:
							disabled.append(yellow("-"+flag)+"%")
						elif flag in old_use:
							disabled.append(green("-"+flag)+"*")

				enabled = " ".join(enabled)
				disabled = " ".join(disabled)
				if enabled and disabled:
					ret = enabled + " " + disabled
				elif enabled:
					ret = enabled
				else:
					ret = disabled
				if ret:
					ret = '%s="%s" ' % (name, ret)
				return ret

		if "--verbose" in myopts:
			overlays = string.split(portage.settings['PORTDIR_OVERLAY'])

		if "--tree" in myopts:
			mylist.reverse()
			mygraph=self.digraph.copy()

		i = 0
		while i < len(mylist):
			if mylist[i][-1]=="nomerge":
				if not ("--tree" in myopts):
					# we don't care about this elements
					mylist.pop(i)
					continue
				if (i == (len(mylist) - 1)) \
				   or (mygraph.depth(string.join(mylist[i])) \
				       >= mygraph.depth(string.join(mylist[i+1]))):
					# end of a useless branch (may be the last one)
					# -> delete the element and test the previous one
					mylist.pop(i)
					if i > 0:
						i -= 1
					continue
			# the branch continues, or we've found a good element.
			# -> let's see what's next, if anything
			i += 1

		display_overlays=False
		# files to fetch list - avoids counting a same file twice
		# in size display (verbose mode)
		myfetchlist=[]
		for x in mylist:
			fetch=" "

			if x[0]=="blocks":
				addl=""+red("B")+"  "+fetch+"  "
				resolved=portage.db[x[1]]["vartree"].resolve_key(x[2])
				print "["+x[0]+" "+addl+"]",red(resolved),
				if resolved!=x[2]:
					if x[3]:
						print red("(\""+x[2]+"\" is blocking "+x[3]+")")
					else:
						print red("(\""+x[2]+"\")")
				else:
					if x[3]:
						print red("(is blocking "+x[3]+")")
					else:
						print
			else:
				if (x[0]!="binary") and ("fetch" in string.split(portage.portdb.aux_get(x[2],["RESTRICT"])[0])):
					fetch = red("F")
					if portage.portdb.fetch_check(x[2], self.applied_useflags[x[2]]):
						fetch = green("f")

				#we need to use "--emptrytree" testing here rather than "empty" param testing because "empty"
				#param is used for -u, where you still *do* want to see when something is being upgraded.
				myoldbest=""
				if (not "--emptytree" in myopts) and portage.db[x[1]]["vartree"].exists_specific(x[2]):
					addl="  "+yellow("R")+fetch+"  "
				elif (not "--emptytree" in myopts) and portage.db[x[1]]["vartree"].exists_specific_cat(x[2]):
					if x[0] == "binary":
						mynewslot=portage.db["/"]["bintree"].getslot(x[2])
					elif x[0] == "ebuild":
						mynewslot=portage.db["/"]["porttree"].getslot(x[2])
					myoldlist=portage.db[x[1]]["vartree"].dbapi.match(portage.pkgsplit(x[2])[0])
					myinslotlist=filter((lambda p: portage.db[portage.root]["vartree"].getslot(p)==mynewslot),myoldlist)
					if myinslotlist:
						myoldbest=portage.best(myinslotlist)
						addl="   "+fetch
						if portage.pkgcmp(portage.pkgsplit(x[2]), portage.pkgsplit(myoldbest)) < 0:
							# Downgrade in slot
							addl+=turquoise("U")+blue("D")
						else:
							# Update in slot
							addl+=turquoise("U")+" "
					else:
						# New slot, mark it new.
						addl=" "+green("NS")+fetch+"  "

					if "--changelog" in myopts:
						changelogs.extend(self.calc_changelog(
							portage.portdb.findname(x[2]),
							portage.db["/"]["vartree"].dep_bestmatch('/'.join(portage.catpkgsplit(x[2])[:2])),
							x[2]
							))
				else:
					addl=" "+green("N")+" "+fetch+"  "

				verboseadd=""
				
				if x[2] in self.applied_useflags:
					# USE flag display
					if x[0] == "binary":
						cur_iuse = string.split(portage.db["/"]["bintree"].dbapi.aux_get(x[2],["IUSE"])[0])
					elif x[0] == "ebuild":
						cur_iuse = string.split(portage.portdb.aux_get(x[2],["IUSE"])[0])
					else:
						cur_iuse = []

					cur_iuse = portage.unique_array(cur_iuse)
					cur_iuse = [flag for flag in cur_iuse if flag not in portage.settings.usemask]
					cur_iuse.sort()
					cur_use = self.applied_useflags[x[2]]
					cur_use = [flag for flag in cur_use if flag in cur_iuse]

					if myoldbest:
						pkg = myoldbest
					else:
						pkg = x[2]
					if portage.db["/"]["vartree"].dbapi.cpv_exists(pkg):
						(old_iuse, old_use) = portage.db["/"]["vartree"].dbapi.aux_get(pkg, ["IUSE", "USE"])
						old_iuse = portage.unique_array(old_iuse.split())
						old_iuse.sort()
						old_use = old_use.split()
						is_new = False
					else:
						old_iuse = []
						old_use = []
						is_new = True
					old_iuse = [flag for flag in old_iuse if flag not in portage.settings.usemask]
					old_use = [flag for flag in old_use if flag in old_iuse]

					use_expand = portage.settings["USE_EXPAND"].lower().split()
					use_expand.sort()
					use_expand.reverse()
					use_expand_hidden = portage.settings["USE_EXPAND_HIDDEN"].lower().split()

					def map_to_use_expand(myvals):
						ret = {}
						for exp in use_expand:
							ret[exp] = []
							for val in myvals[:]:
								if val.startswith(exp.lower()+"_"):
									ret[exp].append(val[len(exp)+1:])
									myvals.remove(val)
						ret["USE"] = myvals
						for exp in use_expand_hidden:
							if exp in ret:
								del ret[exp]
						return ret

					cur_iuse_map = map_to_use_expand(cur_iuse)
					cur_use_map = map_to_use_expand(cur_use)
					old_iuse_map = map_to_use_expand(old_iuse)
					old_use_map = map_to_use_expand(old_use)

					use_expand.sort()
					use_expand.insert(0, "USE")
					
					for key in use_expand:
						verboseadd += create_use_string(key.upper(), cur_iuse_map[key], cur_use_map[key],
						                                old_iuse_map[key], old_use_map[key], is_new)

				if "--verbose" in myopts:
					# size verbose
					mysize=0
					if x[0] == "ebuild" and x[-1]!="nomerge":
						myfilesdict=portage.portdb.getfetchsizes(x[2], useflags=self.applied_useflags[x[2]], debug=edebug)
						if myfilesdict==None:
							myfilesdict="[empty/missing/bad digest]"
						else:
							for myfetchfile in myfilesdict.keys():
								if myfetchfile not in myfetchlist:
									mysize+=myfilesdict[myfetchfile]
									myfetchlist.append(myfetchfile)
							totalsize+=mysize
						verboseadd+=format_size(mysize)+" "

					# overlay verbose
					# XXX: Invalid binaries have caused tracebacks here. 'if file_name'
					# x = ['binary', '/', 'sys-apps/pcmcia-cs-3.2.7.2.6', 'merge']
					file_name=portage.portdb.findname(x[2])
					if file_name: # It might not exist in the tree
						dir_name=os.path.abspath(os.path.dirname(file_name)+"/../..")
						if (overlays.count(dir_name)>0):
							verboseadd+=teal("["+str(overlays.index(os.path.normpath(dir_name))+1)+"]")+" "
							display_overlays=True
					else:
						verboseadd += "[No ebuild?]"

				xs=portage.pkgsplit(x[2])
				if xs[2]=="r0":
					xs[2]=""
				else:
					xs[2]="-"+xs[2]

				if self.pkgsettings.has_key("COLUMNWIDTH"):
					mywidth=int(self.pkgsettings.settings["COLUMNWIDTH"])
				else:
					mywidth=130
				oldlp=mywidth-30
				newlp=oldlp-30

				indent=""
				if ("--tree" in myopts):
					indent=" "*mygraph.depth(string.join(x))

				if myoldbest:
					myoldbest=portage.pkgsplit(myoldbest)[1]+"-"+portage.pkgsplit(myoldbest)[2]
					if myoldbest[-3:]=="-r0":
						myoldbest=myoldbest[:-3]
					myoldbest=blue("["+myoldbest+"]")

				if x[1]!="/":
					if "--columns" in myopts:
						myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(xs[0])
						if (newlp-nc_len(myprint)) > 0:
							myprint=myprint+(" "*(newlp-nc_len(myprint)))
						myprint=myprint+"["+darkblue(xs[1]+xs[2])+"] "
						if (oldlp-nc_len(myprint)) > 0:
							myprint=myprint+" "*(oldlp-nc_len(myprint))
						myprint=myprint+myoldbest
						myprint=myprint+darkgreen("  to "+x[1])+" "+verboseadd
					else:
						myprint="["+x[0]+" "+addl+"] "+darkgreen(x[2])+" "+myoldbest+" "+darkgreen("to "+x[1])+" "+verboseadd
				else:
					if "--columns" in myopts:
						myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(xs[0])
						if (newlp-nc_len(myprint)) > 0:
							myprint=myprint+(" "*(newlp-nc_len(myprint)))
						myprint=myprint+green(" ["+xs[1]+xs[2]+"] ")
						if (oldlp-nc_len(myprint)) > 0:
							myprint=myprint+(" "*(oldlp-nc_len(myprint)))
						myprint=myprint+myoldbest+"  "+verboseadd
					else:
						if x[3]=="nomerge":
							myprint=darkblue("[nomerge      ] "+indent+x[2]+" "+myoldbest+" ")+verboseadd
						else:
							myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(x[2])+" "+myoldbest+" "+verboseadd
				p.append(myprint)

			if ("--tree" not in myopts):
				mysplit=portage.pkgsplit(x[2])

				# XXX mysplit _can_ be None.... Why?
				if mysplit and (len(mysplit)==3):
					if "--emptytree" not in myopts:
						if mysplit[0]=="sys-apps/portage":
							if ((mysplit[1]+mysplit[2]) != portage.VERSION) and \
							   ("livecvsportage" not in portage.settings.features):
								if mylist.index(x)<len(mylist)-1:
									p.append(red("*** Portage will stop merging at this point and reload itself,"))
									p.append(red("    recalculate dependencies, and complete the merge."))
									if "--update" not in myopts:
										p.append(darkgreen("    You may avoid the remerging of packages by updating portage on its own."))
									print
					else:
						if mysplit[0]=="sys-apps/portage" and ("--emptytree" in myopts):
							if mysplit[1]+mysplit[2]!=portage.VERSION:
								p.append(red("***")+" Please update portage to the above version before proceeding.")
								p.append("    Failure to do so may result in failed or improper merges.")
								p.append("    A simple '"+green("emerge -u portage")+"' is sufficient.")
								p.append("")
				del mysplit

		for x in p:
			print x

		if "--verbose" in myopts:
			print
			print "Total size of downloads: "+format_size(totalsize)
			if overlays and display_overlays:
				print "Portage overlays:"
				y=0
				for x in overlays:
					y=y+1
					print " "+teal("["+str(y)+"]"),x

		if "--changelog" in myopts:
			print
			for revision,text in changelogs:
				print bold('*'+revision)
				sys.stdout.write(text)

	def calc_changelog(self,ebuildpath,current,next):
		current = '-'.join(portage.catpkgsplit(current)[1:])
		if current.endswith('-r0'): current = current[:-3]
		next = '-'.join(portage.catpkgsplit(next)[1:])
		if next.endswith('-r0'): next = next[:-3]
		changelogpath = os.path.join(os.path.split(ebuildpath)[0],'ChangeLog')
		try:
			changelog = open(changelogpath).read()
		except SystemExit, e:
			raise # Needed else can't exit
		except:
			return []
		divisions = self.find_changelog_tags(changelog)
		#print 'XX from',current,'to',next
		#for div,text in divisions: print 'XX',div
		# skip entries for all revisions above the one we are about to emerge
		for i in range(len(divisions)):
			if divisions[i][0]==next:
				divisions = divisions[i:]
				break
		# find out how many entries we are going to display
		for i in range(len(divisions)):
			if divisions[i][0]==current:
				divisions = divisions[:i]
				break
		else:
		    # couldnt find the current revision in the list. display nothing
			return []
		return divisions

	def find_changelog_tags(self,changelog):
		divs = []
		release = None
		while 1:
			match = re.search(r'^\*\ ?([-a-zA-Z0-9_.]*)(?:\ .*)?\n',changelog,re.M)
			if match is None:
				if release is not None:
					divs.append((release,changelog))
				return divs
			if release is not None:
				divs.append((release,changelog[:match.start()]))
			changelog = changelog[match.end():]
			release = match.group(1)
			if release.endswith('.ebuild'):
				release = release[:-7]
			if release.endswith('-r0'):
				release = release[:-3]

	def outdated(self):
		return self.outdatedpackages

	def merge(self,mylist):
		returnme=0
		mymergelist=[]

		#check for blocking dependencies
		if ("--fetchonly" not in myopts) and ("--buildpkgonly" not in myopts):
			for x in mylist:
				if x[0]=="blocks":
					print "\n!!! Error: the "+x[2]+" package conflicts with another package."
					print   "!!!        both can't be installed on the same system together."
					print   "!!!        Please use 'emerge --pretend' to determine blockers."
					print
					if ("--pretend" not in myopts):
						sys.exit(1)

		#buildsyspkg: I need mysysdict also on resume (moved from the else block)
		mysysdict=genericdict(syslist)
		if ("--resume" in myopts):
			# We're resuming.
			print green("*** Resuming merge...")
			emergelog(" *** Resuming merge...")
			mymergelist=portage.mtimedb["resume"]["mergelist"][:]
			if ("--skipfirst" in myopts) and mymergelist:
				del portage.mtimedb["resume"]["mergelist"][0]
				del mymergelist[0]
			for bigkey in mymergelist:
				(pkgtype, root, cpv, action) = bigkey
				if pkgtype == "binary" and not portage.db["/"]["bintree"].dbapi.match("="+cpv) or \
				   pkgtype == "ebuild" and not portage.db["/"]["porttree"].dbapi.xmatch("match-all", "="+cpv):
					print red("!!! Error: The resume list contains packages that no longer")
					print red("!!!        available to be emerged. Please restart/continue")
					print red("!!!        the merge operation manually.")
					sys.exit(1)
		else:
			myfavs=portage.grabfile(portage.root+portage.WORLD_FILE)
			myfavdict=genericdict(myfavs)
			for x in range(len(mylist)):
				if mylist[x][3]!="nomerge":
					# Add to the mergelist
					mymergelist.append(mylist[x])
				else:
					myfavkey=portage.cpv_getkey(mylist[x][2])
					if "--onlydeps" in myopts:
						continue
					# Add to the world file. Since we won't be able to later.
					if (not "--fetchonly" in myopts) and (myfavkey in favorites):
						#don't record if already in system profile or already recorded
						if (not mysysdict.has_key(myfavkey)) and (not myfavdict.has_key(myfavkey)):
							#we don't have a favorites entry for this package yet; add one
							myfavdict[myfavkey]=myfavkey
							print ">>> Recording",myfavkey,"in \"world\" favorites file..."
			if not "--fetchonly" in myopts:
				portage.writedict(myfavdict,portage.root+portage.WORLD_FILE,writekey=0)

			portage.mtimedb["resume"]["mergelist"]=mymergelist[:]

		# We need to yank the harmful-to-new-builds settings from features.
		myorigfeat=self.pkgsettings["FEATURES"]
		myfeat=myorigfeat.split()
		while ("keeptemp" in myfeat):
			del myfeat[myfeat.index("keeptemp")]
		while ("keepwork" in myfeat):
			del myfeat[myfeat.index("keepwork")]

		self.pkgsettings["FEATURES"]=string.join(myfeat)

		if "parallel-fetch" in myfeat and not ("--ask" in myopts or "--pretend" in myopts or "--fetchonly" in myopts):
			if "distlocks" not in myfeat:
				print red("!!!")
				print red("!!!")+" parallel-fetching requires the distlocks feature enabled"
				print red("!!!")+" you have it disabled, thus parallel-fetching is being disabled"
				print red("!!!")
			elif len(mymergelist) > 1:
				print ">>> starting parallel fetching"
				pid = os.fork()
				if not pid:
					sys.stdin.close()
					sys.stdout.close()
					sys.stderr.close()
					sys.stdout = open("/dev/null","w")
					sys.stderr = open("/dev/null","w")
					os.dup2(sys.stdout.fileno(), 1)
					os.dup2(sys.stdout.fileno(), 2)
					for x in ("autoaddcvs", "cvs"):
						try:	myfeat.remove(x)
						except ValueError: pass
					self.pkgsettings["FEATURES"] = " ".join(myfeat)
					ret = 0
					for x in mymergelist:
						if x[0] != "ebuild":
							continue
						try:
							ret = portage.doebuild(portage.portdb.findname(x[2]), "fetch", x[1], self.pkgsettings,
								cleanup=0, fetchonly=True, tree="porttree")
						except SystemExit:
							raise
						except Exception:
							ret = 1
					sys.exit(0)
				portage.portage_exec.spawned_pids.append(pid)

		mergecount=0
		for x in mymergelist:
			mergecount+=1
			myroot=x[1]
			pkgindex=2
			if x[0]=="blocks":
				pkgindex=3
			y=portage.portdb.findname(x[pkgindex])
			if not "--pretend" in myopts:
				print ">>> emerge ("+str(mergecount)+" of "+str(len(mymergelist))+")",x[pkgindex],"to",x[1]
				emergelog(" >>> emerge ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" to "+x[1])

			self.pkgsettings["EMERGE_FROM"] = x[0][:]
			self.pkgsettings.backup_changes("EMERGE_FROM")
			self.pkgsettings.reset()

			#buildsyspkg: Check if we need to _force_ binary package creation
			issyspkg = ("buildsyspkg" in myfeat) \
					and x[0] != "blocks" \
					and mysysdict.has_key(portage.cpv_getkey(x[2])) \
					and not ("--buildpkg" in myopts)
			if x[0] in ["ebuild","blocks"]:
				if (x[0]=="blocks") and ("--fetchonly" not in myopts):
					raise Exception, "Merging a blocker"
				elif ("--fetchonly" in myopts) or ("--fetch-all-uri" in myopts):
					if ("--fetch-all-uri" in myopts):
						retval=portage.doebuild(y,"fetch",myroot,self.pkgsettings,edebug,("--pretend" in myopts),fetchonly=1,fetchall=1,tree="porttree")
					else:
						retval=portage.doebuild(y,"fetch",myroot,self.pkgsettings,edebug,("--pretend" in myopts),fetchonly=1,tree="porttree")
					if (retval == None) or retval:
						print
						print "!!! Fetch for",y,"failed, continuing..."
						print
						returnme=1
					continue
				elif "--buildpkg" in myopts or issyspkg:
					#buildsyspkg: Sounds useful to display something, but I don't know if we should also log it
					if issyspkg:
						print ">>> This is a system package, let's pack a rescue tarball."
						#emergelog(">>> This is a system package, let's pack a rescue tarball.")
					#create pkg, then merge pkg
					short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Clean"
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Cleaning ("+x[pkgindex]+"::"+y+")", short_msg=short_msg)
					retval=portage.doebuild(y,"clean",myroot,self.pkgsettings,edebug,cleanup=1,tree="porttree")
					if (retval == None):
						portage_util.writemsg("Unable to run required binary.\n")
						sys.exit(127)
					if retval:
						sys.exit(retval)
					short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Compile"
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Compiling/Packaging ("+x[pkgindex]+"::"+y+")", short_msg=short_msg)
					retval=portage.doebuild(y,"package",myroot,self.pkgsettings,edebug,tree="porttree")
					if (retval == None):
						portage_util.writemsg("Unable to run required binary.\n")
						sys.exit(127)
					if retval:
						sys.exit(retval)
					#dynamically update our database
					if "--buildpkgonly" not in myopts:
						portage.db[portage.root]["bintree"].inject(x[2])
						mytbz2=portage.db[portage.root]["bintree"].getname(x[2])
						short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Merge"
						emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Merging ("+x[pkgindex]+"::"+y+")", short_msg=short_msg)

						self.pkgsettings["EMERGE_FROM"] = "binary"
						self.pkgsettings.backup_changes("EMERGE_FROM")

						retval=portage.pkgmerge(mytbz2,myroot,self.pkgsettings)
						if retval==None:
							sys.exit(1)
				else:
					short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Clean"
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Cleaning ("+x[pkgindex]+"::"+y+")", short_msg=short_msg)
					retval=portage.doebuild(y,"clean",myroot,self.pkgsettings,edebug,cleanup=1,tree="porttree")
					if (retval == None):
						portage_util.writemsg("Unable to run required binary.\n")
						sys.exit(127)
					if retval:
						sys.exit(retval)
					short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Compile"
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Compiling/Merging ("+x[pkgindex]+"::"+y+")", short_msg=short_msg)
					retval=portage.doebuild(y,"merge",myroot,self.pkgsettings,edebug,tree="porttree")
					if (retval == None):
						portage_util.writemsg("Unable to run required binary.\n")
						sys.exit(127)
					if retval:
						sys.exit(retval)
					#dynamically update our database
			elif x[0]=="binary":
				#merge the tbz2
				mytbz2=portage.db[portage.root]["bintree"].getname(x[2])
				if portage.db[portage.root]["bintree"].isremote(x[2]):
					short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Fetch"
					emergelog(" --- ("+str(mergecount)+" of "+str(len(mymergelist))+") Fetching Binary ("+x[pkgindex]+"::"+mytbz2+")", short_msg=short_msg)
					if not portage.db[portage.root]["bintree"].gettbz2(x[2]):
						sys.exit(1)

				if ("--fetchonly" in myopts) or ("--fetch-all-uri" in myopts):
					continue

				short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Merge Binary"
				emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Merging Binary ("+x[pkgindex]+"::"+mytbz2+")", short_msg=short_msg)
				retval=portage.pkgmerge(mytbz2,x[1],self.pkgsettings)
				if retval==None:
					sys.exit(1)
				#need to check for errors
			if "--buildpkgonly" not in myopts:
				portage.db[x[1]]["vartree"].inject(x[2])
				myfavkey=portage.cpv_getkey(x[2])
				if "--fetchonly" not in myopts and "--fetch-all-uri" not in myopts and myfavkey in favorites:
					myfavs=portage.grabfile(myroot+portage.WORLD_FILE)
					myfavdict=genericdict(myfavs)
					mysysdict=genericdict(syslist)
					#don't record if already in system profile or already recorded
					if (not mysysdict.has_key(myfavkey)) and (not myfavdict.has_key(myfavkey)):
						#we don't have a favorites entry for this package yet; add one
						myfavdict[myfavkey]=myfavkey
						print ">>> Recording",myfavkey,"in \"world\" favorites file..."
						emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Updating world file ("+x[pkgindex]+")")
						portage.writedict(myfavdict,myroot+portage.WORLD_FILE,writekey=0)

				if ("noclean" not in portage.features) and (x[0] != "binary"):
					short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Clean Post"
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Post-Build Cleaning ("+x[pkgindex]+"::"+y+")", short_msg=short_msg)
					retval=portage.doebuild(y,"clean",myroot,self.pkgsettings,edebug,cleanup=1,tree="porttree")
					if (retval == None):
						portage_util.writemsg("Unable to run required binary.\n")
						sys.exit(127)
					if retval:
						sys.exit(retval)

				if ("--pretend" not in myopts) and ("--fetchonly" not in myopts) and ("--fetch-all-uri" not in myopts):
					# Clean the old package that we have merged over top of it.
					if self.pkgsettings["AUTOCLEAN"]=="yes":
						xsplit=portage.pkgsplit(x[2])
						emergelog(" >>> AUTOCLEAN: "+xsplit[0])
						retval=unmerge("clean", [xsplit[0]])
						if not retval:
							emergelog(" --- AUTOCLEAN: Nothing unmerged.")

					# Figure out if we need a restart.
					mysplit=portage.pkgsplit(x[2])
					if mysplit[0]=="sys-apps/portage":
						myver=mysplit[1]+"-"+mysplit[2]
						if myver[-3:]=='-r0':
							myver=myver[:-3]
						if (myver != portage.VERSION) and \
						   ("livecvsportage" not in portage.settings.features):
							if len(mymergelist) > mergecount:
								myargv=sys.argv
								myr=0
								for myra in range(len(myargv)):
									if myargv[myr][0:len("portage")]=="portage":
										del myargv[myr]
										myr-=1
									if myargv[myr][0:len("sys-apps/portage")]=="sys-apps/portage":
										del myargv[myr]
										myr-=1
									myr+=1
								emergelog(" *** RESTARTING emerge via exec() after change of portage version.")
								portage.portageexit()
								# Remove --ask from options before restarting
								mynewargv=[]
								badlongopts = ["--ask","--tree","--changelog"]
								badshortopts = ["a","t","l"]
								for arg in myargv:
									if arg[0:2] == "--":
										if arg in badlongopts:
											continue
										mynewargv += [arg]
									elif arg[0] == "-":
										myarg = "-"
										for ch in arg[1:]:
											if ch in badshortopts:
												continue
											myarg += ch
										mynewargv += [myarg]
									else:
										mynewargv += [arg]
								os.execv("/usr/lib/portage/bin/emerge", mynewargv)

			if ("--pretend" not in myopts) and ("--fetchonly" not in myopts) and ("--fetch-all-uri" not in myopts):
				emergelog(" ::: completed emerge ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[2]+" to "+x[1])

			# Unsafe for parallel merges
			del portage.mtimedb["resume"]["mergelist"][0]

		emergelog(" *** Finished. Cleaning up...")

		# We're out of the loop... We're done. Delete the resume data.
		if portage.mtimedb.has_key("resume"):
			del portage.mtimedb["resume"]

		if ("--pretend" not in myopts):
			if ("--fetchonly" not in myopts) and ("--fetch-all-uri" not in myopts):
				if (mergecount>0):
					if retval:
						portage.env_update()

		#by doing an exit this way, --fetchonly can continue to try to
		#fetch everything even if a particular download fails.
		if "--fetchonly" in myopts or "--fetch-all-uri" in myopts:
			if returnme:
				print "\n\n!!! Some fetch errors were encountered.  Please see above for details.\n\n"
				sys.exit(returnme)
			else:
				sys.exit(0)

if __name__ == "__main__":
	main()
