#!/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 __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(" ") + pp.pkgquery("packagename<-version>") + "\n" + \ "\n" + \ "Note: category and version parts are optional. \n" + \ "\n" + \ pp.localoption("") + " 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=") + " - filter output\n" + \ " " + pp.localoption("") + " 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(" ") + 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(" ") + 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(" ") + pp.path("filename") + \ "\n" + \ pp.localoption("") + " 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, " : ") 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(" ") + 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(" ") + pp.pkgquery("pkgspec") + \ "\n" + \ pp.localoption("") + " 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(" ") + 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(" ") + pp.pkgquery("pkgspec") + \ "\n" + \ pp.localoption("") + " 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(" ") + 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(" ") + pp.pkgquery("pkgspec") + \ "\n" + \ pp.localoption("") + " 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(" ") + 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(" ") + pp.pkgquery("pkgspec") + \ "\n" + \ pp.localoption("") + " 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(" ") + 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(" ") + pp.pkgquery("pkgspec") + \ "\n" + \ pp.localoption("") + " 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(" ") + 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(" ") + pp.pkgquery("useflag") + \ "\n" + \ pp.localoption("") + " 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(" ") + pp.command("command") + pp.localoption(" ")) print_info(0, "where " + pp.globaloption("") + " 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 [command] This function will only parse the 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()