#!/usr/bin/python -O
# 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.264 2004/02/08 21:24:15 nakano Exp $

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

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

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

def normpath(mystr):
	if mystr and (mystr[0]=='/'):
		return os.path.normpath("///"+mystr)
	else:
		return os.path.normpath(mystr)

def userquery(prompt, responses=None, colours=None):
	"""Displays a prompt and a set of responses, then waits for a response
	which is checked against the responses and the first to match is 
	returned.
	
	prompt: a String.
	responses: a List of Strings.
	colours: a List of Functions taking and returning a String, used to 
	process the responses for display. Typically these will be functions 
	like red() but could be e.g. lambda x: "DisplayString".
	If responses is omitted, defaults to ["Yes, "No"], [green, red].
	If only colours is omitted, defaults to [bold, ...].

	Returns a member of the List responses. (If called without optional
	arguments, returns "Yes" or "No".)
	KeyboardInterrupt is converted to SystemExit to avoid tracebacks being 
	printed."""
	if responses is None:
		responses, colours = ["Yes", "No"], [green, red]
	elif colours is None:
		colours=[bold]
	colours=(colours*len(responses))[:len(responses)]
	print bold(prompt),
	try:
		while True:
			response=raw_input("["+string.join([colours[i](responses[i]) for i in range(len(responses))],"/")+"] ")
			for key in responses:
				if response.upper()==key[:len(response)].upper():
					return key
			for key in responses:
				if response.upper()==key[:len(response)].upper():
					return key
			print "Sorry, response '%s' not understood." % response,
	except KeyboardInterrupt:
		print "Interrupted."
		sys.exit(1)

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",   "metadata",
"prune", "regen",  "rsync",    "search",
"sync",  "system", "unmerge",  "world",
]
options=[
"--ask",
"--buildpkg",     "--buildpkgonly",
"--changelog",    "--columns",
"--debug",        "--deep",
"--digest",
"--emptytree",    "--fetchonly",
"--getbinpkg",    "--getbinpkgonly",
"--help",         "--noconfmem",
"--nodeps",       "--noreplace",
"--nospinner",    "--oneshot",
"--onlydeps",     "--pretend",
"--quiet",        "--resume",
"--searchdesc",   "--selective",
"--skipfirst",
"--tree",
"--update",       "--upgradeonly",
"--usepkg",       "--usepkgonly",
"--verbose",      "--version"
]

shortmapping={
"a":"--ask",
"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",
't':"--tree",
"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)

if ("--tree" in myopts) and ("--columns" in myopts):
	print "emerge: can't specify both of \"--tree\" and \"--columns\"."
	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):
	if myaction and myaction != "search":
		myfiles.append(myaction)
	if "--search" not in myopts:
		myopts.append("--search")
	myaction = "search"

# Always try and fetch binary packages if FEATURES=getbinpkg
if ("getbinpkg" in portage.features):
	myopts.append("--getbinpkg")

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

# Allow -p to remove --ask
if ("--pretend" in myopts) and ("--ask" in myopts):
	print ">>> --pretend disables --ask... removing --ask from options."
	myopts.remove("--ask")

# forbid --ask when not in a terminal
# note: this breaks `emerge --ask | tee logfile`, but that doesn't work anyway.
if ("--ask" in myopts) and (not sys.stdout.isatty()):
	portage.writemsg("!!! \"--ask\" should only be used in a terminal. Exiting.\n")
	sys.exit(1)

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

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

# Python will try to compile the python modules, so we should do
# it outside of a sandbox first.
tmpsettings = portage.config(clone=portage.settings)
portage.spawn("/usr/lib/portage/bin/portageq pkgdir &> /dev/null", tmpsettings, free=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()
		os.chmod("/var/log/emerge.log", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
		os.chown("/var/log/emerge.log", portage.portage_uid, portage.portage_gid)
	except Exception, e:
		if edebug:
			print "emergelog():",e
		pass

def emergeexit():
	"""This gets out final log message in before we quit."""
	if "--pretend" not in myopts:
		emergelog(" *** terminating.")
	if "notitles" not in portage.features:
		xtermTitleReset()
atexit.register(emergeexit)

def emergeexitsig(signum, frame):
	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

# formats a size given in bytes nicely
def format_size(mysize):
	if type(mysize) != types.IntType:
		return mysize
	mystr=str(mysize/1024)
	mycount=len(mystr)
	while (mycount > 3):
		mycount-=3
		mystr=mystr[:mycount]+","+mystr[mycount:]
	return mystr+" kB"


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=os.uname()[2]

	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", "regen"]:
		pass
	elif (not myaction) and (not myfiles):
		pass
	elif ("--pretend" in myopts) and (myaction in ["world","system","clean","prune","unmerge"]):
		pass
	else:
		if "--debug" in myopts:
			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
					match        = portage.pkgsplit(match)[0]

				if full_package:
					try:
						desc, homepage, license = portage.portdb.aux_get(full_package,["DESCRIPTION","HOMEPAGE","LICENSE"])
					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]

					mydigest = portage.db["/"]["porttree"].dbapi.finddigest(mycat+"/"+mypkg + "-" + myversion)
					
					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(mycat+'/'+mypkg)
						print "     ", darkgreen("Size of downloaded files:"),mysum[0]
						print "     ", darkgreen("Homepage:")+"   ",homepage
						print "     ", darkgreen("Description:"),desc
						print "     ", darkgreen("License:")+"    ",license
						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":
		mylines=portage.settings.packages
	elif mode=="world":
		try:
			myfile=open(portage.root+"var/cache/edb/world","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.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=[]
		#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 Exception, e:
				sys.stderr.write(red("!!! Failed to get all metadata:\n")+"    "+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:
			self.pkgsettings.setcpv(mykey)
			myuse=string.split(self.pkgsettings["USE"], " ")

		self.applied_useflags[mykey] = myuse
	
		# 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)):
					mybigkey.append(myparent.split()[2])
					self.digraph.addnode(string.join(mybigkey),myparent)
				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 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["CDEPEND"]=string.join(string.split(edepend["CDEPEND"])," ")
				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["CDEPEND"]=string.join(mytbz2.getelements("CDEPEND")," ")
				edepend["SLOT"]   =mytbz2.getfile("SLOT",mypkgparts[2])
		elif mytype=="ebuild":
			try:
				for x in ["DEPEND","RDEPEND","PDEPEND","CDEPEND"]:
					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(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.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:
				try: 
					mykey=portage.dep_expand(x,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

				if "--debug" in myopts:
					self.mysd = self.select_dep(portage.root,mykey,arg=x)
				else:
					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],self.pkgsettings,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]=="!":
				# if this package is myself, don't append it to block list.
				if (myparent):
					if myparent.split()[2] in portage.portdb.xmatch("match-all", x[1:]):
						continue
				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.")
							print "!!! "+red("possible candidates are:")
							for p in alleb:
								try:
									mreasons = portage.getmaskingstatus(p)
									print "- "+p+" (masked by: "+string.join(mreasons, ", ")+")"
								except:
									pass
							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:
					if not mypk:
						self.missingbins += [myeb]
						myk=["binary",portage.root,myeb]
					else:
						myk=["binary",portage.root,mypk]
				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=[]
		p=[]
		totalsize=0

		if "--verbose" in myopts:
			overlays = string.split(portage.settings['PORTDIR_OVERLAY'])
			overlays = map(os.path.normpath, overlays)
		
		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
		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[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+"]")

				verboseadd=""
				if "--verbose" in myopts:
					# iuse verbose
					try:
						iuse_split = string.split(portage.portdb.aux_get(x[2],["IUSE"])[0], " ")
					except:
						portage.writemsg("!!! Error getting IUSE (report this to bugs.gentoo.org)\n")
						portage.writemsg("!!! %s\n" % x)
						iuse_split = []
					iuse_split.sort()
					iuse=""
					for ebuild_iuse in iuse_split:
						if not ebuild_iuse:
							continue
						if ebuild_iuse in self.applied_useflags[x[2]]:
							iuse=iuse+red("+"+ebuild_iuse)+" "
						elif ebuild_iuse in portage.settings.usemask:
							iuse=iuse+blue("-("+ebuild_iuse+")")+" "
						else:
							iuse=iuse+blue("-"+ebuild_iuse)+" "
					verboseadd+=iuse+" "

					# size verbose
					mysize=0
					if x[0] == "ebuild" and x[-1]!="nomerge":
						mysize=portage.portdb.getsize(x[2], debug=edebug)
						if type(mysize) == types.IntType:
							totalsize+=mysize
						verboseadd+=format_size(mysize)+" "

					# overlay verbose
					file_name=portage.portdb.findname(x[2])
					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

				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 x[1]!="/":
					if "--columns" in myopts:
						myprint="["+x[0]+" "+addl+"] "+indent+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])+" "+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-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+"  "+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:
								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:
			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]
		else:
			myfavs=portage.grabfile(portage.root+"var/cache/edb/world")
			myfavdict=genericdict(myfavs)
			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=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)

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

			#buildsyspkg: Check if we need to _force_ binary package creation
			issyspkg = ("buildsyspkg" in myfeat) \
					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):
					retval=portage.doebuild(y,"fetch",myroot,self.pkgsettings,edebug,("--pretend" in myopts),fetchonly=1)
					if 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
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Cleaning ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"clean",myroot,self.pkgsettings,edebug,cleanup=1)
					if retval:
						sys.exit(1)
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Compiling/Packaging ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"package",myroot,self.pkgsettings,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+")")

						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:
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Cleaning ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"clean",myroot,self.pkgsettings,edebug,cleanup=1)
					if retval:
						sys.exit(1)
					emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Compiling/Merging ("+x[pkgindex]+"::"+y+")")
					retval=portage.doebuild(y,"merge",myroot,self.pkgsettings,edebug)
					if retval:
						sys.exit(1)
					#dynamically update our database	
			elif x[0]=="binary":
				#merge the tbz2
				if portage.db[portage.root]["bintree"].isremote(x[2]):
					emergelog(" --- ("+str(mergecount)+" of "+str(len(mymergelist))+") Fetching Binary ("+x[pkgindex]+"::"+y+")")
					portage.db[portage.root]["bintree"].gettbz2(x[2])

				if ("--fetchonly" in myopts):
					continue
				
				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],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 (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,self.pkgsettings,edebug,cleanup=1)
					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 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:
							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):
					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:
			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
	
	mysettings = portage.config(clone=portage.settings)
	
	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 (x[0] not in [".","/"]) and (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)
				sp_absx = absx.split("/")
				if sp_absx[-1][-7:] == ".ebuild":
					del sp_absx[-1]
					absx = string.join(sp_absx,"/")

				sp_absx_len = len(sp_absx)

				vdb_path = portage.root+portage.VDB_PATH
				vdb_len  = len(vdb_path)
				
				sp_vdb     = vdb_path.split("/")
				sp_vdb_len = len(sp_vdb)

				if not os.path.exists(absx+"/CONTENTS"):
					print "!!! Not a valid db dir: "+str(absx)
					return 0

				if sp_absx_len <= sp_vdb_len:
					# The Path is shorter... so it can't be inside the vdb.
					print spabsx
					print absx
					print "\n!!!",x,"cannot be inside "+(portage.root+portage.VDB_PATH)+"; aborting.\n"
					return 0

				for idx in range(0,sp_vdb_len):
					if (idx >= sp_absx_len) or (sp_vdb[idx] != sp_absx[idx]):
						print sp_absx
						print absx
						print "\n!!!",x,"is not inside "+(portage.root+portage.VDB_PATH)+"; aborting.\n"
						return 0
				
				print "="+string.join(sp_absx[sp_vdb_len:],"/")
				candidate_catpkgs.append("="+string.join(sp_absx[sp_vdb_len:],"/"))

	if ("--pretend" in myopts) or ("--ask" 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
		try:
			mymatch=localtree.dep_match(x)
		except KeyError:
			mymatch=None
		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)

		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 " + white(x) + " to "+unmerge_action+"."
			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

	for x in pkgmap.keys():
		for y in localtree.dep_match(x):
			if y not in pkgmap[x]["omitted"] and \
			   y not in pkgmap[x]["selected"] and \
			   y not in pkgmap[x]["protected"]:
				pkgmap[x]["omitted"].append(y)
		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
	if "--ask" in myopts:
		if userquery("Do you want me to unmerge these packages?")=="No":
			# enter pretend mode for correct formatting of results
			__main__.myopts+=["--pretend"]
			print
			print "Quitting."
			print
			return 0
	#the real unmerging begins, after a short delay....
	
	if mysettings["CLEAN_DELAY"]:
		secs=string.atoi(mysettings["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,mysettings,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):
	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_infodirs=[]
		for z in infodirs:
			if z=='':
				continue
			inforoot=normpath(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]=infomtime
						regen_infodirs.append(inforoot)
				else:
					regen_infodirs.append(inforoot)

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

			icount=0
			badcount=0
			for inforoot in regen_infodirs:
				if inforoot=='':
					continue
				try:
					os.rename(inforoot+"/dir",inforoot+"/dir.old")
				except:
					pass
				
				if not os.path.isdir(inforoot):
					continue
				for x in os.listdir(inforoot):
					if (x[0] == ".") or (x in ["dir","dir.old"]):
						continue
					myso=commands.getstatusoutput("LANG=C /usr/bin/install-info --dir-file="+inforoot+"/dir "+inforoot+"/"+x)[1]
					existsstr="already exists, for file `"
					if myso!="":
						if re.search(existsstr,myso):
							# Already exists... Don't increment the count for this.
							pass
						else:
							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","metadata"] and (not "--help" in myopts):
	if "--pretend" in myopts:
		print "emerge: \"sync\" actions do not support \"--pretend.\""
		sys.exit(1)

	emergelog(" === "+str(myaction))
	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 myaction == "metadata":
		print "skipping sync"
		updatecache_flg = True
		tmpservertimestampfile = None
	elif 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
		
		rsync_flags = [
			"--recursive",    # Recurse directories
			"--links",        # Consider symlinks
			"--safe-links",   # Ignore links outside of tree
			"--perms",        # Preserve permissions
			"--times",        # Preserive mod times
			"--compress",     # Compress the data transmitted
			"--force",        # Force deletion on non-empty dirs
			"--whole-file",   # Don't do block transfers, only entire files
			"--delete",       # Delete files that aren't in the master tree
			"--delete-after", # Delete only after everything else is done
			"--stats",        # Show final statistics about what was transfered
      "--timeout="+str(mytimeout), # IO timeout if not done in X seconds
			"--exclude='distfiles/*'",   # Exclude distfiles from consideration
			"--exclude='local/*'",       # Exclude local     from consideration
			"--exclude='packages/*'",    # Exclude packages  from consideration
		]

		if "--quiet" in myopts:
			rsync_flags.append("--quiet")    # Shut up a lot
		else:
			rsync_flags.append("--progress") # Progress meter for each file
			
		if "--verbose" in myopts:
			rsync_flags.append("--verbose")  # More noise? Not really sure what

		if "--debug" in myopts:
			rsync_flags.append("--checksum") # Force checksum on all files

		if portage.settings.has_key("RSYNC_EXCLUDEFROM"):
			if os.path.exists(portage.settings["RSYNC_EXCLUDEFROM"]):
				rsync_flags.append("--exclude-from="+portage.settings["RSYNC_EXCLUDEFROM"])
			else:
				print "!!! RSYNC_EXCLUDEFROM specified, but file does not exist."

		if portage.settings.has_key("RSYNC_RATELIMIT"):
			rsync_flags.append("--bwlimit="+portage.settings["RSYNC_RATELIMIT"])

		rsynccommand = "/usr/bin/rsync " + string.join(rsync_flags, " ")

		servertimestampdir  = portage.settings["PORTAGE_CACHEDIR"]+"/"
		servertimestampfile = portage.settings["PORTAGE_CACHEDIR"]+"/timestamp.chk"
		tmpservertimestampdir  = portage.settings["PORTAGE_TMPDIR"]+"/"
		tmpservertimestampfile = portage.settings["PORTAGE_TMPDIR"]+"/timestamp.chk"

		# We only use the backup if a timestamp exists in the portdir.
		content=None
		if os.path.exists(myportdir+"/metadata/timestamp.chk"):
			content=portage.grabfile(servertimestampfile)
		if (not content):
			content=portage.grabfile(myportdir+"/metadata/timestamp.chk")

		if (content):
			mytimestamp=time.mktime(time.strptime(content[0], "%a, %d %b %Y %H:%M:%S +0000"))
		else:
			mytimestamp=0

		if not os.path.exists(servertimestampdir):
			os.mkdir(servertimestampdir)
			os.chown(servertimestampdir, os.getuid(), portage.portage_gid)
			os.chmod(servertimestampdir, 02775)

		#exitcode=0
		try:
			maxretries=int(portage.settings["RSYNC_RETRIES"])
		except:
			maxretries=3 #default number of retries

		retries=0
		hostname, port=re.split("rsync://([^:/]*)(:[0-9]+)?", syncuri)[1:3];
		if port==None:
			port=""
		updatecache_flg=True

		ips=[]
		while (1):
			if ips:
				del ips[0]
			if ips==[]:
				try:
					ips=socket.gethostbyname_ex(hostname)[2]
				except Exception, e:
					print "Notice:",str(e)
					dosyncuri=syncuri

			if ips:
				try:
					dosyncuri=string.replace(syncuri, "//"+hostname+port+"/", "//"+ips[0]+port+"/", 1)
				except Exception, e:
					print "Notice:",str(e)
					dosyncuri=syncuri

			if (retries==0):
				if "--ask" in myopts:
					if userquery("Do you want to sync your Portage tree with the mirror at\n" + blue(dosyncuri) + bold("?"))=="No":
						print
						print "Quitting."
						print
						sys.exit(0)
				emergelog(">>> starting rsync with "+dosyncuri)
				print ">>> starting rsync with "+dosyncuri+"..."
			else:
				emergelog(">>> Starting retry %d of %d with %s" % (retries,maxretries,dosyncuri))
				print "\n\n>>> Starting retry %d of %d with %s" % (retries,maxretries,dosyncuri)

			print ">>> checking server timestamp ..."
			mycommand=rsynccommand+" "+dosyncuri+"/metadata/timestamp.chk "+tmpservertimestampdir
			exitcode=portage.spawn(mycommand,portage.settings,free=1)
			if (exitcode==0):
				try:
					servertimestamp = time.mktime(time.strptime(portage.grabfile(tmpservertimestampfile)[0], "%a, %d %b %Y %H:%M:%S +0000"))
				except:
					servertimestamp = 0
				
				if (servertimestamp != 0) and (servertimestamp == mytimestamp):
					emergelog(">>> Cancelling sync -- Already current.")
					print
					print ">>>"
					print ">>> Timestamps on the server and in the local repository are the same."
					print ">>> Cancelling all further sync action. You are already up to date."
					print ">>>"
					print
					sys.exit(0)
				elif (servertimestamp != 0) and (servertimestamp < mytimestamp):
					emergelog(">>> Server out of date: %s" % dosyncuri)
					print
					print ">>>"
					print ">>> SERVER OUT OF DATE: %s" % dosyncuri
					print ">>>"
					print
				elif (servertimestamp == 0) or (servertimestamp > mytimestamp):
					# actual sync
					mycommand=rsynccommand+" "+dosyncuri+"/* "+myportdir
					exitcode=portage.spawn(mycommand,portage.settings,free=1)
					if exitcode in [0,1,2,3,4,11,14,20,21]:
						break
			elif exitcode in [0,1,2,3,4,11,14,20,21]:
				break

			retries=retries+1

			if retries<=maxretries:
				print ">>> retry ..."
				time.sleep(11)
			else:
				# over retries
				# exit loop
				updatecache_flg=False
				break

		if (exitcode==0):
			emergelog("=== Sync completed with %s" % dosyncuri)
		elif (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",portage.settings,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 -P",portage.settings,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",portage.settings,free=1)) 
	else:
		print "!!! rsync setting: ",syncuri,"not recognized; exiting."
		sys.exit(1)


	if os.path.exists(myportdir+"/metadata/cache") and updatecache_flg:
		print "\n>>> Updating Portage cache...  ",
		os.umask(0002)
		cachedir = os.path.normpath(portage.settings["PORTAGE_CACHEDIR"])
		if cachedir in ["/",    "/bin", "/dev",  "/etc",  "/home",
		                "/lib", "/opt", "/proc", "/root", "/sbin",
		                "/sys", "/tmp", "/usr",  "/var"]:
			print "!!! PORTAGE_CACHEDIR IS SET TO A PRIMARY ROOT DIRECTORY ON YOUR SYSTEM."
			print "!!! This is ALMOST CERTAINLY NOT what you want: "+str(cachedir)
			sys.exit(73)
		if os.path.exists(cachedir):
			portage.spawn("rm -Rf "+cachedir+"/*",portage.settings,free=1)
		else:
			os.mkdir(cachedir)

		# save timestamp.chk for next timestamp check.
		try:
			if tmpservertimestampfile != None:
				portage.movefile(tmpservertimestampfile, servertimestampfile)
		except Exception, e:
			print "!!! Failed to save current timestamp."
			print "!!!",e

		portage.portdb.flush_cache()

		try:
			os.umask(002)
			os.chown(cachedir, os.getuid(), portage.portage_gid)
			os.chmod(cachedir, 02775)
		except:
			pass
		mynodes=portage.portdb.cp_all()
		mynodes.sort()
		for x in mynodes:
			myxsplit=x.split("/")
			mymatches=portage.portdb.xmatch("match-all",x)
			mymatches.sort()
			for y in mymatches:
				update_spinner()
				try:
					ignored=portage.portdb.aux_get(y,[],metacachedir=myportdir+"/metadata/cache",debug=("cachedebug" in portage.features))
				except:
					print "\nFailed cache update:",y
		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]"

	ac_vers = string.join(portage.db["/"]["vartree"].dbapi.match("sys-devel/autoconf"), ",")
	am_vers = string.join(portage.db["/"]["vartree"].dbapi.match("sys-devel/automake"), ",")
	print "Autoconf: " + ac_vers
	print "Automake: " + am_vers
	
	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:
			if "--ask" in myopts:
				if userquery("Do you want to inject the package %s?" % x)=="No":
					print
					print "Quitting."
					print
					sys.exit(0)
			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 ("--pretend" not in myopts) and ("--ask" not 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 installed package tree (%s). This is a problem." % portage.VDB_PATH
	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) and ("--ask" 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)) or ("--ask" in myopts):
		if "--tree" in myopts:
			print
			print darkgreen("These are the packages that I would merge, in reverse order:")
			print
		else:
			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:
			myresumeopts.remove("--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 "--ask" in myopts:
		if "--resume" in myopts:
			mydepgraph.display(portage.mtimedb["resume"]["mergelist"])
			prompt="Do you want me to resume merging these packages?"
		else:
			mydepgraph.display(mydepgraph.altlist())
			mergecount=0
			for x in mydepgraph.altlist():
				if x[3]!="nomerge":
					mergecount+=1
			if mergecount==0:
				if portage.settings["AUTOCLEAN"] and "yes"==portage.settings["AUTOCLEAN"]:
					prompt="Nothing to merge; do you want me to auto-clean packages?"
				else:
					print
					print "Nothing to merge; quitting."
					print
					sys.exit(0)
			elif "--fetchonly" in myopts:
				prompt="Do you want me to fetch the source files for these packages?"
			else:
				prompt="Do you want me to merge these packages?"
		print
		if userquery(prompt)=="No":
			print
			print "Quitting."
			print
			sys.exit(0)
		# Don't ask again (e.g. when auto-cleaning packages after merge)
		myopts.remove("--ask")

	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])
						tmpsettings = portage.config(clone=portage.settings)
						retval=portage.doebuild(y,"digest",portage.root,tmpsettings,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()
