diff options
Diffstat (limited to 'src/equery/equery')
-rwxr-xr-x | src/equery/equery | 1846 |
1 files changed, 1846 insertions, 0 deletions
diff --git a/src/equery/equery b/src/equery/equery new file mode 100755 index 0000000..63eb8b3 --- /dev/null +++ b/src/equery/equery @@ -0,0 +1,1846 @@ +#!/usr/bin/python +# +# Copyright 2003-2004 Karl Trygve Kalleberg +# Copyright 2003-2004 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 +# +# $Header$ +# Author: Karl Trygve Kalleberg <karltk@gentoo.org> + +__author__ = "Karl Trygve Kalleberg" +__email__ = "karltk@gentoo.org" +__version__ = "0.1.4" +__productname__ = "equery" +__description__ = "Gentoo Package Query Tool" + +import os +import re +import sys +import time + +# portage (output module) and gentoolkit need special path modifications +sys.path.insert(0, "/usr/lib/gentoolkit/pym") + +import gentoolkit +try: + import portage +except ImportError: + sys.path.insert(0, "/usr/lib/portage/pym") + import portage + +try: + import portage.checksum as checksum + from portage.util import unique_array +except ImportError: + import portage_checksum as checksum + from portage_util import unique_array + +import gentoolkit.pprinter as pp +from gentoolkit.pprinter import print_info, print_error, print_warn, die + +# Auxiliary functions + +def fileAsStr(name, fdesc, showType=0, showMD5=0, showTimestamp=0): + """ + Return file in fdesc as a filename + @param name: + @param fdesc: + @param showType: + @param showMD5: + @param showTimestamp: + @rtype: string + """ + type = ""; fname = ""; stamp = ""; md5sum = "" + + if fdesc[0] == 'obj': + type = "file" + fname = name + stamp = timestampAsStr(int(fdesc[1])) + md5sum = fdesc[2] + elif fdesc[0] == "dir": + type = "dir" + fname = pp.path(name) + elif fdesc[0] == "sym": + type = "symlink" + stamp = timestampAsStr(int(fdesc[1].replace(")",""))) + tgt = fdesc[2].split()[0] + if Config["piping"]: + fname = name + else: + fname = pp.path_symlink(name + " -> " + tgt) + elif fdesc[0] == "fif": + type = "fifo" + fname = name + elif fdesc[0] == "dev": + type = "device" + fname = name + else: + raise Exception(name + " has unknown type: " + fdesc[0]) + + s = "" + if showType: + s += "%6s " % type + s += fname + if showTimestamp: + s += " " + stamp + " " + if showMD5: + s += " " + md5sum + " " + return s + +def timestampAsStr(timestamp): + return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp)) + + +class Command: + """ + Abstract root class for all equery commands + """ + def __init__(self): + pass + def shortHelp(self): + """Return a help formatted to fit a single line, approx 70 characters. + Must be overridden in the subclass.""" + return " - not implemented yet" + def longHelp(self): + """Return full, multiline, color-formatted help. + Must be overridden in the subclass.""" + return "help for syntax and options" + def perform(self, args): + """Stub code for performing the command. + Must be overridden in the subclass""" + pass + def parseArgs(self, args): + """Stub code for parsing command line arguments for this command. + Must be overridden in the subclass.""" + pass + + +class CmdListFiles(Command): + """List files owned by a particular package""" + def __init__(self): + self.default_options = { + "showType": 0, + "showTimestamp": 0, + "showMD5": 0, + "tree": 0, + "filter": None + } + + def parseArgs(self,args): + query = "" + need_help = 0 + opts = self.default_options + for x in args: + if x in ["-h", "--help"]: + need_help = 1 + elif x in ["--md5sum"]: + opts["showMD5"] = 1 + elif x in ["--timestamp"]: + opts["showTimestamp"] = 1 + elif x in ["--type"]: + opts["showType"] = 1 + elif x in ["--tree"]: + opts["tree"] = 1 + elif x[:9] == "--filter=": + opts["filter"] = x[9:].split(',') + elif x[0] == "/": + die(2, "The query '" + pp.pkgquery(x) + "' does not appear to be a valid package specification") + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + + if need_help or query == "": + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def filterContents(self, cnt, filter): + if filter in [None,[]]: + return cnt + + mycnt = {} + + for mytype in filter: + # Filter elements by type (as recorded in CONTENTS). + if mytype in ["dir","obj","sym","dev","fif"]: + for mykey in cnt.keys(): + if cnt[mykey][0] == mytype: + mycnt[mykey] = cnt[mykey] + + if "cmd" in filter: + # List files that are in $PATH. + userpath = map(os.path.normpath,os.environ["PATH"].split(os.pathsep)) + for mykey in cnt.keys(): + if cnt[mykey][0] in ['obj','sym'] \ + and os.path.dirname(mykey) in userpath: + mycnt[mykey] = cnt[mykey] + + if "path" in filter: + # List only dirs where some files where actually installed, + # and also skip their subdirs. + mykeys = cnt.keys() + mykeys.sort() + while len(mykeys): + mykey = mykeys.pop(0) + if cnt[mykey][0] == 'dir': + i = 0 + while i < len(mykeys) : + if cnt[mykeys[i]][0] != "dir" \ + and os.path.dirname(mykeys[i]) == mykey: + mycnt[mykey] = cnt[mykey] + break + i += 1 + if i < len(mykeys): + while len(mykeys) \ + and len(mykey+"/") < len(mykeys[0]) \ + and mykey+"/" == mykeys[0][:len(mykey)+1]: + mykeys.pop(0) + + if "conf" in filter: + # List configuration files. + conf_path = gentoolkit.settings["CONFIG_PROTECT"].split() + conf_mask_path = gentoolkit.settings["CONFIG_PROTECT_MASK"].split() + conf_path = map(os.path.normpath, conf_path) + conf_mask_path = map(os.path.normpath, conf_mask_path) + for mykey in cnt.keys(): + is_conffile = False + if cnt[mykey][0] == 'obj': + for conf_dir in conf_path: + if conf_dir+"/" == mykey[:len(conf_dir)+1]: + is_conffile = True + for conf_mask_dir in conf_mask_path: + if conf_mask_dir+"/" == mykey[:len(conf_mask_dir)+1]: + is_conffile = False + break + break + if is_conffile: + mycnt[mykey] = cnt[mykey] + + + for mydoctype in ["doc","man","info"]: + # List only files from /usr/share/{doc,man,info} + mydocpath = "/usr/share/"+mydoctype+"/" + if mydoctype in filter: + for mykey in cnt.keys(): + if cnt[mykey][0] == 'obj' \ + and mykey[:len(mydocpath)] == mydocpath : + mycnt[mykey] = cnt[mykey] + + return mycnt + + def perform(self, args): + + (query, opts) = self.parseArgs(args) + + # Turn off filtering for tree output + if opts["tree"]: + opts["filter"] = None + + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]") + + pkgs = gentoolkit.find_installed_packages(query, True) + for x in pkgs: + if not x.is_installed(): + continue + + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(1, pp.section("* ") + "Contents of " + pp.cpv(x.get_cpv()) + ":") + + cnt = self.filterContents(x.get_contents(),opts["filter"]) + + filenames = cnt.keys() + filenames.sort() + + last=[] + for name in filenames: + if not opts["tree"]: + print_info(0, fileAsStr(name, + cnt[name], + showType=opts["showType"], + showTimestamp=opts["showTimestamp"], + showMD5=opts["showMD5"])) + else: + c = name.split( "/" )[1:] + if cnt[name][0] == "dir": + if len(last) == 0: + last = c + print pp.path(" /" + c[0]) + continue + numol = 0 + for d in c: + if d in last: + numol = last.index(d) + 1 + continue + last = c + if len(last) == 1: + print pp.path(" " + last[0]) + continue + print pp.path(" " * ( numol * 3 ) + "> " + "/" + last[-1]) + elif cnt[name][0] == "sym": + print pp.path(" " * ( bl * 3 ) + "+ ") + pp.path_symlink(c[-1] + " -> " + cnt[name][2]) + else: + bl = len(last) + print pp.path(" " * ( bl * 3 ) + "+ ") + c[-1] + + def longHelp(self): + return "List files owned by a particular package\n" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("files") + pp.localoption(" <local-opts> ") + pp.pkgquery("<cat/>packagename<-version>") + "\n" + \ + "\n" + \ + "Note: category and version parts are optional. \n" + \ + "\n" + \ + pp.localoption("<local-opts>") + " is either of: \n" + \ + " " + pp.localoption("--timestamp") + " - append timestamp\n" + \ + " " + pp.localoption("--md5sum") + " - append md5sum\n" + \ + " " + pp.localoption("--type") + " - prepend file type\n" + \ + " " + pp.localoption("--tree") + " - display results in a tree (turns off other options)\n" + \ + " " + pp.localoption("--filter=<rules>") + " - filter output\n" + \ + " " + pp.localoption("<rules>") + " is a comma separated list of elements you want to see:\n" + \ + " " + " " + pp.localoption("dir") + \ + ", " + pp.localoption("obj") + \ + ", " + pp.localoption("sym") + \ + ", " + pp.localoption("dev") + \ + ", " + pp.localoption("fifo") + \ + ", " + pp.localoption("path") + \ + ", " + pp.localoption("conf") + \ + ", " + pp.localoption("cmd") + \ + ", " + pp.localoption("doc") + \ + ", " + pp.localoption("man") + \ + ", " + pp.localoption("info") + + def shortHelp(self): + return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list files owned by " + pp.pkgquery("pkgspec") + + +class CmdListBelongs(Command): + """List all packages owning a particular file""" + def __init__(self): + self.default_opts = { + "category": "*", + "fullRegex": 0, + "earlyOut": 0, + "nameOnly": 0 + } + + def parseArgs(self, args): + + query = [] + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x in ["-c", "--category"]: + opts["category"] = args[i+1] + skip = 1 + elif x in ["-e", "--earlyout"]: + opts["earlyOut"] = 1 + elif x in ["-f", "--full-regex"]: + opts["fullRegex"] = 1 + elif x in ["-n", "--name-only"]: + opts["nameOnly"] = 1 + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query.append(x) + + if need_help or query == []: + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def perform(self, args): + (query, opts) = self.parseArgs(args) + + if opts["fullRegex"]: + q = query + else: + # Trim trailing and multiple slashes from query + for i in range(0, len(query)): + query[i] = re.compile('/+').sub('/', query[i]) + query[i] = query[i].rstrip('/') + q = map(lambda x: ((len(x) and x[0] == "/") and "^" or "/") + + re.escape(x) + "$", query) + print q + try: + q = "|".join(q) + rx = re.compile(q) + except: + die(2, "The query '" + pp.regexpquery(q) + "' does not appear to be a valid regular expression") + + # Pick out only selected categories + cat = opts["category"] + filter_fn = None + if cat != "*": + filter_fn = lambda x: x.find(cat+"/")==0 + + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(3, "[ Searching for file(s) " + pp.regexpquery(",".join(query)) + " in " + pp.cpv(cat) + "... ]") + + matches = portage.db["/"]["vartree"].dbapi.cpv_all() + #matches = gentoolkit.find_all_installed_packages(filter_fn) + + found = 0 + + def dumpToPipe(pkg): + mysplit = pkg.split("/") + cnt = portage.dblink(mysplit[0], mysplit[1], "/", gentoolkit.settings).getcontents() + #cnt = pkg.get_contents() + if not cnt: return + for file in cnt.keys(): + if rx.search(file) and (opts["category"] == "*" or portage.catpkgsplit(pkg)[0] == opts["category"]): + if opts["nameOnly"]: + x = portage.catpkgsplit(pkg) + print x[0]+"/"+x[1] + else: + print pkg + return + + class DummyExp: + pass + + def dumpToScreen(pkg): + mysplit = pkg.split("/") + cnt = portage.dblink(mysplit[0], mysplit[1], "/", gentoolkit.settings).getcontents() + #cnt = pkg.get_contents() + if not cnt: return + for file in cnt.keys(): + if rx.search(file) and (opts["category"] == "*" or portage.catpkgsplit(pkg)[0] == opts["category"]): + if opts["nameOnly"]: + x = portage.catpkgsplit(pkg) + s = x[0]+"/"+x[1] + else: + s = pkg + s += " (" + pp.path(fileAsStr(file, cnt[file])) + ")" + print_info(0, s) + if opts["earlyOut"]: + raise DummyExp + + try: + if Config["piping"]: + map(dumpToPipe, matches) + else: + map(dumpToScreen, matches) + except DummyExp: + pass + + def shortHelp(self): + return pp.localoption("<local-opts> ") + pp.path("files...") + " - list all packages owning " + pp.path("files...") + def longHelp(self): + return "List all packages owning a particular set of files" + \ + "\n" + \ + "\n" + \ + pp.emph("Note: ") + "Normally, only one package will own a file. If multiple packages own the same file, it usually consitutes a problem, and should be reported.\n" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("belongs") + pp.localoption(" <local-opts> ") + pp.path("filename") + \ + "\n" + \ + pp.localoption("<local-opts>") + " is either of: \n" + \ + " " + pp.localoption("-c, --category cat") + " - only search in category " + \ + pp.pkgquery("cat") + "\n" + \ + " " + pp.localoption("-f, --full-regex") + " - supplied query is a regex\n" + \ + " " + pp.localoption("-e, --earlyout") + " - stop when first match is found\n" + \ + " " + pp.localoption("-n, --name-only") + " - don't print the version." + +class CmdDisplayUSEs(Command): + """Advanced report of a package's USE flags""" + def __init__(self): + self.default_opts = { + "allPackages" : False + } + def parseArgs(self, args): + + query = "" + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x in ["-a", "--all"]: + opts["allPackages"] = True + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + + if need_help or query == "": + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def perform(self, args): + + (query, opts) = self.parseArgs(args) + + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]") + + if not opts["allPackages"]: + matches = gentoolkit.find_installed_packages(query, True) + if not matches: + matches = gentoolkit.find_packages(query, False) + if matches: + matches = gentoolkit.sort_package_list(matches) + matches = matches[-1:] + else: + matches = gentoolkit.find_packages(query, True) + + if not matches: + die(3, "No matching packages found for \"" + pp.pkgquery(query) + "\"") + + + useflags = gentoolkit.settings["USE"].split() + usedesc = {} + uselocaldesc = {} + + # Load global USE flag descriptions + try: + fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.desc") + usedesc = {} + for line in fd.readlines(): + if line[0] == "#": + continue + fields = line.split(" - ", 1) + if len(fields) == 2: + usedesc[fields[0].strip()] = fields[1].strip() + except IOError: + print_warn(5, "Could not load USE flag descriptions from " + ppath(gentoolkit.settings["PORTDIR"] + "/profiles/use.desc")) + + # Load local USE flag descriptions + try: + fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.local.desc") + for line in fd.readlines(): + if line[0] == "#": + continue + fields = line.split(" - ", 1) + if len(fields) == 2: + catpkguse = re.search("(.*):(.*)", fields[0]) + if catpkguse: + if not uselocaldesc.has_key(catpkguse.group(1).strip()): + uselocaldesc[catpkguse.group(1).strip()] = {catpkguse.group(2).strip() : fields[1].strip()} + else: + uselocaldesc[catpkguse.group(1).strip()][catpkguse.group(2).strip()] = fields[1].strip() + except IOError: + print_warn(5, "Could not load USE flag descriptions from " + path(gentoolkit.settings["PORTDIR"] + "/profiles/use.local.desc")) + + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(3, "[ Colour Code : " + pp.useflagon("set") + " " + pp.useflagoff("unset") + " ]") + print_info(3, "[ Legend : Left column (U) - USE flags from make.conf ]") + print_info(3, "[ : Right column (I) - USE flags packages was installed with ]") + + # Iterate through matches, printing a report for each package + matches = gentoolkit.sort_package_list(matches) + matches_found = 0 + for p in matches: + + matches_found += 1 + + bestver = p.get_cpv() + iuse = p.get_env_var("IUSE") + + if iuse: + # Fix Bug #91623 by making sure the list of USE flags is unique + # Added sort to make output prettier + usevar = unique_array(iuse.split()) + + # Remove prefixed +/- from flags in IUSE, Bug #232019 + for i in range(len(usevar)): + if usevar[i][0] == "+" or usevar[i][0] == "-": + usevar[i] = usevar[i][1:] + + usevar.sort() + else: + usevar = [] + + inuse = [] + if p.is_installed(): + used = p.get_use_flags().split() + else: + # cosmetic issue here as noninstalled packages don't have "used" flags + used = useflags + + # store (inuse, inused, flag, desc) + output = [] + + for u in usevar: + inuse = 0 + inused = 0 + try: + desc = usedesc[u] + except KeyError: + try: + desc = uselocaldesc[p.get_category() + "/" + p.get_name()][u] + except KeyError: + desc = "" + + if u in p.get_settings("USE").split(): + inuse = 1 + if u in used: + inused = 1 + + output.append((inuse, inused, u, desc)) + + # pretty print + if output: + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(0, "[ Found these USE variables for " + pp.cpv(bestver) + " ]") + print_info(3, pp.emph(" U I")) + maxflag_len = 0 + for inuse, inused, u, desc in output: + if len(u) > maxflag_len: + maxflag_len = len(u) + + for in_makeconf, in_installed, flag, desc in output: + markers = ["-","+"] + colour = [pp.useflagoff, pp.useflagon] + if Config["piping"]: + print_info(0, markers[in_makeconf] + flag) + else: + if in_makeconf != in_installed: + print_info(0, pp.emph(" %s %s" % (markers[in_makeconf], markers[in_installed])), False) + else: + print_info(0, " %s %s" % (markers[in_makeconf], markers[in_installed]), False) + + print_info(0, " " + colour[in_makeconf](flag.ljust(maxflag_len)), False) + + # print description + if desc: + print_info(0, " : " + desc) + else: + print_info(0, " : <unknown>") + else: + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(1, "[ No USE flags found for " + pp.cpv(p.get_cpv()) + "]") + + if Config["verbosityLevel"] >= 2: + if matches_found == 0: + s = "" + die(3, "No " + s + "packages found for " + pp.pkgquery(query)) + + + def shortHelp(self): + return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - display USE flags for " + pp.pkgquery("pkgspec") + def longHelp(self): + return "Display USE flags for a given package\n" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("uses") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \ + "\n" + \ + pp.localoption("<local-opts>") + " is: \n" + \ + " " + pp.localoption("-a, --all") + " - include all package versions\n" + + +class CmdDisplayDepGraph(Command): + """Display tree graph of dependencies for a query""" + + def __init__(self): + self.default_opts = { + "displayUSEFlags": 1, + "fancyFormatting": 1, + "depth": 0 + } + + def parseArgs(self, args): + + query = "" + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x in ["-U","--no-useflags"]: + opts["displayUSEFlags"] = 0 + elif x in ["-l","--linear"]: + opts["fancyFormatting"] = 0 + elif x[:8] == "--depth=": + opts["depth"] = int(x[8:]) + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + + if need_help or query == "": + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def perform(self, args): + (query, opts) = self.parseArgs(args) + + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]") + + matches = gentoolkit.find_packages(query, True) + + for pkg in matches: + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(3, pp.section("* ") + "dependency graph for " + pp.cpv(pkg.get_cpv())) + else: + print_info(0, pkg.get_cpv() + ":") + + stats = { "maxdepth": 0, "packages": 0 } + self._graph(pkg, opts, stats, 0, [], "") + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(0, "[ " + pp.cpv(pkg.get_cpv()) + " stats: packages (" + pp.number(str(stats["packages"])) + \ + "), max depth (" + pp.number(str(stats["maxdepth"])) + ") ]") + + def _graph(self, pkg, opts, stats, level=0, pkgtbl=[], suffix=""): + + stats["packages"] += 1 + stats["maxdepth"] = max(stats["maxdepth"], level) + + cpv = pkg.get_cpv() + + pfx = "" + if opts["fancyFormatting"]: + pfx = level * " " + "`-- " + print_info(0, pfx + cpv + suffix) + + pkgtbl.append(cpv) + + pkgdeps = pkg.get_runtime_deps() + pkg.get_compiletime_deps() + pkg.get_postmerge_deps() + for x in pkgdeps: + suffix = "" + cpv = x[2] + pkg = gentoolkit.find_best_match(x[0] + cpv) + if not pkg: + continue + if pkg.get_cpv() in pkgtbl: + continue + if cpv.find("virtual") == 0: + suffix += " (" + pp.cpv(cpv) + ")" + if len(x[1]) and opts["displayUSEFlags"]: + suffix += " [ " + pp.useflagon(' '.join(x[1])) + " ]" + if (level < opts["depth"] or opts["depth"] <= 0): + pkgtbl = self._graph(pkg, opts, stats, level+1, pkgtbl, suffix) + return pkgtbl + + def shortHelp(self): + return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - display a dependency tree for " + pp.pkgquery("pkgspec") + def longHelp(self): + return "Display a dependency tree for a given package\n" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("depgraph") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \ + "\n" + \ + pp.localoption("<local-opts>") + " is either of: \n" + \ + " " + pp.localoption("-U, --no-useflags") + " - do not show USE flags\n" + \ + " " + pp.localoption("-l, --linear") + " - do not use fancy formatting\n" + \ + " " + pp.localoption(" --depth=n") + " - limit dependency graph to specified depth" + + +class CmdDisplaySize(Command): + """Display disk size consumed by a package""" + def __init__(self): + self.default_opts = { + "regex": 0, + "exact": 0, + "reportSizeInBytes": 0 + } + + def parseArgs(self, args): + + query = "" + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x in ["-b","--bytes"]: + opts["reportSizeInBytes"] = 1 + elif x in ["-f", "--full-regex"]: + opts["regex"] = 1 + elif x in ["-e", "--exact-name"]: + opts["exact"] = 1 + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + +# if need_help or query == "": + if need_help: + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def perform(self, args): + (query, opts) = self.parseArgs(args) + + rev = "" + name = "" + ver = "" + cat = "" + + if query != "": + (cat, name, ver, rev) = gentoolkit.split_package_name(query) + if rev == "r0": rev = "" + + # replace empty strings with .* and escape regular expression syntax + if query != "": + if not opts["regex"]: + cat, name, ver, rev = [re.sub('^$', ".*", re.escape(x)) for x in cat, name, ver, rev] + else: + cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev] + + try: + if opts["exact"]: + filter_fn = lambda x: re.match(cat+"/"+name, x) + else: + filter_fn = lambda x: re.match(cat+"/.*"+name, x) + matches = gentoolkit.find_all_installed_packages(filter_fn) + except: + die(2, "The query '" + pp.regexpquery(query) + "' does not appear to be a valid regular expression") + else: + cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev] + matches = gentoolkit.find_all_installed_packages() + + matches = gentoolkit.sort_package_list(matches) + + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]") + + # If no version supplied, fix regular expression + if ver == ".*": ver = "[0-9]+[^-]*" + + if rev != ".*": # revision supplied + ver = ver + "-" + rev + + if opts["exact"]: + rx = re.compile(cat + "/" + name + "-" + ver) + else: + rx = re.compile(cat + "/.*" + name + ".*-" + ver) + + for pkg in matches: + if rx.search(pkg.get_cpv()): + (size, files, uncounted) = pkg.size() + + if Config["piping"]: + print_info(0, pkg.get_cpv() + ": total(" + str(files) + "), inaccessible(" + str(uncounted) + \ + "), size(" + str(size) + ")") + else: + print_info(0, pp.section("* ") + "size of " + pp.cpv(pkg.get_cpv())) + print_info(0, " Total files : ".rjust(25) + pp.number(str(files))) + + if uncounted: + print_info(0, " Inaccessible files : ".rjust(25) + pp.number(str(uncounted))) + + sz = "%.2f KiB" % (size/1024.0) + if opts["reportSizeInBytes"]: + sz = pp.number(str(size)) + " bytes" + + print_info(0, "Total size : ".rjust(25) + pp.number(sz)) + + + def shortHelp(self): + return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - print size of files contained in package " + pp.pkgquery("pkgspec") + def longHelp(self): + return "Print size total size of files contained in a given package" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("size") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \ + "\n" + \ + pp.localoption("<local-opts>") + " is: \n" + \ + " " + pp.localoption("-b, --bytes") + " - report size in bytes\n" \ + " " + pp.localoption("-f, --full-regex") + " - query is a regular expression\n" + \ + " " + pp.localoption("-e, --exact-name") + " - list only those packages that exactly match\n" + +class CmdDisplayChanges(Command): + """Display changes for pkgQuery""" + pass + +class CheckException: + def __init__(self, s): + self.s = s + +class CmdCheckIntegrity(Command): + """Check timestamps and md5sums for files owned by pkgspec""" + def __init__(self): + self.default_opts = { + "showSummary" : 1, + "showGoodFiles" : 0, + "showBadFiles" : 1, + "checkTimestamp" : 1, + "checkMD5sum": 1 + } + + def parseArgs(self, args): + + query = "" + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + + if need_help: + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def getMD5sum(self, file): + return checksum.perform_md5(file, calc_prelink=1) + + def perform(self, args): + (query, opts) = self.parseArgs(args) + + if query == "": + matches=gentoolkit.find_all_installed_packages() + else: + matches = gentoolkit.find_packages(query, True) + + matches = gentoolkit.sort_package_list(matches) + + for pkg in matches: + if not pkg.is_installed(): + continue + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(1, "[ Checking " + pp.cpv(pkg.get_cpv()) + " ]") + else: + print_info(0, pkg.get_cpv() + ":") + + files = pkg.get_contents() + checked_files = 0 + good_files = 0 + for file in files.keys(): + type = files[file][0] + try: + st = os.lstat(file) + if type == "dir": + if not os.path.isdir(file): + raise CheckException(file + " exists, but is not a directory") + elif type == "obj": + mtime = files[file][1] + md5sum = files[file][2] + if opts["checkMD5sum"]: + try: + actual_checksum = self.getMD5sum(file) + except: + raise CheckException("Failed to calculate MD5 sum for " + file) + + if self.getMD5sum(file) != md5sum: + raise CheckException(file + " has incorrect md5sum") + if opts["checkTimestamp"]: + if int(st.st_mtime) != int(mtime): + raise CheckException(file + (" has wrong mtime (is %d, should be %s)" % (st.st_mtime, mtime))) + elif type == "sym": + # FIXME: nastry strippery; portage should have this fixed! + t = files[file][2] + # target = os.path.normpath(t.strip()) + target = t.strip() + if not os.path.islink(file): + raise CheckException(file + " exists, but is not a symlink") + tgt = os.readlink(file) + if tgt != target: + raise CheckException(file + " does not point to " + target) + elif type == "fif": + pass + else: + pp.print_error(file) + pp.print_error(files[file]) + pp.print_error(type) + raise CheckException(file + " has unknown type " + type) + good_files += 1 + except CheckException, (e): + print_error(e.s) + except OSError: + print_error(file + " does not exist") + checked_files += 1 + print_info(0, pp.section(" * ") + pp.number(str(good_files)) + " out of " + pp.number(str(checked_files)) + " files good") + + def shortHelp(self): + return pp.pkgquery("pkgspec") + " - check MD5sums and timestamps of " + pp.pkgquery("pkgspec") + "'s files" + def longHelp(self): + return "Check package's files against recorded MD5 sums and timestamps" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("check") + pp.pkgquery(" pkgspec") + +class CmdDisplayStatistics(Command): + """Display statistics about installed and uninstalled packages""" + pass + +class CmdWhich(Command): + """Display the filename of the ebuild for a given package + that would be used by Portage with the current configuration.""" + def __init__(self): + self.default_opts = { + "includeMasked": False + } + + def parseArgs(self, args): + + query = "" + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x in ["-m", "--include-masked"]: + opts["includeMasked"] = True + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + + if need_help or query == "": + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def perform(self, args): + (query, opts) = self.parseArgs(args) + + matches = gentoolkit.find_packages(query, opts["includeMasked"]) + matches = gentoolkit.sort_package_list(matches) + + if matches: + pkg = matches[-1] + ebuild_path = pkg.get_ebuild_path() + if ebuild_path: + print_info(0, os.path.normpath(ebuild_path)) + else: + print_warn("There are no ebuilds to satisfy %s" % pkg.get_name()) + else: + print_error("No masked or unmasked packages found for " + pp.pkgquery(query)) + + def shortHelp(self): + return pp.pkgquery("pkgspec") + " - print full path to ebuild for package " + pp.pkgquery("pkgspec") + def longHelp(self): + # Not documenting --include-masked at this time, since I'm not sure that it is needed. - FuzzyRay + return "Print full path to ebuild for a given package" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("which ") + pp.pkgquery("pkgspec") + +class CmdListGLSAs(Command): + """List outstanding GLSAs.""" + pass + +class CmdListDepends(Command): + """List all packages directly or indirectly depending on pkgQuery""" + def __init__(self): + self.default_opts = { + "onlyDirect": 1, + "onlyInstalled": 1, + "spacing": 0, + "depth": -1 + } + # Used to cache and detect looping + self.pkgseen = [] + self.pkglist = [] + self.pkgdeps = {} + self.deppkgs = {} + + def parseArgs(self, args): + + query = "" + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x in ["-d", "--direct"]: + opts["onlyDirect"] = 1 + elif x in ["-D", "--indirect"]: + opts["onlyDirect"] = 0 + elif x in ["-a", "--all-packages"]: + opts["onlyInstalled"] = 0 + elif x[:10] == "--spacing=": + opts["spacing"] = int(x[10:]) + elif x[:8] == "--depth=": + opts["depth"] = int(x[8:]) + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + + if need_help or query == "": + print self.longHelp() + sys.exit(-1) + return (query, opts) + + def perform(self, args): + + (query, opts) = self.parseArgs(args) + + # We call ourself recursively if --indirect specified. spacing is used to control printing the tree. + spacing = opts["spacing"] + + if not Config["piping"] and Config["verbosityLevel"] >= 3 and not spacing: + print_info(3, "[ Searching for packages depending on " + pp.pkgquery(query) + "... ]") + + isdepend = gentoolkit.split_package_name(query) + isdepends = map((lambda x: x.get_cpv()), gentoolkit.find_packages(query)) + if not isdepends: + print_warn("Warning: No packages found matching %s" % query) + + # Cache the list of packages + if not self.pkglist: + if opts["onlyInstalled"]: + packages = gentoolkit.find_all_installed_packages() + else: + packages = gentoolkit.find_all_packages() + + packages = gentoolkit.sort_package_list(packages) + self.pkglist = packages + else: + packages = self.pkglist + + for pkg in packages: + pkgcpv = pkg.get_cpv() + if not pkgcpv in self.pkgdeps: + try: + deps = pkg.get_runtime_deps() + pkg.get_compiletime_deps() + pkg.get_postmerge_deps() + except KeyError, e: + # If the ebuild is not found... + continue + # Remove duplicate deps + deps = unique_array(deps) + self.pkgdeps[pkgcpv] = deps + else: + deps = self.pkgdeps[pkgcpv] + isdep = 0 + for dependency in deps: + # TODO determine if dependency is enabled by USE flag + # Find all packages matching the dependency + depstr = dependency[0]+dependency[2] + if not depstr in self.deppkgs: + try: + depcpvs = map((lambda x: x.get_cpv()), gentoolkit.find_packages(depstr)) + self.deppkgs[depstr] = depcpvs + except KeyError, e: + print_warn("") + print_warn("Package: " + pkgcpv + " contains invalid dependency specification.") + print_warn("Portage error: " + str(e)) + print_warn("") + continue + else: + depcpvs = self.deppkgs[depstr] + for x in depcpvs: + cpvs=gentoolkit.split_package_name(x) + if x in isdepends: + cat_match=1 + name_match=1 + ver_match=1 + else: + cat_match=0 + name_match=0 + ver_match=0 + # Match Category + if not isdepend[0] or cpvs[0] == isdepend[0]: + cat_match=1 + # Match Name + if cpvs[1] == isdepend[1]: + name_match=1 + # Match Version + if not isdepend[2] or ( cpvs[2] == isdepend[2] and (isdepend[3] \ + or isdepend[3] == "r0" or cpvs[3] == isdepend[3])): + ver_match=1 + + if cat_match and ver_match and name_match: + if not isdep: + if dependency[1]: + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print " " * (spacing * 2) + pp.cpv(pkg.get_cpv()), + print "(" + \ + pp.useflag(" & ".join(dependency[1]) + "? ") + \ + pp.pkgquery(dependency[0]+dependency[2]) + ")" + else: + print " " * (spacing * 2) + pkg.get_cpv() + else: + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print " " * (spacing * 2) + pp.cpv(pkg.get_cpv()), + print "(" + pp.pkgquery(dependency[0]+dependency[2]) + ")" + else: + print " " * (spacing * 2) + pkg.get_cpv() + isdep = 1 + elif not Config["piping"] and Config["verbosityLevel"] >= 3: + if dependency[1]: + print " "*len(pkg.get_cpv()) + " " * (spacing * 2) + \ + " (" + pp.useflag("&".join(dependency[1]) + "? ") + \ + pp.pkgquery(dependency[0]+dependency[2]) + ")" + else: + print " "*len(pkg.get_cpv()) + " " * (spacing * 2) + " (" + \ + pp.pkgquery(dependency[0]+dependency[2]) + ")" + + break + + # if --indirect specified, call ourselves again with the dependency + # Do not call, if we have already called ourselves. + if isdep and not opts["onlyDirect"] and pkg.get_cpv() not in self.pkgseen \ + and (spacing < opts["depth"] or opts["depth"] == -1): + self.pkgseen.append(pkg.get_cpv()) + self.perform(['=' + pkg.get_cpv(), '--indirect', '--spacing=' + str(int(opts["spacing"]+1))]) + opts["spacing"] = spacing; + + + def shortHelp(self): + return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list all direct dependencies matching " + \ + pp.pkgquery("pkgspec") + + def longHelp(self): + return "List all direct dependencies matching a query pattern" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("depends") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \ + "\n" + \ + pp.localoption("<local-opts>") + " is either of: \n" + \ + " " + pp.localoption("-a, --all-packages") + " - search in all available packages (slow)\n" + \ + " " + pp.localoption("-d, --direct") + " - search direct dependencies only (default)\n" + \ + " " + pp.localoption("-D, --indirect") + " - search indirect dependencies (VERY slow)\n" + \ + " " + pp.localoption(" --depth=n") + " - limit indirect dependency tree to specified depth" + + +class CmdListPackages(Command): + """List packages satisfying pkgQuery""" + def __init__(self): + self.default_opts = { + "category": "*", + "includeInstalled": 1, + "includePortTree": 0, + "includeOverlayTree": 0, + "includeMasked": 1, + "regex": 0, + "exact": 0, + "duplicates": 0 + } + + def parseArgs(self, args): + + query = "" + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x in ["-i", "--installed"]: + opts["includeInstalled"] = 1 + elif x in ["-I", "--exclude-installed"]: + # If -I is the only option, warn + # (warning located in perform()) + opts["includeInstalled"] = 0 + elif x in ["-p", "--portage-tree"]: + opts["includePortTree"] = 1 + elif x in ["-o", "--overlay-tree"]: + opts["includeOverlayTree"] = 1 + elif x in ["-m", "--exclude-masked"]: + opts["includeMasked"] = 0 + elif x in ["-f", "--full-regex"]: + opts["regex"] = 1 + elif x in ["-e", "--exact-name"]: + opts["exact"] = 1 + elif x in ["-d", "--duplicates"]: + opts["duplicates"] = 1 + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + + # Only search installed packages when listing duplicated packages + if opts["duplicates"]: + opts["includeInstalled"] = 1 + opts["includePortTree"] = 0 + opts["includeOverlayTree"] = 0 + + if need_help: + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def perform(self, args): + (query, opts) = self.parseArgs(args) + + rev = "" + name = "" + ver = "" + cat = "" + + if query != "": + try: (cat, name, ver, rev) = gentoolkit.split_package_name(query) + except ValueError, e: + if str(e) == 'too many values to unpack': + print_error("A pattern to match against package names was expected, ") + warn_msg = "but %s has too many slashes ('/') to match any package." + die (1, warn_msg % query) + else: raise ValueError(e) + if rev == "r0": rev = "" + + package_finder = None + + if opts["includeInstalled"]: + if opts["includePortTree"] or opts["includeOverlayTree"]: + package_finder = gentoolkit.find_all_packages + else: + package_finder = gentoolkit.find_all_installed_packages + elif opts["includePortTree"] or opts["includeOverlayTree"]: + package_finder = gentoolkit.find_all_uninstalled_packages + else: + # -I was specified, and no selection of what packages to list was made + print_warn("With -I you must specify one of -i, -p or -o. Assuming -p") + opts["includePortTree"] = 1 + package_finder = gentoolkit.find_all_uninstalled_packages + + filter_fn = None + + if Config["verbosityLevel"] >= 3: + scat = "'" + cat + "'" + if not cat: + scat = "all categories" + sname = "package '" + name + "'" + if not name: + sname = "all packages" + if not Config["piping"] and Config["verbosityLevel"] >= 3: + print_info(1, "[ Searching for " + pp.cpv(sname) + " in " + pp.cpv(scat) + " among: ]") + + # replace empty strings with .* and escape regular expression syntax + if query != "": + if not opts["regex"]: + cat, name, ver, rev = [re.sub('^$', ".*", re.escape(x)) for x in cat, name, ver, rev] + else: + cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev] + + try: + if opts["exact"]: + filter_fn = lambda x: re.match(cat+"/"+name, x) + else: + filter_fn = lambda x: re.match(cat+"/.*"+name, x) + matches = package_finder(filter_fn) + except: + die(2, "The query '" + pp.regexpquery(query) + "' does not appear to be a valid regular expression") + else: + cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev] + filter_fn = lambda x: True + matches = package_finder(filter_fn) + + # Find duplicate packages + if opts["duplicates"]: + dups = {} + newmatches = [] + for pkg in matches: + mykey = pkg.get_category() + "/" + pkg.get_name() + if dups.has_key(mykey): + dups[mykey].append(pkg) + else: + dups[mykey] = [pkg] + + for mykey in dups.keys(): + if len(dups[mykey]) > 1: + newmatches += dups[mykey] + + matches = newmatches + + matches = gentoolkit.sort_package_list(matches) + + # If no version supplied, fix regular expression + if ver == ".*": ver = "[0-9]+[^-]*" + + if rev != ".*": # revision supplied + ver = ver + "-" + rev + + if opts["exact"]: + rx = re.compile(cat + "/" + name + "-" + ver) + else: + rx = re.compile(cat + "/.*" + name + ".*-" + ver) + + if opts["includeInstalled"]: + self._print_installed(matches, rx) + + if opts["includePortTree"]: + self._print_porttree(matches, rx) + + if opts["includeOverlayTree"]: + self._print_overlay(matches, rx) + + def _get_mask_status(self, pkg): + pkgmask = 0 + if pkg.is_masked(): + # Uncomment to only have package masked files show an 'M' + # maskreasons = portage.getmaskingstatus(pkg.get_cpv()) + # if "package.mask" in maskreasons: + pkgmask = pkgmask + 3 + keywords = pkg.get_env_var("KEYWORDS").split() + if gentoolkit.settings["ARCH"] not in keywords: + if "~" + gentoolkit.settings["ARCH"] in keywords: + pkgmask = pkgmask + 1 + elif "-" + gentoolkit.settings["ARCH"] in keywords or "-*" in keywords: + pkgmask = pkgmask + 2 + return pkgmask + + def _generic_print(self, header, exclude, matches, rx, status): + if Config["verbosityLevel"] >= 3: + print_info(1, header) + + pfxmodes = [ "---", "I--", "-P-", "--O" ] + maskmodes = [ " ", " ~", " -", "M ", "M~", "M-" ] + + for pkg in matches: + if exclude(pkg): + continue + + pkgmask = self._get_mask_status(pkg) + + slot = pkg.get_env_var("SLOT") + + if rx.search(pkg.get_cpv()): + if Config["piping"]: + print_info(0, pkg.get_cpv()) + else: + print_info(0, "[" + pp.installedflag(pfxmodes[status]) + "] [" + pp.maskflag(maskmodes[pkgmask]) + "] " + pp.cpv(pkg.get_cpv()) + " (" + pp.slot(slot) + ")") + + def _print_overlay(self, matches, rx): + self._generic_print( + pp.section(" *") + " overlay tree (" + pp.path(gentoolkit.settings["PORTDIR_OVERLAY"]) + ")", + lambda x: not x.is_overlay(), + matches, rx, 3) + + def _print_porttree(self, matches, rx): + self._generic_print( + pp.section(" *") + " Portage tree (" + pp.path(gentoolkit.settings["PORTDIR"]) + ")", + lambda x: x.is_overlay() or x.is_installed(), + matches, rx, 2) + + def _print_installed(self, matches, rx): + self._generic_print( + pp.section(" *") + " installed packages", + lambda x: not x.is_installed(), + matches, rx, 1) + + def shortHelp(self): + return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list all packages matching " + pp.pkgquery("pkgspec") + def longHelp(self): + return "List all packages matching a query pattern" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("list") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \ + "\n" + \ + pp.localoption("<local-opts>") + " is either of: \n" + \ + " " + pp.localoption("-i, --installed") + " - search installed packages (default)\n" + \ + " " + pp.localoption("-I, --exclude-installed") + " - do not search installed packages\n" + \ + " " + pp.localoption("-p, --portage-tree") + " - also search in portage tree (" + gentoolkit.settings["PORTDIR"] + ")\n" + \ + " " + pp.localoption("-o, --overlay-tree") + " - also search in overlay tree (" + gentoolkit.settings["PORTDIR_OVERLAY"] + ")\n" + \ + " " + pp.localoption("-f, --full-regex") + " - query is a regular expression\n" + \ + " " + pp.localoption("-e, --exact-name") + " - list only those packages that exactly match\n" + \ + " " + pp.localoption("-d, --duplicates") + " - list only installed duplicate packages\n" + +class CmdFindUSEs(Command): + """Find all packages with a particular USE flag.""" + def __init__(self): + self.default_opts = { + "category": "*", + "includeInstalled": 1, + "includePortTree": 0, + "includeOverlayTree": 0, + "includeMasked": 1, + "regex": 0 + } + + def parseArgs(self, args): + + query = "" + need_help = 0 + opts = self.default_opts + skip = 0 + + for i in xrange(len(args)): + + if skip: + skip -= 1 + continue + x = args[i] + + if x in ["-h","--help"]: + need_help = 1 + break + elif x in ["-i", "--installed"]: + opts["includeInstalled"] = 1 + elif x in ["-I", "--exclude-installed"]: + opts["includeInstalled"] = 0 + elif x in ["-p", "--portage-tree"]: + opts["includePortTree"] = 1 + elif x in ["-o", "--overlay-tree"]: + opts["includeOverlayTree"] = 1 + elif x in ["-m", "--exclude-masked"]: + opts["includeMasked"] = 0 + elif x[0] == "-": + print_warn("unknown local option %s, ignoring" % x) + else: + query = x + + if need_help: + print_info(0, self.longHelp()) + sys.exit(-1) + + return (query, opts) + + def perform(self, args): + (query, opts) = self.parseArgs(args) + + rev = ".*" + name = ".*" + ver = ".*" + cat = ".*" + + package_finder = None + + if opts["includeInstalled"] and (opts["includePortTree"] or opts["includeOverlayTree"]): + package_finder = gentoolkit.find_all_packages + elif opts["includeInstalled"]: + package_finder = gentoolkit.find_all_installed_packages + elif opts["includePortTree"] or opts["includeOverlayTree"]: + package_finder = gentoolkit.find_all_uninstalled_packages + + if not package_finder: + die(2,"You must specify one of -i, -p or -o") + + filter_fn = lambda x: True + + if Config["verbosityLevel"] >= 3: + scat = "'" + cat + "'" + if cat == ".*": + scat = "all categories" + if not Config["piping"]: + print_info(2, "[ Searching for USE flag " + pp.useflag(query) + " in " + pp.cpv(scat) + " among: ]") + if opts["includeInstalled"]: + print_info(1, pp.section(" *") + " installed packages") + if opts["includePortTree"]: + print_info(1, pp.section(" *") + " Portage tree (" + pp.path(gentoolkit.settings["PORTDIR"]) + ")") + if opts["includeOverlayTree"]: + print_info(1, pp.section(" *") + " overlay tree (" + pp.path(gentoolkit.settings["PORTDIR_OVERLAY"]) + ")") + + matches = package_finder(filter_fn) + + rx = re.compile(cat + "/" + name + "-" + ver + "(-" + rev + ")?") + pfxmodes = [ "---", "I--", "-P-", "--O" ] + maskmodes = [ " ", " ~", " -", "M ", "M~", "M-" ] + for pkg in matches: + status = 0 + if pkg.is_installed(): + status = 1 + elif pkg.is_overlay(): + status = 3 + else: + status = 2 + + useflags = pkg.get_env_var("IUSE").split() + + if query not in useflags: + continue + + # Determining mask status + pkgmask = 0 + if pkg.is_masked(): + pkgmask = pkgmask + 3 + keywords = pkg.get_env_var("KEYWORDS").split() + if "~"+gentoolkit.settings["ARCH"] in keywords: + pkgmask = pkgmask + 1 + elif "-*" in keywords or "-"+gentoolkit.settings["ARCH"] in keywords: + pkgmask = pkgmask + 2 + + # Determining SLOT value + slot = pkg.get_env_var("SLOT") + + if (status == 1 and opts["includeInstalled"]) or \ + (status == 2 and opts["includePortTree"]) or \ + (status == 3 and opts["includeOverlayTree"]): + if Config["piping"]: + print_info(0, pkg.get_cpv()) + else: + print_info(0, "[" + pp.installedflag(pfxmodes[status]) + "] [" + pp.maskflag(maskmodes[pkgmask]) + "] " + pp.cpv(pkg.get_cpv()) + " (" + pp.slot(slot) + ")") + + def shortHelp(self): + return pp.localoption("<local-opts> ") + pp.pkgquery("useflag") + " - list all packages with " + pp.pkgquery("useflag") + def longHelp(self): + return "List all packages with a particular USE flag" + \ + "\n" + \ + "Syntax:\n" + \ + " " + pp.command("list") + pp.localoption(" <local-opts> ") + pp.pkgquery("useflag") + \ + "\n" + \ + pp.localoption("<local-opts>") + " is either of: \n" + \ + " " + pp.localoption("-i, --installed") + " - search installed packages (default)\n" + \ + " " + pp.localoption("-I, --exclude-installed") + " - do not search installed packages\n" + \ + " " + pp.localoption("-p, --portage-tree") + " - also search in portage tree (" + gentoolkit.settings["PORTDIR"] + ")\n" + \ + " " + pp.localoption("-o, --overlay-tree") + " - also search in overlay tree (" + gentoolkit.settings["PORTDIR_OVERLAY"] + ")\n" + +# +# Command line tokens to commands mapping +# + +Known_commands = { + "list" : CmdListPackages(), + "files" : CmdListFiles(), + "belongs" : CmdListBelongs(), + "depends" : CmdListDepends(), + "hasuse" : CmdFindUSEs(), + "uses" : CmdDisplayUSEs(), + "depgraph" : CmdDisplayDepGraph(), + "changes" : CmdDisplayChanges(), + "size" : CmdDisplaySize(), + "check" : CmdCheckIntegrity(), + "stats" : CmdDisplayStatistics(), + "glsa" : CmdListGLSAs(), + "which": CmdWhich() + } + +# Short command line tokens + +Short_commands = { + "a" : "glsa", + "b" : "belongs", + "c" : "changes", + "d" : "depends", + "f" : "files", + "g" : "depgraph", + "h" : "hasuse", + "k" : "check", + "l" : "list", + "s" : "size", + "t" : "stats", + "u" : "uses", + "w" : "which" +} + +from gentoolkit import Config + +Config = { + # Query will include packages installed on the system + "installedPackages": 1, + # Query will include packages available for installation + "uninstalledPackages": 0, + # Query will include overlay packages (iff uninstalledPackages==1) + "overlayPackages": 1, + # Query will include masked packages (iff uninstalledPackages==1) + "maskedPackages": 0, + # Query will only consider packages in the following categories, empty means all. + "categoryFilter": [], + # Enable color output (-1 = use Portage setting, 0 = force off, 1 = force on) + "color": -1, + # Level of detail on the output + "verbosityLevel": 3, + # Query will display info for multiple SLOTed versions + "considerDuplicates": 1, + # Are we writing to a pipe? + "piping": 0 +} + +def printVersion(): + """Print the version of this tool to the console.""" + print_info(0, __productname__ + "(" + __version__ + ") - " + \ + __description__) + print_info(0, "Author(s): " + __author__) + +def buildReverseMap(m): + r = {} + for x in m.keys(): + r[m[x]] = x + return r + +def printUsage(): + """Print full usage information for this tool to the console.""" + + short_cmds = buildReverseMap(Short_commands); + + print_info(0, pp.emph("Usage: ") + pp.productname(__productname__) + pp.globaloption(" <global-opts> ") + pp.command("command") + pp.localoption(" <local-opts>")) + print_info(0, "where " + pp.globaloption("<global-opts>") + " is one of") + print_info(0, pp.globaloption(" -q, --quiet") + " - minimal output") + print_info(0, pp.globaloption(" -C, --nocolor") + " - turn off colours") + print_info(0, pp.globaloption(" -h, --help") + " - this help screen") + print_info(0, pp.globaloption(" -V, --version") + " - display version info") + print_info(0, pp.globaloption(" -N, --no-pipe") + " - turn off pipe detection") + + print_info(0, "where " + pp.command("command") + "(" + pp.command("short") + ") is one of") + keys = Known_commands.keys() + keys.sort() + for x in keys: + print_info(0, " " + pp.command(x) + "(" + pp.command(short_cmds[x]) + ") " + \ + Known_commands[x].shortHelp()) + print + +def configure(): + """Set up default configuration. + """ + + # Guess colour output + if (Config["color"] == -1 and \ + ((not sys.stdout.isatty()) or (gentoolkit.settings["NOCOLOR"] in ["yes","true"]))): + pp.output.nocolor() + + # Guess piping output + if not sys.stdout.isatty(): + Config["piping"] = True + else: + Config["piping"] = False + + +def parseArgs(args): + """Parse tool-specific arguments. + + Arguments are on the form equery <tool-specific> [command] <command-specific> + + This function will only parse the <tool-specific> bit. + """ + command = None + local_opts = [] + showhelp = 0 + + def expand(x): + if x in Short_commands.keys(): + return Short_commands[x] + return x + + for i in xrange(len(args)): + x = args[i] + if 0: + pass + elif x in ["-h", "--help"]: + showhelp = True + elif x in ["-V", "--version"]: + printVersion() + sys.exit(0) + elif x in ["-C", "--nocolor"]: + Config["color"] = 0 + pp.output.nocolor() + elif x in ["-N", "--no-pipe"]: + Config["piping"] = False + elif x in ["-q","--quiet"]: + Config["verbosityLevel"] = 0 + elif expand(x) in Known_commands.keys(): + command = Known_commands[expand(x)] + local_opts.extend(args[i+1:]) + if showhelp: + local_opts.append("--help") + break + else: + print_warn("unknown global option %s, reusing as local option" % x) + local_opts.append(x) + + if not command and showhelp: + printUsage() + sys.exit(0) + + return (command, local_opts) + +if __name__ == "__main__": + configure() + (cmd, local_opts) = parseArgs(sys.argv[1:]) + if cmd: + try: + cmd.perform(local_opts) + except KeyError, e: + if e and e[0].find("Specific key requires an operator") >= 0: + print_error("Invalid syntax: missing operator") + print_error("If you want only specific versions please use one of") + print_error("the following operators as prefix for the package name:") + print_error(" > >= = <= <") + print_error("Example to only match gcc versions greater or equal 3.2:") + print_error(" >=sys-devel/gcc-3.2") + print_error("") + print_error("Note: The symbols > and < are used for redirection in the shell") + print_error("and must be quoted if either one is used.") + + else: + print_error("Internal portage error, terminating") + if len(e[0]): + print_error(str(e)) + sys.exit(2) + except ValueError, e: + if isinstance(e[0], list): + print_error("Ambiguous package name " + pp.emph("\"" + local_opts[0] + "\"")) + print_error("Please use one of the following long names:") + for p in e[0]: + print_error(" " + str(p)) + else: + print_error("Internal portage error, terminating") + if len(e[0]): + print_error(str(e[0])) + sys.exit(2) + except KeyboardInterrupt: + print_info(0, "Interrupted by user, aborting.") + else: + print_error("No command or unknown command given") + printUsage() + |