Bug #211949 - As suggested by vapier, tighten the variable filter to also
[portage.git] / bin / portageq
index c3adf9114fff4b00ff82aef80709d0063556d9c6..a29669a243b72cce70efc57773fc03cb777e4c0c 100755 (executable)
@@ -44,7 +44,6 @@ import types
 # and will automaticly add a command by the same name as the function!
 #
 
-
 def has_version(argv):
        """<root> <category/package>
        Return code 0 if it's available, 1 otherwise.
@@ -124,6 +123,94 @@ def metadata(argv):
 
 metadata.uses_root = True
 
+def contents(argv):
+       """<root> <category/package>
+       List the files that are installed for a given package, with
+       one file listed on each line. All file names will begin with
+       <root>.
+       """
+       if len(argv) != 2:
+               print "ERROR: expected 2 parameters, got %d!" % len(argv)
+               return 2
+
+       root, cpv = argv
+       vartree = portage.db[root]["vartree"]
+       if not vartree.dbapi.cpv_exists(cpv):
+               sys.stderr.write("Package not found: '%s'\n" % cpv)
+               return 1
+       cat, pkg = portage.catsplit(cpv)
+       db = portage.dblink(cat, pkg, root, vartree.settings,
+               treetype="vartree", vartree=vartree)
+       file_list = db.getcontents().keys()
+       file_list.sort()
+       for f in file_list:
+               sys.stdout.write("%s\n" % f)
+       sys.stdout.flush()
+contents.uses_root = True
+
+def owners(argv):
+       """<root> [<filename>]+
+       Given a list of files, print the packages that own the files and which
+       files belong to each package. Files owned by a package are listed on
+       the lines below it, indented by a single tab character (\\t). All file
+       paths must start with <root>. Returns 1 if no owners could be found,
+       and 0 otherwise.
+       """
+       if len(argv) < 2:
+               sys.stderr.write("ERROR: insufficient parameters!\n")
+               sys.stderr.flush()
+               return 2
+
+       from portage import catsplit, dblink
+       settings = portage.settings
+       root = settings["ROOT"]
+       vardb = portage.db[root]["vartree"].dbapi
+
+       cwd = None
+       try:
+               cwd = os.getcwd()
+       except OSError:
+               pass
+
+       files = []
+       for f in argv[1:]:
+               f = portage.normalize_path(f)
+               if not f.startswith(os.path.sep):
+                       if cwd is None:
+                               sys.stderr.write("ERROR: cwd does not exist!\n")
+                               sys.stderr.flush()
+                               return 2
+                       f = os.path.join(cwd, f)
+                       f = portage.normalize_path(f)
+               if not f.startswith(root):
+                       sys.stderr.write("ERROR: file paths must begin with <root>!\n")
+                       sys.stderr.flush()
+                       return 2
+               files.append(f[len(root):])
+
+       found_owner = False
+       for cpv in vardb.cpv_all():
+               cat, pkg = catsplit(cpv)
+               mylink = dblink(cat, pkg, root, settings, vartree=vardb.vartree)
+               myfiles = []
+               for f in files:
+                       if mylink.isowner(f, root):
+                               myfiles.append(f)
+               if myfiles:
+                       found_owner = True
+                       sys.stdout.write("%s\n" % cpv)
+                       for f in myfiles:
+                               sys.stdout.write("\t%s\n" % \
+                                       os.path.join(root, f.lstrip(os.path.sep)))
+                       sys.stdout.flush()
+       if not found_owner:
+               sys.stderr.write("None of the installed packages claim the file(s).\n")
+               sys.stderr.flush()
+               return 1
+       return 0
+
+owners.uses_root = True
+
 def best_visible(argv):
        """<root> [<category/package>]+
        Returns category/package-version (without .ebuild).
@@ -133,7 +220,12 @@ def best_visible(argv):
                sys.exit(2)
        try:
                mylist=portage.db[argv[0]]["porttree"].dbapi.match(argv[1])
-               print portage.best(mylist)
+               visible=portage.best(mylist)
+               if visible:
+                       print visible
+                       sys.exit(0)
+               else:
+                       sys.exit(1)
        except KeyError:
                sys.exit(1)
 best_visible.uses_root = True
@@ -171,26 +263,22 @@ all_best_visible.uses_root = True
 
 
 def match(argv):
-       """<root> <category/package>
-       Returns \n seperated list of category/package-version
+       """<root> <atom>
+       Returns a \\n separated list of category/package-version.
+       When given an empty string, all installed packages will
+       be listed.
        """
-       if (len(argv) < 2):
-               print "ERROR: insufficient parameters!"
+       if len(argv) != 2:
+               print "ERROR: expected 2 parameters, got %d!" % len(argv)
                sys.exit(2)
-       try:
-               print "\n".join(portage.db[argv[0]]["vartree"].dbapi.match(argv[1]))
-       except ValueError, e:
-               # Multiple matches thrown from cpv_expand
-               pkgs = e.args[0]
-               # An error has occurred so we writemsg to stderr and exit nonzero.
-               portage.writemsg("The following packages available:\n", noiselevel=-1)
-               for pkg in pkgs:
-                       portage.writemsg("* %s\n" % pkg, noiselevel=-1)
-               portage.writemsg("\nPlease use a more specific atom.\n", noiselevel=-1)
-               sys.exit(1)
-       except KeyError, e:
-               portage.writemsg("%s\n" % str(e), noiselevel=-1)
-               sys.exit(1)
+       root, atom = argv
+       if atom:
+               results = portage.db[root]["vartree"].dbapi.match(atom)
+       else:
+               results = portage.db[root]["vartree"].dbapi.cpv_all()
+               results.sort()
+       for cpv in results:
+               print cpv
 match.uses_root = True
 
 
@@ -289,15 +377,35 @@ def get_repo_path(argv):
        for arg in arvg[1:]:
                print portage.db[argv[0]]["porttree"].dbapi.getRepositoryPath(argv[1])
 
+def list_preserved_libs(argv):
+       """<root>
+       Print a list of libraries preserved during a package update in the form
+       package: path. Returns 0 if no preserved libraries could be found, 
+       1 otherwise.
+       """
+
+       if len(argv) != 1:
+               print "ERROR: wrong number of arguments"
+               sys.exit(2)
+       mylibs = portage.db[argv[0]]["vartree"].dbapi.plib_registry.getPreservedLibs()
+       rValue = 0
+       for cpv in mylibs:
+               print cpv,
+               for path in mylibs[cpv]:
+                       print path,
+                       rValue = 1
+               print
+       return rValue
+list_preserved_libs.uses_root = True
+
 #-----------------------------------------------------------------------------
 #
 # DO NOT CHANGE CODE BEYOND THIS POINT - IT'S NOT NEEDED!
 #
 
 def usage(argv):
-       rev="$Revision: 1.13.2.1 $"
-       ver= rev.split(' ')[1]
-       print ">>> Portage information query tool -- version "+ver
+       print ">>> Portage information query tool"
+       print ">>> $Id$"
        print ">>> Usage: portageq <command> [<option> ...]"
        print ""
        print "Available commands:"
@@ -306,11 +414,12 @@ def usage(argv):
        # Show our commands -- we do this by scanning the functions in this
        # file, and formatting each functions documentation.
        #
-       for name in globals().keys():
-               # Drop python stuff, modules, and our own support functions.
-               if (name in ("usage", "__doc__", "__name__", "main", "os", "portage", "sys", "__builtins__", "types", "string","exithandler")):
-                       continue
+       commands = [x for x in globals() if x not in \
+                               ("usage", "__doc__", "__name__", "main", "os", "portage", \
+                               "sys", "__builtins__", "types", "string","exithandler")]
+       commands.sort()
 
+       for name in commands:
                # Drop non-functions
                obj = globals()[name]
                if  (type(obj) != types.FunctionType):
@@ -342,23 +451,61 @@ def main():
                sys.exit(os.EX_USAGE)
 
        cmd = sys.argv[1]
+       function = globals().get(cmd)
+       if function is None:
+               usage(sys.argv)
+               sys.exit(os.EX_USAGE)
+       function = globals()[cmd]
+       uses_root = getattr(function, "uses_root", False) and len(sys.argv) > 2
+       if uses_root:
+               if not os.path.isdir(sys.argv[2]):
+                       sys.stderr.write("Not a directory: '%s'\n" % sys.argv[2])
+                       sys.stderr.write("Run portageq with --help for info\n")
+                       sys.stderr.flush()
+                       sys.exit(os.EX_USAGE)
+               os.environ["ROOT"] = sys.argv[2]
+
+       global portage
+
+       # First import the main portage module without legacy globals since it
+       # is almost certain to succeed in that case. This provides access to
+       # the portage.exception namespace which is needed for later exception
+       # handling, like if portage.exception.PermissionDenied is raised when
+       # constructing the legacy global config instance.
+       os.environ["PORTAGE_LEGACY_GLOBALS"] = "false"
+       import portage
+       del os.environ["PORTAGE_LEGACY_GLOBALS"]
        try:
-               function = globals()[cmd]
-               uses_root = (getattr(function, "uses_root", False) and len(sys.argv) > 2)
-               if uses_root:
-                       os.environ["ROOT"] = sys.argv[2]
-               global portage
                try:
-                       import portage
+                       reload(portage)
                except ImportError:
-                       sys.path.insert(0, "/usr/lib/portage/pym")
+                       from os import path as osp
+                       sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
                        import portage
                if uses_root:
                        sys.argv[2] = portage.root
-               function(sys.argv[2:])
-       except KeyError:
-               usage(sys.argv)
-               sys.exit(os.EX_USAGE)
+               retval = function(sys.argv[2:])
+               if retval:
+                       sys.exit(retval)
+       except portage.exception.PermissionDenied, e:
+               sys.stderr.write("Permission denied: '%s'\n" % str(e))
+               sys.exit(e.errno)
+       except portage.exception.ParseError, e:
+               sys.stderr.write("%s\n" % str(e))
+               sys.exit(1)
+       except ValueError, e:
+               if not e.args or \
+                       not hasattr(e.args[0], "__len__") or \
+                       len(e.args[0]) < 2:
+                       raise
+               # Multiple matches thrown from cpv_expand
+               pkgs = e.args[0]
+               # An error has occurred so we writemsg to stderr and exit nonzero.
+               portage.writemsg("The following packages available:\n", noiselevel=-1)
+               for pkg in pkgs:
+                       portage.writemsg("* %s\n" % pkg, noiselevel=-1)
+               portage.writemsg("\nPlease use a more specific atom.\n", noiselevel=-1)
+               sys.exit(1)
 
 main()