#!/usr/bin/python
# Copyright 1999-2003 Gentoo Technologies, Inc.
# Distributed under the terms of the GNU General Public License v2
# $Header: /home/cvsroot/gentoo-src/portage/bin/emerge,v 1.228 2003/10/13 07:43:38 carpaski Exp $

import os,sys
os.environ["PORTAGE_CALLER"]="emerge"
sys.path += ["/usr/lib/portage/pym"]

import emergehelp,xpak,string,re,commands,time,shutil,traceback,atexit,signal
from stat import *
from output import *

import portage
if (not sys.stdout.isatty()) or (portage.settings["NOCOLOR"] in ["yes","true"]):
	nocolor()

if portage.settings.has_key("PORTAGE_NICENESS"):
	try:
		os.nice(int(portage.settings["PORTAGE_NICENESS"]))
	except Exception,e:
		print "!!! Failed to change nice value to '"+str(portage.settings["PORTAGE_NICENESS"])+"'"
		print "!!!",e

#Freeze the portdbapi for enhanced performance:
portage.portdb.freeze()

# Kill noauto as it will break merges otherwise.
while 'noauto' in portage.features:
	del portage.features[portage.features.index('noauto')]


spinner="\|/-\|/-"
spinpos=0
#number of ebuilds merged
merged=0
params=["selective", "deep", "self", "recurse", "empty"]
actions=[
"clean", "config", "depclean", "help", "info",   "inject",  "prune",
"regen", "rsync",  "search",   "sync", "system", "unmerge", "world"
]
options=[
"--buildpkg",     "--buildpkgonly",
"--changelog",    "--columns",
"--debug",        "--deep",
"--digest",
"--emptytree",    "--fetchonly",
"--getbinpkg",    "--getbinpkgonly",
"--help",         "--noconfmem",
"--nodeps",       "--noreplace",
"--nospinner",    "--oneshot",
"--onlydeps",     "--pretend",
"--quiet",        "--resume",
"--searchdesc",   "--selective",
"--skipfirst",
"--update",       "--upgradeonly",
"--usepkg",       "--usepkgonly",
"--verbose",      "--version"
]

shortmapping={
"b":"--buildpkg",  "B":"--buildpkgonly",
"c":"--clean",     "C":"--unmerge",
"d":"--debug",     "D":"--deep",
"e":"--emptytree",
"f":"--fetchonly",
"g":"--getbinpkg", "G":"--getbinpkgonly",
"h":"--help",
"i":"--inject",
"k":"--usepkg",    "K":"--usepkgonly",
"l":"--changelog",
"n":"--noreplace",
"o":"--onlydeps",  "O":"--nodeps",
"p":"--pretend",   "P":"--prune",
"q":"--quiet",
"s":"--search",    "S":"--searchdesc",
"u":"--update",    "U":"--upgradeonly",
"v":"--verbose",   "V":"--version"
}

myaction=None
myopts=[]
myfiles=[]
edebug=0

# process short actions and options
tmpcmdline=sys.argv[1:]
#tmpcmdline.extend(portage.settings["EMERGE_OPTS"].split())
cmdline=[]
for x in tmpcmdline:
	if x[0:1]=="-"and x[1:2]!="-":
		for y in x[1:]:
			if shortmapping.has_key(y):
				if shortmapping[y] in cmdline:
					print
					print "*** Warning: Redundant use of",shortmapping[y]
				else:
					cmdline.append(shortmapping[y])
			else:
				print "!!! Error: -"+y+" is an invalid short action or option."
				sys.exit(1)
	else:
		cmdline.append(x)

# process the command arguments
for x in cmdline:
	if not x:
		continue
	if len(x)>=2 and x[0:2]=="--":
			if x in options:
				myopts.append(x)
			elif x[2:] in actions:
				if x[2:]=="rsync":
					#print
					#print red("*** --rsync has been deprecated.")
					#print red("***   Please use '--sync' instead.")
					x="--sync"
				if myaction:
					if myaction not in ["system", "world"]:
						myaction="--"+myaction
					print
					print red("!!!")+green(" Multiple actions requested... Please choose one only.")
					print red("!!!")+" '"+darkgreen(myaction)+"' "+red("or")+" '"+darkgreen(x)+"'"
					print
					sys.exit(1)
				myaction=x[2:]
			else:
				print "!!! Error:",x,"is an invalid option."
				sys.exit(1)
	elif (not myaction) and (x in actions):
		if x not in ["system", "world"]:
			#print red("*** Deprecated use of action '"+x+"'")
			if x=="rsync":
				#print red("***   Please use '--sync' instead.")
				x="sync"
			#else:
				#print red("***   Please use '--"+x+"' instead.")
		if myaction:
			print
			print red("!!!")+green(" Multiple actions requested... Please choose one only.")
			#print red("!!! '")+darkgreen("--"+myaction)+"' "+red("or")+" '"+darkgreen("--"+x)+"'"
			print red("!!! '")+darkgreen(myaction)+"' "+red("or")+" '"+darkgreen(x)+"'"
			print
			sys.exit(1)
		myaction=x
	elif x[-1]=="/":
		# this little conditional helps tab completion
		myfiles.append(x[:-1])
	else:
		myfiles.append(x)

if (myaction in ["world", "system"]) and myfiles:
	print "emerge: please specify a package class (\"world\" or \"system\") or individual packages, but not both."
	sys.exit(1)

# Always create packages if FEATURES=buildpkg
# Imply --buildpkg if --buildpkgonly
if ("buildpkg" in portage.features) or ("--buildpkgonly" in myopts):
	if "--buildpkg" not in myopts:
		myopts.append("--buildpkg")

# Also allow -S to invoke search action (-sS)
if ("--searchdesc" in myopts) and (not myaction):
	myaction = "search"

if ("--getbinpkgonly" in myopts) and not ("--usepkgonly" in myopts):
	myopts.append("--usepkgonly")

if ("--getbinpkgonly" in myopts) and not ("--getbinpkg" in myopts):
	myopts.append("--getbinpkg")

if ("--getbinpkg" in myopts) and not ("--usepkg" in myopts):
	myopts.append("--usepkg")

# Also allow -K to apply --usepkg/-k
if ("--usepkgonly" in myopts) and not ("--usepkg" in myopts):
	myopts.append("--usepkg")

# Also allow -U to apply --update/-u
if ("--upgradeonly" in myopts) and not ("--update" in myopts):
	print ">>> --upgradeonly implies --update... adding --update to options."
	myopts.append("--update")

# Also allow -l to apply --pretend/-p
if ("--changelog" in myopts) and not ("--pretend" in myopts):
	print ">>> --changelog implies --pretend... adding --pretend to options."
	myopts.append("--pretend")

# Set so that configs will be merged regardless of remembered status
if ("--noconfmem" in myopts):
	portage.settings["NOCONFMEM"]="1"

# Set various debug markers... They should be merged somehow.
if ("--debug" in myopts):
	portage.settings["PORTAGE_DEBUG"]="1"
	portage.debug=1


def emergelog(mystr):
	if "notitles" not in portage.features:
		xtermTitle(mystr)
	try:
		mylogfile=open("/var/log/emerge.log", "a")
		mylogfile.write(str(time.time())[:10]+": "+mystr+"\n")
		mylogfile.flush()
		mylogfile.close()
	except Exception, e:
		if edebug:
			print "emergelog():",e
		pass

def emergeexit():
	"""This gets out final log message in before we quit. As it overrides
	any other atexit's we have setup, we need to call them ourself."""
	portage.portageexit()
	emergelog(" *** terminating.")
	if "notitles" not in portage.features:
		xtermTitleReset()
atexit.register(emergeexit)

def emergeexitsig(signum, frame):
	emergeexit()
	sys.exit(100+signum)
signal.signal(signal.SIGINT, emergeexitsig)

def countdown(secs=5, doing="Starting"):
	if secs:
		print ">>> Waiting",secs,"seconds before starting..."
		print ">>> (Control-C to abort)...\n"+doing+" in: ",
		ticks=range(secs)
		ticks.reverse()
		for sec in ticks:
			sys.stdout.write(red(str(sec+1)+" "))
			sys.stdout.flush()
			time.sleep(1)
		print

def getgccversion():
	"""
	rtype: C{str}
	return:  the current in-use gcc version
	"""

	gcc_env_dir = os.path.join('/', 'etc', 'env.d', 'gcc')
	gcc_config_config = os.path.join(gcc_env_dir, 'config')
	gcc_ver_command = '`which gcc` -dumpversion'
	gcc_ver_prefix = 'gcc-'

	gcc_not_found_error = red(
	"!!! No gcc found. You probably need to 'source /etc/profile'\n" +
	"!!! to update the environment of this terminal and possibly\n" +
	"!!! other terminals also."
	)

	gcc_distcc_broken_error = green(
	'!!! Using `which gcc` to gcc locate version, this may break\n' +
	'!!! DISTCC, installing gcc-config and setting your current gcc\n' +
	'!!! profile will fix this'
	)

	def fallback():

		print >>sys.stderr, gcc_distcc_broken_error 

		gccout = commands.getstatusoutput(gcc_ver_command)

		if gccout[0] != 0:
			print >>sys.stderr, gcc_not_found_error
			gccver = "[unavailable]"
		else:
			gccver = gcc_ver_prefix + gccout[1]

		return gccver
	   
	if os.path.isfile(gcc_config_config):
		try:
			gccver = gcc_ver_prefix + open(gcc_config_config).read().strip().split('-')[-1] 
		except IndexError:
			gccver = fallback()

	else:
		import glob
		dir_l = glob.glob(os.path.join(gcc_env_dir, '*-*'))

		if len(dir_l) == 1:
			try: 
				gccver = gcc_ver_prefix + dir_l[0].split('-')[-1]
			except IndexError:
				gccver = fallback()
			
		else:
			# There was no "config" file in /etc/env.d/gcc and there was more
			# than one profile in /etc/env.d/gcc so we can't actively
			# determine what version of gcc we are using so we fall back on the
			# old way that breaks distcc

			gccver = fallback()

	return gccver

def getportageversion():
	try:
		profilever=os.path.basename(os.path.normpath(os.readlink("/etc/make.profile")))
	except:
		profilever="unavailable"
	glibcver=[]
	for x in portage.vardbapi(portage.root).match("glibc"):
		xs=portage.pkgsplit(x)
		if glibcver:
			glibcver+=","+xs[1]+"-"+xs[2]
		else:
			glibcver=xs[1]+"-"+xs[2]
	if glibcver==[]:
		glibcver="unavailable"

	gccver = getgccversion()

	unameout=commands.getstatusoutput("uname -r")[1]

	return "Portage " + portage.VERSION +" ("+profilever+", "+gccver+", glibc-"+glibcver+", "+unameout+")"

def help():
	# Move all the help stuff out of this file.
	emergehelp.help(myaction,myopts,havecolor)

# check if root user is the current user for the actions where emerge needs this
if ("--pretend" in myopts) or (myaction=="search"):
	if not portage.secpass:
		if portage.wheelgid==portage.portage_gid:
			print "emerge: wheel group membership required for \"--pretend\" and search."
			print "emerge: wheel group use is being deprecated. Please update group and passwd to"
			print "        include the portage user as noted above, and then use group portage."
		else:
			print "emerge: portage group membership required for \"--pretend\" and search."
		sys.exit(1)
elif "--version" in myopts:
	print getportageversion()
	sys.exit(0)
elif "--help" in myopts:
	help()
	sys.exit(0)
elif portage.secpass!=2:
	if myaction in ["search", "help", "info"]:
		pass
	elif (not myaction) and (not myfiles):
		pass
	elif ("--pretend" in myopts) and (myaction in ["world","system","clean","prune","unmerge"]):
		pass
	else:
		print "myaction",myaction
		print "myopts",myopts
		print "emerge: root access required."
		sys.exit(1)

if not "--pretend" in myopts:
	emergelog("Started emerge on: "+time.strftime("%b %d, %Y %H:%M:%S", time.localtime()))
	myelogstr=""
	if myopts:
		myelogstr=string.join(myopts, " ")
	if myaction:
		myelogstr+=" "+myaction
	if myfiles:
		myelogstr+=" "+string.join(myfiles, " ")
	emergelog(" *** emerge "+myelogstr)

#configure emerge engine parameters
#
# self:      include _this_ package regardless of if it is merged.
# selective: exclude the package if it is merged
# recurse:   go into the dependencies
# empty:     pretend nothing is merged
myparams=["self","recurse"]
add=[]
sub=[]
if "--update" in myopts:
	add.extend(["selective","empty"])
if "--emptytree" in myopts:
	add.extend(["empty"])
	sub.extend(["selective"])
if "--nodeps" in myopts:
	sub.extend(["recurse"])
if "--noreplace" in myopts:
	add.extend(["selective"])
if "--deep" in myopts:
	add.extend(["deep"])
if "--selective" in myopts:
	add.extend(["selective"])
if myaction in ["world","system"]:
	add.extend(["selective"])
elif myaction in ["depclean"]:
	add.extend(["empty"])
	sub.extend(["selective"])
for x in add:
	if (x not in myparams) and (x not in sub):
		myparams.append(x)
for x in sub:
	if x in myparams:
		myparams.remove(x)

def update_spinner():
	global spinner, spinpos
	if sys.stdout.isatty() and not ("--nospinner" in myopts):
		sys.stdout.write("\b"+spinner[spinpos])
		spinpos=(spinpos+1)%8
		sys.stdout.flush()

# search functionality
class search:
	
	#
	# class constants
	#
	VERSION_SHORT=1
	VERSION_RELEASE=2
	
	#
	# public interface
	#
	def __init__(self):
		"""Searches the available and installed packages for the supplied search key.
		The list of available and installed packages is created at object instantiation.
		This makes successive searches faster."""
		self.installcache = portage.db["/"]["vartree"]
		
	def execute(self,searchkey):
		"""Performs the search for the supplied search key"""
		global myopts
		self.searchkey=searchkey
		self.packagematches = []
		if "--searchdesc" in myopts:
			self.searchdesc=1
			self.matches = {"pkg":[], "desc":[]}
		else:
			self.searchdesc=0
			self.matches = {"pkg":[]}
		print "Searching...   ",
		if self.searchkey=="*":
			#hack for people who aren't regular expression gurus
			self.searchkey==".*"
		if re.search("\+\+", self.searchkey):
			#hack for people who aren't regular expression gurus
			self.searchkey=re.sub("\+\+","\+\+",self.searchkey)
		self.searchre=re.compile(self.searchkey.lower(),re.I)
		for package in portage.portdb.cp_all():
			update_spinner()
			package_parts=package.split("/")
			masked=0
			if self.searchre.search(package_parts[1]):
				if not portage.portdb.xmatch("match-visible",package):
					masked=1
				self.matches["pkg"].append([package,masked])
			elif self.searchdesc: # DESCRIPTION searching
				full_package = portage.portdb.xmatch("bestmatch-visible",package)
				if not full_package:
					#no match found; we don't want to query description
					full_package=portage.best(portage.portdb.xmatch("match-all",package))
					if not full_package:
						continue
					else:
						masked=1
				try:
					full_desc = portage.portdb.aux_get(full_package,["DESCRIPTION"])[0]
				except KeyError:
					print "emerge: search: aux_get() failed, skipping"
					continue
				if self.searchre.search(full_desc):
					self.matches["desc"].append([full_package,masked])
		self.mlen=0
		for mtype in self.matches.keys():
			self.matches[mtype].sort()
			self.mlen += len(self.matches[mtype])	

	def output(self):
		"""Outputs the results of the search."""
		print "\b\b  \n[ Results for search key : "+white(self.searchkey)+" ]"
		print "[ Applications found : "+white(str(self.mlen))+" ]"
		print " "
		for mtype in self.matches.keys():	
			for match,masked in self.matches[mtype]:
				if mtype=="pkg":
					catpack=match
					full_package = portage.portdb.xmatch("bestmatch-visible",match)
					if not full_package:
						#no match found; we don't want to query description
						masked=1
						full_package=portage.best(portage.portdb.xmatch("match-all",match))
				else:
					full_package=match
					catpack=portage.pkgsplit(match)[0]
				if full_package:
					try:
						desc, homepage = portage.portdb.aux_get(full_package,["DESCRIPTION","HOMEPAGE"])
					except KeyError:
						print "emerge: search: aux_get() failed, skipping"
						continue
					if masked:
						print green("*")+"  "+white(match)+" "+red("[ Masked ]")
					else:
						print green("*")+"  "+white(match)
					myversion = self.getVersion(full_package, search.VERSION_RELEASE)

					mysum = [0,0]
					mycat = match.split("/")[0]
					mypkg = match.split("/")[1] + "-" + myversion

					mydigest = portage.db["/"]["porttree"].dbapi.finddigest(mycat+"/"+mypkg)
					
					try:
						myfile = open(mydigest,"r")
						for line in myfile.readlines():
							mysum[0] += int(line.split(" ")[3])
						myfile.close()
						mystr = str(mysum[0]/1024)
						mycount=len(mystr)
						while (mycount > 3):
							mycount-=3
							mystr=mystr[:mycount]+","+mystr[mycount:]
						mysum[0]=mystr+" kB"
					except Exception, e:
						if edebug:
							print "!!! Exception:",e
						mysum[0]=" [no/bad digest]"
						
					if "--quiet" not in myopts:
						print "     ", darkgreen("Latest version available:"),myversion
						print "     ", self.getInstallationStatus(catpack)
						print "     ", darkgreen("Size of downloaded files:"),mysum[0]
						print "     ", darkgreen("Homepage:")+"   ",homepage
						print "     ", darkgreen("Description:"),desc
						print
		print
	#
	# private interface
	#
	def getInstallationStatus(self,package):
		installed_package = self.installcache.dep_bestmatch(package)
		result = ""
		version = self.getVersion(installed_package,search.VERSION_RELEASE)
		if len(version) > 0:
			result = darkgreen("Latest version installed:")+" "+version
		else:
			result = darkgreen("Latest version installed:")+" [ Not Installed ]"
		return result

	def getVersion(self,full_package,detail):
		if len(full_package) > 1:
			package_parts = portage.catpkgsplit(full_package)
			if detail == search.VERSION_RELEASE and package_parts[3] != 'r0':
				result = package_parts[2]+ "-" + package_parts[3]
			else:
				result = package_parts[2]
		else:
			result = ""
		return result


#build our package digraph
def getlist(mode):
	if mode=="system":
		if portage.profiledir:
			pfile=portage.profiledir+"/packages"
		else:
			print "!!! No profile directory; system mode unavailable."
			sys.exit(1)
	elif mode=="world":
		pfile=portage.root+"var/cache/edb/world"
	try:
		myfile=open(pfile,"r")
		mylines=myfile.readlines()
		myfile.close()
	except OSError:
		print "!!! Couldn't open "+pfile+"; exiting."
		sys.exit(1)
	except IOError:
		#world file doesn't exist
		mylines=[]
	mynewlines=[]
	for x in mylines:
		myline=string.join(string.split(x))
		if not len(myline):
			continue
		elif myline[0]=="#":
			continue
		elif mode=="system":
			if myline[0]!="*":
				continue
			myline=myline[1:]
		mynewlines.append(myline.strip())
	return mynewlines

def genericdict(mylist):
	mynewdict={}
	for x in mylist:
		mynewdict[portage.dep_getkey(x)]=x
	return mynewdict

olddbapi=None
class depgraph:

	def __init__(self,myaction,myopts):
		global olddbapi
		self.missingbins=[]
		self.myaction=myaction
		self.virts=portage.getvirtuals("/")
		self.digraph=portage.digraph()
		self.orderedkeys=[]
		#the following is so we have an empty vartree (used in emerge update calculations)
		self.fakedbapi=portage.fakedbapi()
		#self.fakedbapi.cpv_inject("sys-libs/glibc-2.3")	
		self.outdatedpackages=[]
		self.mydbapi={}
		if "empty" in myparams:
			#for --update, we want to rebuild an entire empty tree of dependencies, and then we won't merge was is already merged.
			self.mydbapi["/"]=self.fakedbapi
		else:
			self.mydbapi["/"]=portage.db["/"]["vartree"].dbapi
		if portage.root!="/":
			if "empty" in myparams:
				self.mydbapi[portage.root]=self.fakedbapi
			else:
				self.mydbapi[portage.root]=portage.db[portage.root]["vartree"].dbapi
			
		if "--usepkg" in myopts:
			try:
				portage.db["/"]["bintree"].populate(("--getbinpkg" in myopts), ("--getbinpkgonly" in myopts))
			except ValueError, e:
				sys.stderr.write(red("!!! Failed to get all metadata: "+str(e)+"\n"))
				sys.exit(1)

	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
		if myuse == None:
			myuse=string.split(portage.settings["USE"], " ")
	
		# select the correct /var database that we'll be checking against
		vardbapi=portage.db[myroot]["vartree"].dbapi

		merging=1
		# this is where we add the node to the list of packages to merge
		if addme:
			# 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 myparent and (self.mydbapi[myroot].match(mykey) or vardbapi.match(mykey)):
					# otherwise, encode parent information where we would normally
					# write "(no)merge" and continue:
					mypsplit=string.split(myparent)
					parenttype=mypsplit[0]
					parentroot=mypsplit[1]
					parentkey=mypsplit[2]
					mergeme=mypsplit[-1]
					mykexp=portage.dep_expand(mykey,self.mydbapi[myroot])
					pakexp=portage.dep_expand(parentkey,self.mydbapi[myroot])
					myrealkey=portage.dep_getkey(mykexp)
					parealkey=portage.dep_getkey(pakexp)
					if myrealkey!=parealkey:
						mybigkey.append(myparent.split()[2])
						self.digraph.addnode(string.join(mybigkey),myparent)
					# since our blocks doesn't match any installed packages,
					# it doesn't apply to us and we can ignore it.
				return 1
			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
		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 portage.db[portage.root]["bintree"].isremote(mykey) and ("--pretend" in myopts):
				# It's remote, and we have the info already. In pretend,
				# we don't want to be downloading the tbz2's.
				#if not portage.db[portage.root]["bintree"].remotepkgs.has_key(mypkgparts[1]):
				#	sys.stderr.write("\nThere are no packages available to satisfy "+str(mykey)+"\n\n")
				#	sys.exit(1)
				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["CDEPEND"]=string.join(string.split(edepend["CDEPEND"])," ")
				edepend["SLOT"]   =string.strip(edepend["SLOT"])
			else:
				if portage.db[portage.root]["bintree"].isremote(mykey) or \
				   tbz2name in portage.db[portage.root]["bintree"].invalids:
					# Get the tbz2
					if not portage.db[portage.root]["bintree"].remotepkgs.has_key(tbz2name):
						sys.stderr.write("\nThere are no packages available to satisfy "+str(mykey)+"\n\n")
						sys.exit(1)
					portage.db[portage.root]["bintree"].gettbz2(mykey)
				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["CDEPEND"]=string.join(mytbz2.getelements("CDEPEND")," ")
				edepend["REBUILD"]=string.join(mytbz2.getelements("REBUILD")," ")
				edepend["SLOT"]   =mytbz2.getfile("SLOT",mypkgparts[2])
		elif mytype=="ebuild":
			try:
				#edepend=portage.portdb.aux_get(mykey,["DEPEND","RDEPEND"])
				for x in ["DEPEND","RDEPEND","PDEPEND","REBUILD"]:
					edepend[x]=string.join(portage.portdb.aux_get(mykey,[x]), " ")
			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
			elif 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:
			if x[-5:]==".tbz2":
				if not os.path.exists(x):
					if os.path.exists(portage.settings["PKGDIR"]+"/All/"+x):
						x=portage.settings["PKGDIR"]+"/All/"+x
					elif os.path.exists(portage.settings["PKGDIR"]+"/"+x):
						x=portage.settings["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.basename(x)[:-5]
				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 x[-7:]==".ebuild":
				mykey=os.path.basename(os.path.abspath(x+"/../.."))+"/"+os.path.basename(x)[:-7]
				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:
				mykey=portage.dep_expand(x,portage.portdb)
				# select needs to return 0 on dep_check failure
				#self.mysd=self.select_dep(portage.root,mykey,arg=x)
				try:
					self.mysd=self.select_dep(portage.root,mykey,arg=x)
				except Exception, e:
					print "\n!!! Problem in",mykey,"dependencies."
					print "!!!",e
					sys.exit(1)

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

		self.missingbins=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 self.missingbins == 0:
						print
					self.missingbins+=1
					print "Missing binary for:",xs[2]

		# We're true here unless we are missing binaries.
		return (not self.missingbins,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

		myslot=portage.portdb.aux_get(pkgver,["SLOT"])[0]
		alleb=portage.portdb.xmatch("match-all",pkg)
		while alleb:
			cand=portage.portdb.xmatch("bestmatch-list",pkg,mylist=alleb)
			if not cand:
				break
			curslot=portage.portdb.aux_get(cand,["SLOT"])[0]
			if (curslot==myslot) and vardbapi.cpv_exists(cand):
				# installed, is this package newer?
				if portage.pkgcmp(portage.catpkgsplit(pkgver)[1:], portage.catpkgsplit(cand)[1:]) < 0:
					return cand
				break
			alleb.remove(cand)

	def select_dep(self,myroot,depstring,myparent=None,arg=None,myuse=None):
		"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],myuse=myuse)
			if not mycheck[0]:
				return 0
			mymerge=mycheck[1]
		else:
			#we're processing a command-line argument; unconditionally merge it even if it's already merged
			mymerge=[depstring]
		if "--debug" in myopts:
			print "Candidates:",mymerge
		for x in mymerge:
			myk=None
			binpkguseflags=None
			if x[0]=="!":
				#add our blocker; it will be ignored later if necessary (if we are remerging the same pkg, for example)
				myk=["blocks",myroot,x[1:]]
			else:
				#We are not processing a blocker but a normal dependency
				myeb_pkg=None
				if ("--usepkg" in myopts):
					myeb_pkg=portage.db[portage.root]["bintree"].dep_bestmatch(x)
				
				myeb=None
				if ("--usepkgonly" not in myopts):
					myeb=portage.portdb.xmatch("bestmatch-visible",x)
				
				if (not myeb) and (not myeb_pkg):
					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:
							print "\n!!! "+red("all ebuilds that could satisfy ")+green(xinfo)+red(" have been masked.")
							if myparent:
								print     "!!!    "+red(xfrom)
						else:
							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 masked or unmasked ebuilds to satisfy "+xinfo+"."
					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: # eb is newer than pkg
						myeb_pkg = None
					else:
						myeb = None

				if "--upgradeonly" in myopts:
					# Check that there isn't a newer version of this package already installed
					cand = None
					if myeb:
						cand=self.is_newer_ver_installed(myroot,x,myeb)
					elif myeb_pkg:
						cand=self.is_newer_ver_installed(myroot,x,myeb_pkg)
					if cand:
						myeb=cand
				
				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
		if mode=="system":
			mylist=syslist
		else:
			#world mode
			worldlist=getlist("world")
			sysdict=genericdict(syslist)
			worlddict=genericdict(worldlist)
			#we're effectively upgrading sysdict to contain all new deps from worlddict
			for x in worlddict.keys():
				#only add the world node if the package is:
				#actually installed -- this prevents the remerging of already unmerged packages when we do a world --update;
				#actually available -- this prevents emerge from bombing out due to no match being found (we want a silent ignore)
				if "empty" in myparams:
					if portage.db["/"]["vartree"].dbapi.match(x):
						sysdict[x]=worlddict[x]
				elif portage.db[portage.root]["vartree"].dbapi.match(x):
					#package is installed
					sysdict[x]=worlddict[x]
				else:
					print "\n*** Package in world file is not installed: "+x
			mylist=[]
			for x in sysdict.keys():
				mylist.append(sysdict[x])

		for mydep in mylist:	
			myeb=portage.portdb.xmatch("bestmatch-visible",mydep)
			if not myeb:
				#this is an unavailable world entry; just continue
				continue

			if "--upgradeonly" in myopts:
				cand=self.is_newer_ver_installed(portage.root,mydep,myeb)
				if cand:
					myeb=cand

			#THIS NEXT BUNCH OF CODE NEEDS TO BE REPLACED TO SUPPORT WORLD ANTI-DEPS
			#if mydep2[0]=="!":, etc.
			if "--usepkg" in myopts:
				mypk=portage.db[portage.root]["bintree"].dep_bestmatch(mydep)
				if myeb==mypk:
					myk=["binary",portage.root,mypk]
				elif "--usepkgonly" in myopts:
					self.missingbins += [myeb]
					myk=["binary",portage.root,myeb]
					#print "mypk:",mypk
					#print "myeb:",myeb
				else:
					myk=["ebuild",portage.root,myeb]
			else:
				myk=["ebuild",portage.root,myeb]
			if not self.create(myk):
				print
				print "!!! Problem with",myk[0],myk[2]
				print "!!! Possibly a DEPEND/*DEPEND problem."
				print
				return 0
		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=[]
		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]+"\" from pkg "+x[3]+")")
					else:
						print red("(\""+x[2]+"\")")
				else:
					if x[3]:
						print red("(from pkg "+x[3]+")")
					else:
						print
			else:
				if x[-1]=="nomerge":
					continue

				if (x[0]!="binary") and ("fetch" in string.split(portage.portdb.aux_get(x[2],["RESTRICT"])[0])):
					fetch = red("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]):
					mynewslot=portage.portdb.aux_get(x[2],["SLOT"])[0]
					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("N")+" "+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+"  "
				if myoldbest:
					myoldbest=portage.pkgsplit(myoldbest)[1]+"-"+portage.pkgsplit(myoldbest)[2]
					if myoldbest[-3:]=="-r0":
						myoldbest=myoldbest[:-3]
					myoldbest=blue("["+myoldbest+"]")

				iuse=""
				if "--verbose" in myopts:
					for ebuild_iuse in string.split(portage.portdb.aux_get(x[2],["IUSE"])[0], " "):
						try:
							if (portage.usesplit.index(ebuild_iuse) >= 0) :
								iuse=iuse+red("+"+ebuild_iuse)+" "
						except ValueError:
							if ebuild_iuse != "":
								iuse=iuse+blue("-"+ebuild_iuse)+" "

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

				if portage.settings.has_key("COLUMNWIDTH"):
					mywidth=int(portage.settings["COLUMNWIDTH"])
				else:
					mywidth=130
				oldlp=mywidth-30
				newlp=oldlp-30
				
				if x[1]!="/":
					if "--columns" in myopts:
						myprint="["+x[0]+" "+addl+"] "+darkgreen(xs[0])
						if (newlp-len(myprint)) > 0:
							myprint=myprint+(" "*(newlp-len(myprint)))
						myprint=myprint+"["+darkblue(xs[1]+xs[2])+"] "
						if (oldlp-len(myprint)) > 0:
							myprint=myprint+" "*(oldlp-len(myprint))
						myprint=myprint+myoldbest
						myprint=myprint+darkgreen("  to "+x[1])+" "+iuse
					else:
						myprint="["+x[0]+" "+addl+"] "+darkgreen(x[2])+" "+myoldbest+" "+darkgreen("to "+x[1])+" "+iuse
				else:
					if "--columns" in myopts:
						myprint="["+x[0]+" "+addl+"] "+darkgreen(xs[0])
						if (newlp-len(myprint)) > 0:
							myprint=myprint+(" "*(newlp-len(myprint)))
						myprint=myprint+green(" ["+xs[1]+xs[2]+"] ")
						if (oldlp-len(myprint)) > 0:
							myprint=myprint+(" "*(oldlp-len(myprint)))
						myprint=myprint+myoldbest+"  "+iuse
					else:
						myprint="["+x[0]+" "+addl+"] "+darkgreen(x[2])+" "+myoldbest+" "+iuse
				print myprint

			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:
							if mylist.index(x)<len(mylist)-1:
								print red("*** Portage will stop merging at this point and reload itself,")
								print red("    recalculate dependencies, and complete the merge.")
								if "--update" not in myopts:
									print 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:
							print red("***")+" Please update portage to the above version before proceeding."
							print "    Failure to do so may result in failed or improper merges."
							print "    A simple '"+green("emerge -u portage")+"' is sufficient."
							print
			del mysplit

		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:
			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)

		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 mymergelist[0]
		else:
			myfavs=portage.grabfile(portage.root+"var/cache/edb/world")
			myfavdict=genericdict(myfavs)
			mysysdict=genericdict(syslist)
			for x in range(len(mylist)):
				if mylist[x][3]!="nomerge":
					# Add to the mergelist
					mymergelist.append(mylist[x])
				else:
					# Add to the world file. Since we won't be able to later.
					myfavkey=portage.cpv_getkey(mylist[x][2])
					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+"var/cache/edb/world",writekey=0)

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

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

		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])
			if x[0] in ["ebuild","blocks"]:
				if ("--fetchonly" in myopts) or (x[0]=="blocks"):
					retval=portage.doebuild(y,"fetch",myroot,edebug,("--pretend" in myopts),fetchonly=1)
					if retval:
						print
						print "!!! Fetch for",y,"failed, continuing..."
						print	
						returnme=1
					continue
				elif "--buildpkg" in myopts:
					#create pkg, then merge pkg
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Cleaning ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"clean",myroot,edebug)
					if retval:
						sys.exit(1)
					portage.settings["FEATURES"]=myorigfeat # Put back flags.
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Compiling/Packaging ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"package",myroot,edebug)
					if retval:
						sys.exit(1)
					#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])
						emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Merging ("+x[pkgindex]+"::"+y+")")
						retval=portage.pkgmerge(mytbz2,myroot)
						if retval==None:
							sys.exit(1)
				else:
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Cleaning ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"clean",myroot,edebug)
					if retval:
						sys.exit(1)
					portage.settings["FEATURES"]=myorigfeat # Put back flags.
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Compiling/Merging ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"merge",myroot,edebug)
					if retval:
						sys.exit(1)
					#dynamically update our database	
			elif x[0]=="binary":
				if ("--fetchonly" in myopts):
					# Fetch binaries
					continue
				#merge the tbz2
				mytbz2=portage.db[portage.root]["bintree"].getname(x[2])
				emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Merging Binary ("+x[pkgindex]+"::"+y+")")
				retval=portage.pkgmerge(mytbz2,x[1])
				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 (not "--fetchonly" in myopts) and (myfavkey in favorites):
					myfavs=portage.grabfile(myroot+"var/cache/edb/world")
					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+"var/cache/edb/world",writekey=0)

				if ("noclean" not in portage.features) and (x[0] != "binary"):
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Post-Build Cleaning ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"clean",myroot,edebug)
					if retval:
						sys.exit(1)
			
				if ("--pretend" not in myopts) and ("--fetchonly" not in myopts):
					# Clean the old package that we have merged over top of it.
					if portage.settings.has_key("AUTOCLEAN") and (portage.settings["AUTOCLEAN"]=="yes"):
						xsplit=portage.pkgsplit(x[2])
						emergelog(" >>> AUTOCLEAN: "+xsplit[0])
						if not unmerge("clean", [xsplit[0]]):
							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:
							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()
								os.execv("/usr/lib/portage/bin/emerge", myargv)

			if ("--pretend" not in myopts) and ("--fetchonly" 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):
				if (mergecount>0):
					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:
			if returnme:
				print "\n\n!!! Some fetch errors were encountered.  Please see above for details.\n\n"
				sys.exit(returnme)
			else:
				sys.exit(0)

def unmerge(unmerge_action, unmerge_files):
	candidate_catpkgs=[]
	global_unmerge=0
	
	if not unmerge_files or "world" in unmerge_files or "system" in unmerge_files:
		if "unmerge"==unmerge_action:
			print
			print bold("emerge unmerge")+" can only be used with specific package names, not with "+bold("world")+" or"
			print bold("system")+" targets."
			print
			return 0
		else:
			global_unmerge=1
	
	localtree=portage.db[portage.root]["vartree"]
	# process all arguments and add all valid db entries to candidate_catpkgs
	if global_unmerge:
		if not unmerge_files or "world" in unmerge_files:
			candidate_catpkgs.extend(localtree.getallnodes())	
		elif "system" in unmerge_files:
			candidate_catpkgs.extend(getlist("system"))
	else:
		#we've got command-line arguments
		if not unmerge_files:
			print "\nNo packages to unmerge have been provided.\n"
			return 0
		for x in unmerge_files:
			arg_parts=x.split('/')
			if arg_parts[-1][-7:]!=".ebuild":
				#possible cat/pkg or dep; treat as such
				candidate_catpkgs.append(x)
			elif unmerge_action in ["prune","clean"]:
				print "\n!!! Prune and clean do not accept individual ebuilds as arguments;\n    skipping.\n"
				continue
			else:
				# it appears that the user is specifying an installed ebuild and we're in "unmerge" mode, so it's
				# ok.
				if not os.path.exists(x):
					print "\n!!! The path '"+x+"' doesn't exist.\n"
					return 0
				absx=os.path.abspath(x)
				spabsx=absx.split("/")
				if absx[:12]!="/var/db/pkg/" or len(spabsx)!=7:
					print spabsx
					print absx
					print "\n!!!",x,"is not inside /var/db/pkg; aborting.\n"
					return 0
				candidate_catpkgs.append("="+spabsx[4]+"/"+spabsx[5])

	if "--pretend" in myopts:
		print darkgreen("\n>>> These are the packages that I would unmerge:")
	
	pkgmap={}
	numselected=0
	for x in candidate_catpkgs:
		#cycle through all our candidate deps and determine what will and will not get unmerged
		mymatch=localtree.dep_match(x)
		if not mymatch and x[0] not in "<>=~":
			#add a "=" if missing
			mymatch=localtree.dep_match("="+x)
		if not mymatch:
			print "\n!!! Couldn't find match for",white(x)
			continue
		mykey=portage.key_expand(portage.dep_getkey(mymatch[0]),portage.db["/"]["vartree"].dbapi)
		if not pkgmap.has_key(mykey):
			pkgmap[mykey]={"protected":[], "selected":[], "omitted":[] }
		if unmerge_action=="unmerge":
				for y in mymatch:
					if not y in pkgmap[mykey]["selected"]:
						pkgmap[mykey]["selected"].append(y)
						numselected=numselected+len(mymatch)
		else:
			#unmerge_action in ["prune", clean"]
			slotmap={}
			for mypkg in mymatch:
				if unmerge_action=="clean":
					myslot=localtree.getslot(mypkg)
				else:
					#since we're pruning, we don't care about slots and put all the pkgs in together
					myslot=0
				if not slotmap.has_key(myslot):
					slotmap[myslot]={}
				slotmap[myslot][localtree.dbapi.cpv_counter(mypkg)]=mypkg
			for myslot in slotmap.keys():
				counterkeys=slotmap[myslot].keys()
				counterkeys.sort()
				if not counterkeys:
					continue
				counterkeys.sort()
				pkgmap[mykey]["protected"].append(slotmap[myslot][counterkeys[-1]])
				del counterkeys[-1]
				#be pretty and get them in order of merge:
				for ckey in counterkeys:
					pkgmap[mykey]["selected"].append(slotmap[myslot][ckey])
					numselected=numselected+1
				#ok, now the last-merged package is protected, and the rest are selected
	if global_unmerge and not numselected:
		print "\n>>> No outdated packages were found on your system.\n"
		return 0

	if not numselected:
		print "\n>>>",unmerge_action+": No packages selected for removal.\n"
		return 0

	if mymatch:
		for x in candidate_catpkgs:
			for y in localtree.dep_nomatch(mymatch[0]):
				if y not in pkgmap[mykey]["omitted"] and \
				   y not in pkgmap[mykey]["selected"] and \
				   y not in pkgmap[mykey]["protected"]:
					pkgmap[mykey]["omitted"].append(y)

	for x in pkgmap.keys():
		if global_unmerge and not pkgmap[x]["selected"]:
			#avoid cluttering the preview printout with stuff that isn't getting unmerged
			continue
		print "\n "+white(x)
		for mytype in ["selected","protected","omitted"]:
			print string.rjust(mytype,12)+":",
			if pkgmap[x][mytype]:
				for mypkg in pkgmap[x][mytype]:
					mysplit=portage.catpkgsplit(mypkg)
					if mysplit[3]=="r0":
						myversion=mysplit[2]
					else:
						myversion=mysplit[2]+"-"+mysplit[3]
					if mytype=="selected":
						print red(myversion),
					else:
						print green(myversion),
			else:
					print "none",
			print
	
	print "\n>>>",red("'Selected'"),"packages are slated for removal."
	print ">>>",green("'Protected'"),"and",green("'omitted'"),"packages will not be removed.\n"
	
	if "--pretend" in myopts:
		#we're done... return
		return 0
	#the real unmerging begins, after a short delay....
	
	if portage.settings["CLEAN_DELAY"]:
		secs=string.atoi(portage.settings["CLEAN_DELAY"])
	else:
		secs=5
	countdown(secs, ">>> Unmerging")

	for x in pkgmap.keys():
		for y in pkgmap[x]["selected"]:
			print ">>> Unmerging "+y+"..."
			emergelog("=== Unmerging... ("+y+")")
			mysplit=string.split(y,"/")
			#unmerge...
			retval=portage.unmerge(mysplit[0],mysplit[1],portage.root,unmerge_action not in ["clean","prune"])
			if retval:
				emergelog(" !!! unmerge FAILURE: "+y)
			else:
				emergelog(" >>> unmerge success: "+y)
	#run ldconfig, etc...
	portage.env_update()
	if not numselected:
		return 0
	else:
		return 1


def post_emerge(retval=0):
	auxpat=re.compile('^([^-]*)(-\d+)?\.info(-\d+)?(\.gz)?')	
	os.chdir("/")
	global myopts
	print
	if "--pretend" in myopts:
		sys.exit(retval)

	emergelog(" *** exiting successfully.")
	root=portage.root

	infodirs=[]
	infodirs.extend(string.split(portage.settings["INFOPATH"], ":"))
	infodirs.extend(string.split(portage.settings["INFODIR"], ":"))

	if os.path.exists("/usr/bin/install-info"):
		regen=0
		for z in infodirs:
			if z=='':
				continue
			inforoot=root+z
			if os.path.isdir(inforoot):
				try:
					infomtime=os.stat(inforoot)[ST_MTIME]
				except:
					infomtime=0

				if not portage.mtimedb.has_key("info"):
					portage.mtimedb["info"]={}
				if portage.mtimedb["info"].has_key(inforoot):
					if portage.mtimedb["info"][inforoot]==infomtime:
						pass
					else:
						portage.mtimedb["info"][inforoot]=0
						regen+=1
				else:
					regen+=1

		if not regen:
			print " "+green("*")+" GNU info directory index is up-to-date."
		else:
			print " "+green("*")+" Regenerating GNU info directory index..."

			icount=0
			badcount=0
			for z in infodirs:
				if z=='':
					continue
				inforoot=root+z
				if portage.mtimedb["info"].has_key(inforoot):
					if portage.mtimedb["info"][inforoot]!=0:
						continue
				try:
					os.rename(inforoot+"/dir",inforoot+"/dir.old")
				except:
					pass
				
				if not os.path.isdir(inforoot):
					continue
				for x in os.listdir(inforoot):
					aux=auxpat.search(x)
					if not aux:
						continue
					auxgroups=aux.groups()	
					if not (auxgroups[1] or auxgroups[2]):
						myso=commands.getstatusoutput("/usr/bin/install-info --dir-file="+inforoot+"/dir "+inforoot+"/"+x)[1]
						if myso!="":
							badcount=badcount+1
							if "--verbose" in myopts:
								print myso
						icount=icount+1
				#update mtime so we can potentially avoid regenerating.
				portage.mtimedb["info"][inforoot]=os.stat(inforoot)[ST_MTIME]

			if badcount:
				if "--verbose" not in myopts:
					print " "+yellow("*")+" Processed",icount,"info files:",badcount,"errors; run with "+green("emerge --verbose")+" to view errors."
				else:
					print " "+yellow("*")+" Processed",icount,"info files;",badcount,"errors."
			else:
				print " "+green("*")+" Processed",icount,"info files."

	if portage.settings["CONFIG_PROTECT"]:
		#number of directories with some protect files in them
		procount=0
		for x in string.split(portage.settings["CONFIG_PROTECT"]):
			if os.path.isdir(x):
				a=commands.getstatusoutput("cd "+x+"; find . -iname '._cfg????_*'")
				if a[0]!=0:
					print " "+red("*")+" error scanning",x
				else:
					files=string.split(a[1])
					if files:
						procount=procount+1
						print " "+yellow("* IMPORTANT:")+"",len(files),"config files in",x,"need updating."
		if procount:
			#print " "+yellow("*")+" Type "+green("emerge --help config")+" to learn how to update config files."
			print " "+yellow("*")+" Type "+green("emerge --help config")+" to learn how to update config files."
		print
	sys.exit(retval)

# general options that should be taken into account before any action
if "--debug" in myopts:
	edebug=1

if myaction in ["sync","rsync"] and (not "--help" in myopts):
	if "--pretend" in myopts:
		print "emerge: the \"sync\" action does not support \"--pretend.\""
		sys.exit(1)

	emergelog(" === rsync")
	myportdir=portage.settings["PORTDIR"]
	if myportdir[-1]=="/":
		myportdir=myportdir[:-1]
	if not os.path.exists(myportdir):
		print ">>>",myportdir,"not found, creating it."
		os.makedirs(myportdir,0755)	
	syncuri=string.rstrip(portage.settings["SYNC"])
	os.umask(0022)
	if syncuri[:8]=="rsync://":
		if not os.path.exists("/usr/bin/rsync"):
			print "!!! /usr/bin/rsync does not exist, so rsync support is disabled."
			print "!!! Type \"emerge net-misc/rsync\" to enable rsync support."
			sys.exit(1)
		mytimeout=180
		if portage.settings.has_key("RSYNC_TIMEOUT"):
			try:
				mytimeout=int(portage.settings["RSYNC_TIMEOUT"])
			except:
				pass
		mycommand="/usr/bin/rsync -rlptDvz --progress --stats --delete --delete-after --timeout="+str(mytimeout)+" --exclude='distfiles/*' --exclude='local/*' --exclude='packages/*' "
		if portage.settings.has_key("RSYNC_EXCLUDEFROM"):
			if os.path.exists(portage.settings["RSYNC_EXCLUDEFROM"]):
				mycommand=mycommand+" --exclude-from "+portage.settings["RSYNC_EXCLUDEFROM"]
			else:
				print "!!! RSYNC_EXCLUDEFROM specified, but file does not exist."
		mycommand=mycommand+" "+syncuri+"/* "+myportdir
		print ">>> starting rsync with "+syncuri+"..."
		exitcode=portage.spawn(mycommand,free=1)
		#exitcode=0
		try:
			maxretries=int(portage.settings["RSYNC_RETRIES"])
		except:
			maxretries=3 #default number of retries
		retries=1
		while (exitcode not in [0,1,2,3,4,11,14,20,21]) and (retries<=maxretries):
			print "\n\n>>> Starting retry %d of %d"% (retries,maxretries)
			time.sleep(11)
			retries=retries+1
			exitcode=portage.spawn(mycommand,free=1)
		if (exitcode>0):
			print
			if exitcode==1:
				print darkred("!!!")+green(" Rsync has reported that there is a syntax error. Please ensure")
				print darkred("!!!")+green(" that your SYNC statement is proper.")
				print darkred("!!!")+green(" SYNC="+portage.settings["SYNC"])
			elif exitcode==11:
				print darkred("!!!")+green(" Rsync has reported that there is a File IO error. Normally")
				print darkred("!!!")+green(" this means your disk is full, but can be caused by corruption")
				print darkred("!!!")+green(" on the filesystem that contains PORTDIR. Please investigate")
				print darkred("!!!")+green(" and try again after the problem has been fixed.")
				print darkred("!!!")+green(" PORTDIR="+portage.settings["PORTDIR"])
			elif exitcode==20:
				print darkred("!!!")+green(" Rsync was killed before it finished.")
			else:
				print darkred("!!!")+green(" Rsync has not successfully finished. It is recommended that you keep")
				print darkred("!!!")+green(" trying or that you use the 'emerge-webrsync' option if you are unable")
				print darkred("!!!")+green(" to use rsync due to firewall or other restrictions. This should be a")
				print darkred("!!!")+green(" temporary problem unless complications exist with your network")
				print darkred("!!!")+green(" (and possibly your system's filesystem) configuration.")
			print
			sys.exit(exitcode)
	elif syncuri[:6]=="cvs://":
		if not os.path.exists("/usr/bin/cvs"):
			print "!!! /usr/bin/cvs does not exist, so rsync support is disabled."
			print "!!! Type \"emerge dev-util/cvs\" to enable CVS support."
			sys.exit(1)
		cvsroot=syncuri[6:]
		cvsdir=os.path.dirname(myportdir)
		if not os.path.exists(myportdir+"/CVS"):
			#initial checkout
			print ">>> starting initial cvs checkout with "+syncuri+"..."
			if not portage.spawn("cd "+cvsdir+"; cvs -d "+cvsroot+" login",free=1):
				print "!!! cvs login error; exiting."
				sys.exit(1)
			if os.path.exists(cvsdir+"/gentoo-x86"):
				print "!!! existing",cvsdir+"/gentoo-x86 directory; exiting."
				sys.exit(1)
			if not portage.spawn("cd "+cvsdir+"; cvs -z0 -d "+cvsroot+" co gentoo-x86",free=1):
				print "!!! cvs checkout error; exiting."
				sys.exit(1)
			if cvsdir!=myportdir:
				portage.movefile(cvsdir,portage.settings["PORTDIR"])
			sys.exit(0)
		else:
			#cvs update
			print ">>> starting cvs update with "+syncuri+"..."
			sys.exit(portage.spawn("cd "+myportdir+"; cvs -z0 -q update -dP",free=1)) 
	else:
		print "!!! rsync setting: ",syncuri,"not recognized; exiting."
		sys.exit(1)
	if os.path.exists(myportdir+"/metadata/cache"):
		print "\n>>> Updating Portage cache...  ",
		os.umask(0002)
		portage.db["/"]["porttree"].dbapi.flush_auxcache()
		if os.path.exists(portage.dbcachedir):
			portage.spawn("rm -Rf "+portage.dbcachedir,free=1)
		try:
			os.mkdir(portage.dbcachedir)
			os.chown(portage.dbcachedir, os.getuid(), portage.portage_gid)
			os.chmod(portage.dbcachedir, 06775)
			os.umask(002)
		except:
			pass
		mynodes=portage.portdb.cp_all()
		portage.eclassdb["modifications_limit"] = 1000
		for x in mynodes:
			myxsplit=x.split("/")
			if not os.path.exists(portage.dbcachedir+"/"+myxsplit[0]):
				os.mkdir(portage.dbcachedir+"/"+myxsplit[0])
				os.chown(portage.dbcachedir+"/"+myxsplit[0], os.getuid(), portage.portage_gid)
				os.chmod(portage.dbcachedir+"/"+myxsplit[0], 06775)
			mymatches=portage.portdb.xmatch("match-all",x)
			for y in mymatches:
				update_spinner()
				try:
					ignored=portage.portdb.aux_get(y,[],metacachedir=myportdir+"/metadata/cache",debug=("cachedebug" in portage.features))
				except:
					pass
		portage.eclassdb["modifications_limit"] = 1
		portage.save_eclassdb(forced=1)
		portage.spawn("chmod -R g+rw "+portage.dbcachedir, free=1)
		sys.stdout.write("\b\b  ...done!\n\n")
		sys.stdout.flush()

	portage.portageexit()
	reload(portage)
	mybestpv=portage.portdb.xmatch("bestmatch-visible","sys-apps/portage")
	mypvs=portage.best(portage.db[portage.root]["vartree"].dbapi.match("sys-apps/portage"))

	if(mybestpv != mypvs):
		print
		print red(" * ")+bold("An update to portage is available.")+" It is _highly_ recommended"
		print red(" * ")+"that you update portage now, before any other packages are updated."
		print red(" * ")+"Please do so and then update "+bold("ALL")+" of your configuration files."
		print
elif myaction=="regen":
	emergelog(" === regen")
	#regenerate cache entries
	print "Regenerating cache entries... "
	sys.stdout.flush()
	mynodes=portage.portdb.cp_all()
	for x in mynodes:
		mymatches=portage.portdb.xmatch("match-all",x)
		if not "--quiet" in myopts:
			print "processing",x
		for y in mymatches:
			try:
				foo=portage.portdb.aux_get(y,["DEPEND"],debug=1)
			except:
				print "\nerror processing",y+", continuing..."
	print "done!"
# HELP action
elif "config"==myaction:
	emergelog(" === config")
	print
	print "Currently, \'config\' is a help option only."
	print
# INFO action
elif "info"==myaction:
	unameout=commands.getstatusoutput("/bin/uname -mrp")[1]
	print getportageversion()
	print "================================================================="
	print "System uname: "+unameout
	os.system("cat /etc/gentoo-release")
	
	output=commands.getstatusoutput("`which distcc` --version")
	if not output[0]:
		print str(string.split(output[1],"\n",1)[0]),
		if "distcc" in portage.features:
			print "[enabled]"
		else:
			print "[disabled]"

	output=commands.getstatusoutput("`which ccache` -V")
	if not output[0]:
		print str(string.split(output[1],"\n",1)[0]),
		if "ccache" in portage.features:
			print "[enabled]"
		else:
			print "[disabled]"

	if "--verbose" in myopts:
		myvars=portage.settings.keys()
	else:
		myvars=['GENTOO_MIRRORS', 'CONFIG_PROTECT', 'CONFIG_PROTECT_MASK',
				'PORTDIR', 'DISTDIR', 'PKGDIR', 'PORTAGE_TMPDIR', 'PORTDIR_OVERLAY',
				'USE', 'COMPILER', 'CHOST', 'CFLAGS', 'CXXFLAGS','ACCEPT_KEYWORDS', 
				'MAKEOPTS', 'AUTOCLEAN', 'SYNC', 'FEATURES']
	myvars.sort()
	for x in myvars:
		print x+'="'+portage.settings[x]+'"'
	#print portage.settings.keys()
	print
# SEARCH action
elif "search"==myaction:
	if not myfiles:
		print "emerge: no search terms provided."
	else:
		searchinstance = search()
		for mysearch in myfiles:
			try:
				searchinstance.execute(mysearch)
			except re.error, comment:
				print "\n!!! Regular expression error in \"%s\": %s" % ( mysearch, comment )
				sys.exit(1)
			searchinstance.output()
elif "inject"==myaction:
	if not myfiles:
		print "emerge: please specify at least one cat/pkg-ver to inject."
		sys.exit(1)
	if "--pretend" in myopts:
		print "emerge: the \"inject\" action does not support \"--pretend.\""
		sys.exit(1)
	for x in myfiles:
		if x[0] in [">","<","=","!"]:
			print "!!! '"+x+"' is an invalid specification."
			print "!!! Must be 'category/package-version' with no other symbols."
			print
			continue
		mycps=portage.catpkgsplit(x)
		if (not mycps) or (mycps[0]=="null"):
			print "!!!",x,"is not a specific cat/pkg-version, skipping..."
			continue
		if portage.db["/"]["vartree"].exists_specific(x):
			print "!!! Not injecting",x+"; Package already exists."
		else:
			portage.db["/"]["vartree"].dbapi.cpv_inject(x)
			print ">>> Injected",x+"."
			emergelog(" === inject: "+x)
elif "unmerge"==myaction or "prune"==myaction or "clean"==myaction:
	if 1==unmerge(myaction, myfiles):
		post_emerge()
	
elif "depclean"==myaction:
	# Kill packages that aren't explicitly merged or are required as a
	# dependency of another package. World file is explicit.

	print
	print red("*** WARNING ***")+" : DEPCLEAN CAN  SERIOUSLY  IMPAIR YOUR SYSTEM. USE CAUTION."
	print red("*** WARNING ***")+" : (Cancel: CONTROL-C) -- ALWAYS VERIFY ALL PACKAGES IN THE"
	print red("*** WARNING ***")+" : CANDIDATE LIST FOR  SANITY  BEFORE  ALLOWING DEPCLEAN TO"
	print red("*** WARNING ***")+" : UNMERGE ANY PACKAGES."
	print red("*** WARNING ***")+" :"
	print red("*** WARNING ***")+" : USE FLAGS MAY HAVE AN EXTREME EFFECT ON THE OUTPUT."
	print red("*** WARNING ***")+" : SOME LIBRARIES MAY BE USED BY PACKAGES BUT ARE NOT"
	print red("*** WARNING ***")+" : CONSIDERED TO BE A DEPEND DUE TO USE FLAG SETTINGS."
	print red("*** WARNING ***")+" :"
	print red("*** WARNING ***")+" : Packages  in the list  that are  desired  may be added"
	print red("*** WARNING ***")+" : directly to the world file to cause them to be ignored"
	print red("*** WARNING ***")+" : by depclean and maintained in the future. BREAKAGES DUE"
	print red("*** WARNING ***")+" : TO UNMERGING AN  ==IN-USE LIBRARY==  MAY BE REPAIRED BY"
	print red("*** WARNING ***")+" : MERGING  *** THE PACKAGE THAT COMPLAINS ***  ABOUT THE"
	print red("*** WARNING ***")+" : MISSING LIBRARY."
	print
	if not "--pretend" in myopts:
		countdown(10, ">>> Depclean")
		emergelog(" >>> depclean")

	mydepgraph=depgraph(myaction,myopts)
	syslist=getlist("system")
	worldlist=getlist("world")

	print "Calculating",myaction,"dependencies  ",
	if not mydepgraph.xcreate("world"):
		print "\n!!! Failed to create deptree."
		sys.exit(1)
	print "\b\b ... done!"

	if ("--usepkgonly" in myopts) and mydepgraph.missingbins:
		sys.stderr.write(red("The following binaries are not available for merging...\n"))
		for x in mydepgraph.missingbins:
			sys.stderr.write("   "+str(x)+"\n")
		sys.stderr.write("\nThese are required by '--usepkgonly' -- Terminating.\n\n")
		sys.exit(1)

	alldeps=mydepgraph.digraph.allnodes()
	myvarlist=portage.vardbapi(portage.root).cp_all()

	if not syslist:
		print "!!! You have no system list. Cannot determine system from world."
	if not worldlist:
		print "!!! You have no world file. Cannot determine explicit merges."
	if not myvarlist:
		print "!!! You have no /var/db tree. This is a problem."
	if not alldeps:
		print "!!! You have no dependencies. Impossible. Bug."

	if not (syslist and worldlist and myvarlist and alldeps):
		print
		sys.exit(1)

	reallist=[]
	for x in alldeps:
		myparts=portage.catpkgsplit(string.split(x)[2])
		if not myparts:
			sys.stderr.write(
			  red("!!! There appears to be a problem with the following package:\n")+
				red("!!! "+str(string.split(x)[2])+"\n\n")+
				    "!!! Please ensure that blocking/conflicting packages are not merged."+
						"!!! 'emerge -p "+str(string.split(x)[2])+"\n\n")
			if "--pretend" not in myopts:
				countdown(15, "*** Continuing")
			continue
			
		catpack=myparts[0]+"/"+myparts[1]
		if catpack not in reallist:
			reallist.append(catpack)

	cleanlist=[]
	for x in myvarlist:
		if x not in reallist:
			if x not in cleanlist:
				cleanlist.append(x)

	for x in syslist+worldlist:
		myparts = portage.catpkgsplit(x)
		if myparts:
			if myparts[0][0] in ('<','>','='):
				myparts[0] = myparts[0][1:]
			if myparts[0][0] in ('<','>','='):
				myparts[0] = myparts[0][1:]
			catpack=myparts[0]+"/"+myparts[1]
		else:
			catpack=x
		if catpack in cleanlist:
			cleanlist.remove(catpack)

	#print "\n\n\nCleaning: "
	#for x in cleanlist:
	#	print x
	#print

	if len(cleanlist):
		unmerge("unmerge", cleanlist)

	print
	print "Packages installed:   "+str(len(myvarlist))
	print "Packages in world:    "+str(len(worldlist))
	print "Packages in system:   "+str(len(syslist))
	print "Unique package names: "+str(len(reallist))
	print "Required packages:    "+str(len(alldeps))
	if "--pretend" in myopts:
		print "Number to remove:     "+str(len(cleanlist))
	else:
		print "Number removed:       "+str(len(cleanlist))

	post_emerge()

# "update", "system", or just process files:
else:
	favorites=[]
	syslist=getlist("system")
	if ("--pretend" in myopts) and not ("--fetchonly" in myopts):
		print
		print darkgreen("These are the packages that I would merge, in order:")
		print
	if ("--resume" in myopts) and portage.mtimedb.has_key("resume"):
		myresumeopts=portage.mtimedb["resume"]["myopts"][:]
		if "--skipfirst" in myresumeopts:
			del myresumeopts["--skipfirst"]
		for myopt in myopts:
			if myopt not in myresumeopts:
				myresumeopts.append(myopt)
		myopts=myresumeopts
		mydepgraph=depgraph("resume",myopts)
		if "--resume" not in myopts:
			myopts+=["--resume"]
	else:
		if ("--resume" in myopts):
			del myopts[myopts.index("--resume")]
			print darkgreen("emerge: It seems we have nothing to resume...")
			sys.exit(0)

		mydepgraph=depgraph(myaction,myopts)
		if myaction in ["system","world"]:
			print "Calculating",myaction,"dependencies  ",
			if not mydepgraph.xcreate(myaction):
				print "!!! Depgraph creation failed."
				sys.exit(1)
			print "\b\b ...done!"
		else:
			if not myfiles:
				print "emerge: please tell me what to do."
				help()
				sys.exit(1)
				#we don't have any files to process; skip this step and exit
			print "Calculating dependencies  ",
			retval,favorites=mydepgraph.select_files(myfiles)
			if not retval:
				print "\n!!! Error calculating dependencies. Please correct."
				sys.exit(1)
			print "\b\b ...done!"

			if ("--usepkgonly" in myopts) and mydepgraph.missingbins:
				sys.stderr.write(red("The following binaries are not available for merging...\n"))

		if mydepgraph.missingbins:
			for x in mydepgraph.missingbins:
				sys.stderr.write("   "+str(x)+"\n")
			sys.stderr.write("\nThese are required by '--usepkgonly' -- Terminating.\n\n")
			sys.exit(1)

	if ("--pretend" in myopts) and ("--fetchonly" not in myopts):
		if ("--resume" in myopts):
			mydepgraph.display(portage.mtimedb["resume"]["mergelist"])
		else:
			mydepgraph.display(mydepgraph.altlist())
	else:
		if ("--buildpkgonly" in myopts):
			if not mydepgraph.digraph.hasallzeros():
				print "\n!!! --buildpkgonly requires all dependencies to be merged."
				print "!!! Cannot merge requested packages. Merge deps and try again.\n"
				sys.exit(1)

		if ("--resume" in myopts):
			favorites=portage.mtimedb["resume"]["favorites"]
			mydepgraph.merge(portage.mtimedb["resume"]["mergelist"])
		else:
			portage.mtimedb["resume"]={}
			portage.mtimedb["resume"]["myopts"]=myopts
			portage.mtimedb["resume"]["favorites"]=favorites
			if ("--digest" in myopts) and not ("--fetchonly" in myopts):
				for pkgline in mydepgraph.altlist():
					if pkgline[0]=="ebuild" and pkgline[3]=="merge":
						y=portage.portdb.findname(pkgline[2])
						retval=portage.doebuild(y,"digest",portage.root,edebug,("--pretend" in myopts))
			mydepgraph.merge(mydepgraph.altlist())

		if portage.mtimedb.has_key("resume"):
			del portage.mtimedb["resume"]
		if portage.settings["AUTOCLEAN"] and "yes"==portage.settings["AUTOCLEAN"]:
			print ">>> Auto-cleaning packages ..."
			unmerge("clean", ["world"])
	post_emerge()
