From 3e84557b91b43ea6924bbab8a3d477509513cac8 Mon Sep 17 00:00:00 2001
From: Zac Medico <zmedico@gentoo.org>
Date: Wed, 18 Jul 2012 12:55:16 -0700
Subject: [PATCH] Support =*/*-*9999* wildcard atom, bug #402197.

---
 man/portage.5                                 |  3 +
 pym/portage/dep/__init__.py                   | 75 +++++++++++++++----
 pym/portage/tests/dep/testAtom.py             |  4 +
 .../tests/dep/test_best_match_to_list.py      |  2 +
 4 files changed, 69 insertions(+), 15 deletions(-)

diff --git a/man/portage.5 b/man/portage.5
index 9823eb1d5..ad84ff1a7 100644
--- a/man/portage.5
+++ b/man/portage.5
@@ -162,6 +162,9 @@ next to each other.
 
 .I Examples:
 .nf
+# match anything with a version containing 9999, which can be used in
+# package.mask to prevent emerge --autounmask from selecting live ebuilds
+=*/*-*9999*
 # match anything from the 'sys\-apps' category
 sys\-apps/*
 # match packages named 'zlib' from any category
diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py
index f0d07a52a..8286e8dab 100644
--- a/pym/portage/dep/__init__.py
+++ b/pym/portage/dep/__init__.py
@@ -141,9 +141,10 @@ def _get_atom_wildcard_re(eapi_attrs):
 	else:
 		pkg_re = r'[\w+*][\w+*-]*?'
 
-	atom_re = re.compile(r'(?P<simple>(' +
-		_extended_cat + r')/(' + pkg_re +
-		r'))(:(?P<slot>' + _slot_loose + r'))?(' +
+	atom_re = re.compile(r'((?P<simple>(' +
+		_extended_cat + r')/(' + pkg_re + r'))' + \
+		'|(?P<star>=((' + _extended_cat + r')/(' + pkg_re + r'))-(?P<version>\*\d+\*)))' + \
+		'(:(?P<slot>' + _slot_loose + r'))?(' +
 		_repo_separator + r'(?P<repo>' + _repo_name + r'))?$')
 
 	_atom_wildcard_re_cache[cache_key] = atom_re
@@ -1243,18 +1244,27 @@ class Atom(_unicode):
 		self.__dict__['blocker'] = blocker
 		m = atom_re.match(s)
 		extended_syntax = False
+		extended_version = None
 		if m is None:
 			if allow_wildcard:
-				m = _get_atom_wildcard_re(eapi_attrs).match(s)
+				atom_re = _get_atom_wildcard_re(eapi_attrs)
+				m = atom_re.match(s)
 				if m is None:
 					raise InvalidAtom(self)
-				op = None
 				gdict = m.groupdict()
-				cpv = cp = gdict['simple']
+				if m.group('star') is not None:
+					op = '=*'
+					base = atom_re.groupindex['star']
+					cp = m.group(base + 1)
+					cpv = m.group('star')[1:]
+					extended_version = m.group(base + 4)
+				else:
+					op = None
+					cpv = cp = m.group('simple')
 				if cpv.find("**") != -1:
 					raise InvalidAtom(self)
-				slot = gdict['slot']
-				repo = gdict['repo']
+				slot = m.group('slot')
+				repo = m.group('repo')
 				use_str = None
 				extended_syntax = True
 			else:
@@ -1297,7 +1307,7 @@ class Atom(_unicode):
 		except InvalidData:
 			# plain cp, wildcard, or something
 			self.__dict__['cpv'] = cpv
-			self.__dict__['version'] = None
+			self.__dict__['version'] = extended_version
 		self.__dict__['repo'] = repo
 		if slot is None:
 			self.__dict__['slot'] = None
@@ -2003,19 +2013,23 @@ def best_match_to_list(mypkg, mylist):
 	"""
 	operator_values = {'=':6, '~':5, '=*':4,
 		'>':2, '<':2, '>=':2, '<=':2, None:1}
-	maxvalue = -2
+	maxvalue = -99
 	bestm  = None
 	mypkg_cpv = None
 	for x in match_to_list(mypkg, mylist):
 		if x.extended_syntax:
-			if dep_getslot(x) is not None:
+			if x.operator == '=*':
 				if maxvalue < 0:
 					maxvalue = 0
 					bestm = x
-			else:
+			elif x.slot is not None:
 				if maxvalue < -1:
 					maxvalue = -1
 					bestm = x
+			else:
+				if maxvalue < -2:
+					maxvalue = -2
+					bestm = x
 			continue
 		if dep_getslot(x) is not None:
 			if maxvalue < 3:
@@ -2099,7 +2113,39 @@ def match_from_list(mydep, candidate_list):
 
 	mylist = []
 
-	if operator is None:
+	if mydep.extended_syntax:
+
+		for x in candidate_list:
+			cp = getattr(x, "cp", None)
+			if cp is None:
+				mysplit = catpkgsplit(remove_slot(x))
+				if mysplit is not None:
+					cp = mysplit[0] + '/' + mysplit[1]
+
+			if cp is None:
+				continue
+
+			if cp == mycpv or extended_cp_match(mydep.cp, cp):
+				mylist.append(x)
+
+		if mylist and mydep.operator == "=*":
+
+			candidate_list = mylist
+			mylist = []
+			# Currently, only \*\d+\* is supported.
+			ver = mydep.version[1:-1]
+
+			for x in candidate_list:
+				x_ver = getattr(x, "version", None)
+				if x_ver is None:
+					xs = catpkgsplit(remove_slot(x))
+					if xs is None:
+						continue
+					x_ver = "-".join(xs[-2:])
+				if ver in x_ver:
+					mylist.append(x)
+
+	elif operator is None:
 		for x in candidate_list:
 			cp = getattr(x, "cp", None)
 			if cp is None:
@@ -2110,8 +2156,7 @@ def match_from_list(mydep, candidate_list):
 			if cp is None:
 				continue
 
-			if cp == mycpv or (mydep.extended_syntax and \
-				extended_cp_match(mydep.cp, cp)):
+			if cp == mydep.cp:
 				mylist.append(x)
 
 	elif operator == "=": # Exact match
diff --git a/pym/portage/tests/dep/testAtom.py b/pym/portage/tests/dep/testAtom.py
index e0cfaabcf..f5a7d3749 100644
--- a/pym/portage/tests/dep/testAtom.py
+++ b/pym/portage/tests/dep/testAtom.py
@@ -20,6 +20,10 @@ class TestAtom(TestCase):
 				(None,  'sys-apps/portage', None, '0', '[doc]', None), False, False ),
 			( "*/*",
 				(None,  '*/*', None, None, None, None), True, False ),
+			( "=*/*-*9999*",
+				('=*',  '*/*', '*9999*', None, None, None), True, False ),
+			( "=*/*-*9999*:0::repo_name",
+				('=*',  '*/*', '*9999*', '0', None, 'repo_name'), True, True ),
 			( "sys-apps/*",
 				(None,  'sys-apps/*', None, None, None, None), True, False ),
 			( "*/portage",
diff --git a/pym/portage/tests/dep/test_best_match_to_list.py b/pym/portage/tests/dep/test_best_match_to_list.py
index 58ab95b0f..8a1403828 100644
--- a/pym/portage/tests/dep/test_best_match_to_list.py
+++ b/pym/portage/tests/dep/test_best_match_to_list.py
@@ -38,6 +38,8 @@ class Test_best_match_to_list(TestCase):
 						[Atom("=dev-libs/A-1:0")], True),
 					("dev-libs/A-1", [Atom("dev-libs/*", allow_wildcard=True), Atom("=dev-libs/A-1:0")], \
 						[Atom("=dev-libs/A-1:0"), Atom("dev-libs/*", allow_wildcard=True)], True),
+					("dev-libs/A-4.9999-r1", [Atom("dev-libs/*", allow_wildcard=True), Atom("=*/*-*9999*", allow_wildcard=True)], \
+						[Atom("=*/*-*9999*", allow_wildcard=True), Atom("dev-libs/*", allow_wildcard=True)], True),
 					("dev-libs/A-1:0", [Atom("dev-*/*", allow_wildcard=True), Atom("dev-*/*:0", allow_wildcard=True),\
 						Atom("dev-libs/A"), Atom("<=dev-libs/A-2"), Atom("dev-libs/A:0"), \
 						Atom("=dev-libs/A-1*"), Atom("~dev-libs/A-1"), Atom("=dev-libs/A-1")], \
-- 
2.26.2