Merged from trunk 6765:6802
authorFabian Groffen <grobian@gentoo.org>
Mon, 11 Jun 2007 14:26:02 +0000 (14:26 -0000)
committerFabian Groffen <grobian@gentoo.org>
Mon, 11 Jun 2007 14:26:02 +0000 (14:26 -0000)
   +------------------------------------------------------------------------+
   | 6766    | For bug #181104, make econf give an appropriate error        |
   | zmedico | message when configure is not executable.                    |
   |---------+--------------------------------------------------------------|
   | 6768    | For bug #139383, in xtermTitleReset() use os.system()        |
   | zmedico | instead of commands.getoutput() since the latter puts too    |
   |         | much restriction on what $PROMPT_COMMAND may contain.        |
   |---------+--------------------------------------------------------------|
   | 6770    | For bug #152828, run ${CHOST}-ldconfig when ${CBUILD} !=     |
   | zmedico | ${CHOST}.                                                    |
   |---------+--------------------------------------------------------------|
   | 6772    | Only execute $PROMPT_COMMAND when appropriate.               |
   | zmedico |                                                              |
   |---------+--------------------------------------------------------------|
   | 6775    | Skip bogus '$ECLASS inherited illegally' messages when       |
   | zmedico | installing from a binary package.                            |
   |---------+--------------------------------------------------------------|
   | 6778    | For bug #181387, fix an UnboundLocalError for the mymtime    |
   | zmedico | variable that is triggered by confmem.                       |
   |---------+--------------------------------------------------------------|
   | 6780    | For bug #148702, automatically enable flags from IUSE if     |
   | zmedico | they correspond to unset USE_EXPAND variables such as        |
   |         | LINGUAS. The variable only has to be set to some value or an |
   |         | empty string in order to disable these flags.                |
   |---------+--------------------------------------------------------------|
   | 6782    | When an update is discarded due to confmem, record it's      |
   | zmedico | mtime in the contents (it's md5 is already recorded there    |
   |         | too).                                                        |
   |---------+--------------------------------------------------------------|
   | 6784    | For bug #148702, use the * token to trigger wildcard         |
   | zmedico | expansion of IUSE. This adds flexibility and prevents flags  |
   |         | from being enabled unexpectedly.                             |
   |---------+--------------------------------------------------------------|
   | 6786    | Preserve order of flags from IUSE in wildcard expansion.     |
   | zmedico |                                                              |
   |---------+--------------------------------------------------------------|
   | 6788    | Make repoman work with both + and - flags in IUSE.           |
   | zmedico |                                                              |
   |---------+--------------------------------------------------------------|
   | 6790    | For bug #181551, support slot deps in package.keywords.      |
   | zmedico |                                                              |
   |---------+--------------------------------------------------------------|
   | 6792    | For bug #81097, detect and report suspicious hardlinks to    |
   | zmedico | suid/sgid files. False positives are prevented by doing      |
   |         | reference counts for each inode having suid/sgid bits and    |
   |         | multiple hardlinks. The security check is done prior to each |
   |         | merge or unmerge phase and it will cause the phase to abort  |
   |         | if a problem is found (so that the user can investigate      |
   |         | before any files are removed).                               |
   |---------+--------------------------------------------------------------|
   | 6793    | Don't bother to warn about hardlinks to suid/sgid files      |
   | zmedico | before unlink since it can give false positives and the      |
   |         | existing suid/sgid security check should spot problems       |
   |         | before this anyway.                                          |
   |---------+--------------------------------------------------------------|
   | 6794    | Fix the suid/sgid check so it only looks at regular files    |
   | zmedico | since it can generate false positives otherwise.             |
   |---------+--------------------------------------------------------------|
   | 6796    | For bug #181355, detect parenthesis mismatch in              |
   | zmedico | paren_reduce(), raise an InvalidDependString exception, and  |
   |         | make sure that all callers handle the exception properly.    |
   |---------+--------------------------------------------------------------|
   | 6797    | Remove the "missing left parenthesis" test for now since it  |
   | zmedico | gives false positives.                                       |
   |---------+--------------------------------------------------------------|
   | 6799    | For consistency with other xterm titles code, redirect       |
   | zmedico | $PROMPT_COMMAND output to stderr.                            |
   |---------+--------------------------------------------------------------|
   | 6802    | Bump 2.1.3 to 2.2 for some features and add news about       |
   | zmedico | USE_EXPAND wildcards.                                        |
   +------------------------------------------------------------------------+

svn path=/main/branches/prefix/; revision=6804

NEWS
RELEASE-NOTES
bin/ebuild.sh
bin/repoman
pym/portage/__init__.py
pym/portage/dbapi/bintree.py
pym/portage/dbapi/porttree.py
pym/portage/dbapi/vartree.py
pym/portage/dep.py
pym/portage/output.py

diff --git a/NEWS b/NEWS
index e0f8d66c0ca62c2ef310e6bb74d3b4f51cafff43..3dce9553727640e708ae150248ff703c84702acc 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,6 @@
 News (mainly features/major bug fixes)
 
-portage-2.1.3
+portage-2.2
 -------------
 
 * Allow per-module setting of PORTAGE_ELOG_CLASSES
@@ -17,6 +17,13 @@ portage-2.1.3
 * Make elog functionality available to python code
 * Add support for news items (GLEP 42)
 
+portage-2.1.3
+-------------
+
+* USE_EXPAND variables such as LINGUAS support a * wildcard that will enable
+  all of the corresponding flags that are listed in IUSE for a given package.
+  USE_EXPAND wildcards such as linguas_* are supported in USE and package.use.
+
 portage-2.1.2
 -------------
 
index eba5316e933c3fd46b6f055a54623a75bf7d5298..01507f0c2a579b4556fef9fa1120fbdab53b1990 100644 (file)
@@ -1,7 +1,7 @@
 Release Notes; upgrade information mainly.
 Features/major bugfixes are listed in NEWS
 
-portage-2.1.3
+portage-2.2
 ==================================
 
 * The python namespace for portage has been sanitized, all portage related code
index af075880f1b39b0258be49b43d1bda9cd9c27bfb..66d86818cc466d26a284b9edc338e0a5070c3679 100755 (executable)
@@ -515,6 +515,8 @@ econf() {
                        fi
                        die "econf failed"
                fi
+       elif [ -f "${ECONF_SOURCE:-.}/configure" ]; then
+               die "configure is not executable"
        else
                die "no configure script found"
        fi
@@ -1159,7 +1161,8 @@ inherit() {
                export ECLASS="$1"
 
                if [ "${EBUILD_PHASE}" != "depend" ] && \
-                       [[ ${EBUILD_PHASE} != *rm ]]; then
+                       [[ ${EBUILD_PHASE} != *rm ]] && \
+                       [[ ${EMERGE_FROM} != "binary" ]] ; then
                        # This is disabled in the *rm phases because they frequently give
                        # false alarms due to INHERITED in /var/db/pkg being outdated
                        # in comparison the the eclasses from the portage tree.
@@ -1525,6 +1528,11 @@ if [ "${EBUILD_PHASE}" != "depend" ]; then
        done
        export IUSE=${iuse_temp}
        unset iuse_temp
+       # unset USE_EXPAND variables that contain only the special "*" token
+       for x in ${USE_EXPAND} ; do
+               [ "${!x}" == "*" ] && unset ${x}
+       done
+       unset x
        # Lock the dbkey variables after the global phase
        declare -r DEPEND RDEPEND SLOT SRC_URI RESTRICT HOMEPAGE LICENSE DESCRIPTION
        declare -r KEYWORDS INHERITED IUSE PDEPEND PROVIDE
index 2433adcb7258e50fc3675fbc3de30d9a0a006df1..6b53577a06162ddc9407aab628a8edbf369e67df 100755 (executable)
@@ -1259,6 +1259,10 @@ for x in scanlist:
                        except ValueError:
                                badsyntax.append("parenthesis mismatch")
                                mydeplist = []
+                       except portage.exception.InvalidDependString, e:
+                               badsyntax.append(str(e))
+                               del e
+                               mydeplist = []
 
                        try:
                                portage.dep.use_reduce(mydeplist, excludeall=myiuse)
@@ -1412,13 +1416,12 @@ for x in scanlist:
                myuse = []
                default_use = []
                for myflag in myaux["IUSE"].split():
-                       if myflag.startswith("+"):
+                       flag_name = myflag.lstrip("+-")
+                       if myflag != flag_name:
                                default_use.append(myflag)
-                               myflag = myflag[1:]
-                       myuse.append(myflag)
-               for mypos in range(len(myuse)-1,-1,-1):
-                       if myuse[mypos] and (myuse[mypos] in uselist):
-                               del myuse[mypos]
+                       if flag_name not in uselist:
+                               myuse.append(flag_name)
+
                # uselist checks - local
                mykey = portage.dep_getkey(catpkg)
                if luselist.has_key(mykey):
index 48ef66eba357e1c76ae69ea264b4aff2261060cd..ec84a80b4be3860f1852f850c02e64abf09ee6b5 100644 (file)
@@ -461,13 +461,16 @@ class digraph:
 
 #parse /etc/env.d and generate /etc/profile.env
 
-def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None):
+def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None,
+       env=None):
        if target_root is None:
                global root
                target_root = root
        if prev_mtimes is None:
                global mtimedb
                prev_mtimes = mtimedb["ldpath"]
+       if env is None:
+               env = os.environ
        envd_dir = os.path.join(target_root + EPREFIX, "etc", "env.d")
        portage.util.ensure_dirs(envd_dir, mode=0755)
        fns = listdir(envd_dir, EmptyOnError=1)
@@ -652,8 +655,14 @@ def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None):
                if not libdir_contents_changed:
                        makelinks = False
 
+       ldconfig = EPREFIX+"/sbin/ldconfig"
+       if "CHOST" in env and "CBUILD" in env and \
+               env["CHOST"] != env["CBUILD"]:
+               from portage.process import find_binary
+               ldconfig = find_binary("%s-ldconfig" % env["CHOST"])
+
        # Only run ldconfig as needed
-       if (ld_cache_update or makelinks):
+       if (ld_cache_update or makelinks) and ldconfig:
                # ldconfig has very different behaviour between FreeBSD and Linux
                if ostype=="Linux" or ostype.lower().endswith("gnu"):
                        # We can't update links if we haven't cleaned other versions first, as
@@ -662,14 +671,15 @@ def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None):
                        # we can safely create links.
                        writemsg(">>> Regenerating %s/etc/ld.so.cache...\n" % (target_root+EPREFIX))
                        if makelinks:
-                               commands.getstatusoutput("cd / ; "+EPREFIX+"/sbin/ldconfig -r '%s'" % target_root)
+                               os.system("cd / ; %s -r '%s'" % (ldconfig, target_root))
                        else:
-                               commands.getstatusoutput("cd / ; "+EPREFIX+"/sbin/ldconfig -X -r '%s'" % target_root)
+                               os.system("cd / ; %s -X -r '%s'" % (ldconfig, target_root))
                elif ostype in ("FreeBSD","DragonFly"):
-                       writemsg(">>> Regenerating %svar/run/ld-elf.so.hints...\n" % target_root+EPREFIX+os.sep)
-                       commands.getstatusoutput(
-                               "cd / ; "+EPREFIX+"/sbin/ldconfig -elf -i -f '%s/var/run/ld-elf.so.hints' '%s/etc/ld.so.conf'" % \
-                               (target_root+EPREFIX, target_root+EPREFIX))
+                       writemsg(">>> Regenerating %svar/run/ld-elf.so.hints...\n" % \
+                               target_root+EPREFIX)
+                       os.system(("cd / ; %s -elf -i " + \
+                               "-f '%svar/run/ld-elf.so.hints' '%setc/ld.so.conf'") % \
+                               (ldconfig, target_root+EPREFIX, target_root+EPREFIX))
 
        del specials["LDPATH"]
 
@@ -1705,6 +1715,7 @@ class config:
                cp = dep_getkey(mycpv)
                cpv_slot = self.mycpv
                pkginternaluse = ""
+               iuse = ""
                if mydb:
                        slot, iuse = mydb.aux_get(self.mycpv, ["SLOT", "IUSE"])
                        cpv_slot = "%s:%s" % (self.mycpv, slot)
@@ -1765,6 +1776,9 @@ class config:
                        has_changed = True
                self.configdict["pkg"]["PKGUSE"] = self.puse[:] # For saving to PUSE file
                self.configdict["pkg"]["USE"]    = self.puse[:] # this gets appended to USE
+               if iuse != self.configdict["pkg"].get("IUSE",""):
+                       self.configdict["pkg"]["IUSE"] = iuse
+                       has_changed = True
                # CATEGORY is essential for doebuild calls
                self.configdict["pkg"]["CATEGORY"] = mycpv.split("/")[0]
                if has_changed:
@@ -2028,10 +2042,10 @@ class config:
                usesplit = [ x for x in myflags if \
                        x not in self.usemask]
 
-               usesplit.sort()
-
                # Use the calculated USE flags to regenerate the USE_EXPAND flags so
                # that they are consistent.
+               iuse = self.configdict["pkg"].get("IUSE","").split()
+               iuse = [ x.lstrip("+-") for x in iuse ]
                for var in use_expand:
                        prefix = var.lower() + "_"
                        prefix_len = len(prefix)
@@ -2042,11 +2056,34 @@ class config:
                        # like LINGUAS.
                        var_split = [ x for x in var_split if x in expand_flags ]
                        var_split.extend(expand_flags.difference(var_split))
-                       if var_split or var in self:
+                       if (var_split or var in self) and \
+                               "*" not in var_split:
                                # Don't export empty USE_EXPAND vars unless the user config
                                # exports them as empty.  This is required for vars such as
                                # LINGUAS, where unset and empty have different meanings.
                                self[var] = " ".join(var_split)
+                       elif "*" in var_split:
+                               # * means to enable everything in IUSE that's not masked
+                               filtered_split = []
+                               for x in var_split:
+                                       if x == "*":
+                                               continue
+                                       if (prefix + x) in iuse:
+                                               filtered_split.append(x)
+                               var_split = filtered_split
+                               for x in iuse:
+                                       if x.startswith(prefix) and x not in self.usemask:
+                                               suffix = x[prefix_len:]
+                                               if suffix in var_split:
+                                                       continue
+                                               var_split.append(suffix)
+                                               usesplit.append(x)
+                               if var_split:
+                                       self[var] = " ".join(var_split)
+                               elif var in self:
+                                       # ebuild.sh will see this and unset the variable so
+                                       # that things like LINGUAS work properly
+                                       self[var] = "*"
 
                # Pre-Pend ARCH variable to USE settings so '-*' in env doesn't kill arch.
                if self.configdict["defaults"].has_key("ARCH"):
@@ -2054,6 +2091,7 @@ class config:
                                if self.configdict["defaults"]["ARCH"] not in usesplit:
                                        usesplit.insert(0,self.configdict["defaults"]["ARCH"])
 
+               usesplit.sort()
                self.configlist[-1]["USE"]= " ".join(usesplit)
 
                self.already_in_regenerate = 0
@@ -3201,9 +3239,14 @@ def doebuild_environment(myebuild, mydo, myroot, mysettings, debug, use_cache, m
                if not eapi_is_supported(eapi):
                        # can't do anything with this.
                        raise portage.exception.UnsupportedAPIException(mycpv, eapi)
-               mysettings["PORTAGE_RESTRICT"] = " ".join(flatten(
-                       portage.dep.use_reduce(portage.dep.paren_reduce(
-                       mysettings["RESTRICT"]), uselist=mysettings["USE"].split())))
+               try:
+                       mysettings["PORTAGE_RESTRICT"] = " ".join(flatten(
+                               portage.dep.use_reduce(portage.dep.paren_reduce(
+                               mysettings.get("RESTRICT","")),
+                               uselist=mysettings.get("USE","").split())))
+               except portage.exception.InvalidDependString:
+                       # RESTRICT is validated again inside doebuild, so let this go
+                       mysettings["PORTAGE_RESTRICT"] = ""
 
        if mysplit[2] == "r0":
                mysettings["PVR"]=mysplit[1]
@@ -4489,7 +4532,10 @@ def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None,
                myusesplit=[]
 
        #convert parenthesis to sublists
-       mysplit = portage.dep.paren_reduce(depstring)
+       try:
+               mysplit = portage.dep.paren_reduce(depstring)
+       except portage.exception.InvalidDependString, e:
+               return [0, str(e)]
 
        mymasks = set()
        useforce = set()
index 31086a5c54e1aa9535e97c13e89b10389f3a4d6c..7fb58748f0b40c0486e84fdf5524fb93b4e4b22e 100644 (file)
@@ -718,10 +718,19 @@ class binarytree(object):
                        from portage.dep import paren_reduce, use_reduce, \
                                paren_normalize, paren_enclose
                        for k in "LICENSE", "RDEPEND", "DEPEND", "PDEPEND", "PROVIDE":
-                               deps = paren_reduce(d[k])
-                               deps = use_reduce(deps, uselist=use)
-                               deps = paren_normalize(deps)
-                               deps = paren_enclose(deps)
+                               try:
+                                       deps = paren_reduce(d[k])
+                                       deps = use_reduce(deps, uselist=use)
+                                       deps = paren_normalize(deps)
+                                       deps = paren_enclose(deps)
+                               except portage.exception.InvalidDependString, e:
+                                       writemsg("%s: %s\n" % (k, str(e)),
+                                               noiselevel=-1)
+                                       del e
+                                       writemsg("!!! Invalid binary package: '%s'\n" % \
+                                               self.getname(cpv), noiselevel=-1)
+                                       self.dbapi.cpv_remove(cpv)
+                                       return
                                if deps:
                                        d[k] = deps
                                else:
index 9a26e038e6ff3c0512929f4bedd8710dbaabce10..0f23ce70e9c3c61c965f163709820535ccf3dd89 100644 (file)
@@ -648,10 +648,10 @@ class portdbapi(dbapi):
 
                accept_keywords = self.mysettings["ACCEPT_KEYWORDS"].split()
                pkgdict = self.mysettings.pkeywordsdict
-               aux_keys = ["KEYWORDS", "LICENSE", "EAPI"]
+               aux_keys = ["KEYWORDS", "LICENSE", "EAPI", "SLOT"]
                for mycpv in mylist:
                        try:
-                               keys, licenses, eapi = self.aux_get(mycpv, aux_keys)
+                               keys, licenses, eapi, slot = self.aux_get(mycpv, aux_keys)
                        except KeyError:
                                continue
                        except PortageException, e:
@@ -666,7 +666,8 @@ class portdbapi(dbapi):
                        match=0
                        cp = dep_getkey(mycpv)
                        if pkgdict.has_key(cp):
-                               matches = match_to_list(mycpv, pkgdict[cp].keys())
+                               cpv_slot = "%s:%s" % (mycpv, slot)
+                               matches = match_to_list(cpv_slot, pkgdict[cp].keys())
                                for atom in matches:
                                        pgroups.extend(pkgdict[cp][atom])
                                if matches:
index 445d0d229103d5eaa8c6fa541c2637dbdcb65863..850b5c43f933ed5ffe6a036dcc1aad80a528f45a 100644 (file)
@@ -996,6 +996,17 @@ class dblink(object):
                before and after this method.
                """
 
+               # When new_contents is supplied, the security check has already been
+               # done for this slot, so it shouldn't be repeated until the next
+               # replacement or unmerge operation.
+               if new_contents is None:
+                       slot = self.vartree.dbapi.aux_get(self.mycpv, ["SLOT"])[0]
+                       slot_matches = self.vartree.dbapi.match(
+                               "%s:%s" % (dep_getkey(self.mycpv), slot))
+                       retval = self._security_check(slot_matches)
+                       if retval:
+                               return retval
+
                contents = self.getcontents()
                # Now, don't assume that the name of the ebuild is the same as the
                # name of the dir; the package may have been moved.
@@ -1091,7 +1102,7 @@ class dblink(object):
                                                del e
                                        unlockdir(catdir_lock)
                env_update(target_root=self.myroot, prev_mtimes=ldpath_mtimes,
-                       contents=contents)
+                       contents=contents, env=self.settings.environ())
                return os.EX_OK
 
        def _unmerge_pkgfiles(self, pkgfiles, new_contents=None):
@@ -1215,12 +1226,9 @@ class dblink(object):
                                                writemsg_stdout("--- !md5   %s %s\n" % ("obj", obj))
                                                continue
                                        try:
-                                               if statobj.st_mode & (stat.S_ISUID | stat.S_ISGID):
-                                                       # Always blind chmod 0 before unlinking to avoid race conditions.
-                                                       os.chmod(obj, 0000)
-                                                       if statobj.st_nlink > 1:
-                                                               writemsg("setXid: "+str(statobj.st_nlink-1)+ \
-                                                                       " hardlinks to '%s'\n" % obj)
+                                               # Remove permissions to ensure that any hardlinks to
+                                               # suid/sgid files are rendered harmless.
+                                               os.chmod(obj, 0)
                                                os.unlink(obj)
                                        except (OSError, IOError), e:
                                                pass
@@ -1458,6 +1466,48 @@ class dblink(object):
                        except OSError:
                                pass
 
+       def _security_check(self, slot_matches):
+               if not slot_matches:
+                       return 0
+               file_paths = set()
+               for cpv in slot_matches:
+                       file_paths.update(dblink(self.cat, catsplit(cpv)[1],
+                               self.vartree.root, self.settings,
+                               vartree=self.vartree).getcontents())
+               inode_map = {}
+               for path in file_paths:
+                       try:
+                               s = os.lstat(path)
+                       except OSError, e:
+                               if e.errno != errno.ENOENT:
+                                       raise
+                               del e
+                               continue
+                       if stat.S_ISREG(s.st_mode) and \
+                               s.st_nlink > 1 and \
+                               s.st_mode & (stat.S_ISUID | stat.S_ISGID):
+                               k = (s.st_dev, s.st_ino)
+                               inode_map.setdefault(k, []).append((path, s))
+               suspicious_hardlinks = []
+               for path_list in inode_map.itervalues():
+                       path, s = path_list[0]
+                       if len(path_list) == s.st_nlink:
+                               # All hardlinks seem to be owned by this package.
+                               continue
+                       suspicious_hardlinks.append(path_list)
+               if not suspicious_hardlinks:
+                       return 0
+               from portage.output import colorize
+               prefix = colorize("SECURITY_WARN", "*") + " WARNING: "
+               writemsg(prefix + "suid/sgid file(s) " + \
+                       "with suspicious hardlink(s):\n", noiselevel=-1)
+               for path_list in suspicious_hardlinks:
+                       for path, s in path_list:
+                               writemsg(prefix + "  '%s'\n" % path, noiselevel=-1)
+               writemsg(prefix + "See the Gentoo Security Handbook " + \
+                       "guide for advice on how to proceed.\n", noiselevel=-1)
+               return 1
+
        def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
                mydbapi=None, prev_mtimes=None):
                """
@@ -1507,6 +1557,10 @@ class dblink(object):
 
                slot_matches = self.vartree.dbapi.match(
                        "%s:%s" % (self.mysplit[0], self.settings["SLOT"]))
+               retval = self._security_check(slot_matches)
+               if retval:
+                       return retval
+
                if slot_matches:
                        # Used by self.isprotected().
                        max_cpv = None
@@ -1692,7 +1746,7 @@ class dblink(object):
                #update environment settings, library paths. DO NOT change symlinks.
                env_update(makelinks=(not downgrade),
                        target_root=self.settings["ROOT"], prev_mtimes=prev_mtimes,
-                       contents=contents)
+                       contents=contents, env=self.settings.environ())
                #dircache may break autoclean because it remembers the -MERGING-pkg file
                global dircache
                if dircache.has_key(self.dbcatdir):
@@ -1777,12 +1831,14 @@ class dblink(object):
                        # handy variables; mydest is the target object on the live filesystems;
                        # mysrc is the source object in the temporary install dir
                        try:
-                               mydmode = os.lstat(mydest).st_mode
+                               mydstat = os.lstat(mydest)
+                               mydmode = mydstat.st_mode
                        except OSError, e:
                                if e.errno != errno.ENOENT:
                                        raise
                                del e
                                #dest file doesn't exist
+                               mydstat = None
                                mydmode = None
 
                        if stat.S_ISLNK(mymode):
@@ -1902,6 +1958,7 @@ class dblink(object):
                                mydestdir = os.path.dirname(mydest)
                                moveme = 1
                                zing = "!!!"
+                               mymtime = None
                                if mydmode != None:
                                        # destination file exists
                                        if stat.S_ISDIR(mydmode):
@@ -1925,9 +1982,11 @@ class dblink(object):
                                                                        """ An identical update has previously been
                                                                        merged.  Skip it unless the user has chosen
                                                                        --noconfmem."""
-                                                                       zing = "-o-"
                                                                        moveme = cfgfiledict["IGNORE"]
                                                                        cfgprot = cfgfiledict["IGNORE"]
+                                                                       if not moveme:
+                                                                               zing = "-o-"
+                                                                               mymtime = long(mystat.st_mtime)
                                                                else:
                                                                        moveme = 1
                                                                        cfgprot = 1
index 34291c006567cf15b4f8bf5323ad28d8b9ab96b5..235829ba27a238c6b7c40b104eead6c1399e88b9 100644 (file)
@@ -89,13 +89,20 @@ def paren_reduce(mystr,tokenize=1):
        """
        mylist = []
        while mystr:
-               if ("(" not in mystr) and (")" not in mystr):
+               left_paren = mystr.find("(")
+               has_left_paren = left_paren != -1
+               right_paren = mystr.find(")")
+               has_right_paren = right_paren != -1
+               if not has_left_paren and not has_right_paren:
                        freesec = mystr
                        subsec = None
                        tail = ""
                elif mystr[0] == ")":
                        return [mylist,mystr[1:]]
-               elif ("(" in mystr) and (mystr.index("(") < mystr.index(")")):
+               elif has_left_paren and not has_right_paren:
+                       raise portage.exception.InvalidDependString(
+                               "missing right parenthesis: '%s'" % mystr)
+               elif has_left_paren and left_paren < right_paren:
                        freesec,subsec = mystr.split("(",1)
                        subsec,tail = paren_reduce(subsec,tokenize)
                else:
index 46fc7738129d2a7468966d0a7146c3063923915e..301832cccab6c8cc3984f2707ab76abeb5f3c78a 100644 (file)
@@ -127,6 +127,7 @@ codes["BRACKET"]    = codes["blue"]
 # Portage functions
 codes["INFORM"] = codes["darkgreen"]
 codes["UNMERGE_WARN"] = codes["red"]
+codes["SECURITY_WARN"] = codes["red"]
 codes["MERGE_LIST_PROGRESS"] = codes["yellow"]
 
 def parse_color_map():
@@ -194,7 +195,18 @@ def xtermTitleReset():
                if prompt_command == "":
                        default_xterm_title = ""
                elif prompt_command is not None:
-                       default_xterm_title = commands.getoutput(prompt_command)
+                       if dotitles and "TERM" in os.environ and sys.stderr.isatty():
+                               from portage.process import find_binary, spawn
+                               shell = os.environ.get("SHELL")
+                               if not shell or not os.access(shell, os.EX_OK):
+                                       shell = find_binary("sh")
+                               if shell:
+                                       spawn([shell, "-c", prompt_command], env=os.environ,
+                                               fdpipes={0:sys.stdin.fileno(),1:sys.stderr.fileno(),
+                                               2:sys.stderr.fileno()})
+                               else:
+                                       os.system(prompt_command)
+                       return
                else:
                        pwd = os.getenv('PWD','')
                        home = os.getenv('HOME', '')