aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'gentoolkit/pym/gentoolkit/equery')
-rw-r--r--gentoolkit/pym/gentoolkit/equery/__init__.py351
-rw-r--r--gentoolkit/pym/gentoolkit/equery/belongs.py156
-rw-r--r--gentoolkit/pym/gentoolkit/equery/changes.py205
-rw-r--r--gentoolkit/pym/gentoolkit/equery/check.py291
-rw-r--r--gentoolkit/pym/gentoolkit/equery/depends.py193
-rw-r--r--gentoolkit/pym/gentoolkit/equery/depgraph.py223
-rw-r--r--gentoolkit/pym/gentoolkit/equery/files.py320
-rw-r--r--gentoolkit/pym/gentoolkit/equery/hasuse.py156
-rw-r--r--gentoolkit/pym/gentoolkit/equery/list_.py224
-rw-r--r--gentoolkit/pym/gentoolkit/equery/meta.py494
-rw-r--r--gentoolkit/pym/gentoolkit/equery/size.py193
-rw-r--r--gentoolkit/pym/gentoolkit/equery/uses.py317
-rw-r--r--gentoolkit/pym/gentoolkit/equery/which.py102
13 files changed, 3225 insertions, 0 deletions
diff --git a/gentoolkit/pym/gentoolkit/equery/__init__.py b/gentoolkit/pym/gentoolkit/equery/__init__.py
new file mode 100644
index 0000000..5833f29
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/__init__.py
@@ -0,0 +1,351 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Gentoo package query tool"""
+
+# Move to Imports section after Python 2.6 is stable
+from __future__ import with_statement
+
+__all__ = (
+ 'format_options',
+ 'format_package_names',
+ 'mod_usage'
+)
+__docformat__ = 'epytext'
+# version is dynamically set by distutils sdist
+__version__ = "svn"
+
+# =======
+# Imports
+# =======
+
+import errno
+import os
+import sys
+import time
+from getopt import getopt, GetoptError
+
+import portage
+
+import gentoolkit
+from gentoolkit import CONFIG
+from gentoolkit import errors
+from gentoolkit import pprinter as pp
+from gentoolkit.textwrap_ import TextWrapper
+
+__productname__ = "equery"
+__authors__ = (
+ 'Karl Trygve Kalleberg - Original author',
+ 'Douglas Anderson - 0.3.0 author'
+)
+
+# =======
+# Globals
+# =======
+
+NAME_MAP = {
+ 'b': 'belongs',
+ 'c': 'changes',
+ 'k': 'check',
+ 'd': 'depends',
+ 'g': 'depgraph',
+ 'f': 'files',
+ 'h': 'hasuse',
+ 'l': 'list_',
+ 'm': 'meta',
+ 's': 'size',
+ 'u': 'uses',
+ 'w': 'which'
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @param with_description (bool): Option to print module's __doc__ or not
+ """
+
+ if with_description:
+ print __doc__
+ print main_usage()
+ print
+ print pp.globaloption("global options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -q, --quiet", "minimal output"),
+ (" -C, --no-color", "turn off colors"),
+ (" -N, --no-pipe", "turn off pipe detection"),
+ (" -V, --version", "display version info")
+ ))
+ print
+ print pp.command("modules") + " (" + pp.command("short name") + ")"
+ print format_options((
+ (" (b)elongs", "list what package FILES belong to"),
+ (" (c)hanges", "list changelog entries for ATOM"),
+ (" chec(k)", "verify checksums and timestamps for PKG"),
+ (" (d)epends", "list all packages directly depending on ATOM"),
+ (" dep(g)raph", "display a tree of all dependencies for PKG"),
+ (" (f)iles", "list all files installed by PKG"),
+ (" (h)asuse", "list all packages that have USE flag"),
+ (" (l)ist", "list package matching PKG"),
+ (" (m)eta", "display metadata about PKG"),
+ (" (s)ize", "display total size of all files owned by PKG"),
+ (" (u)ses", "display USE flags for PKG"),
+ (" (w)hich", "print full path to ebuild for PKG")
+ ))
+
+
+def expand_module_name(module_name):
+ """Returns one of the values of NAME_MAP or raises KeyError"""
+
+ if module_name == 'list':
+ # list is a Python builtin type, so we must rename our module
+ return 'list_'
+ elif module_name in NAME_MAP.values():
+ return module_name
+ else:
+ return NAME_MAP[module_name]
+
+
+def format_options(options):
+ """Format module options.
+
+ @type options: list
+ @param options: [('option 1', 'description 1'), ('option 2', 'des... )]
+ @rtype: str
+ @return: formatted options string
+ """
+
+ result = []
+ twrap = TextWrapper(width=CONFIG['termWidth'])
+ opts = (x[0] for x in options)
+ descs = (x[1] for x in options)
+ for opt, desc in zip(opts, descs):
+ twrap.initial_indent = pp.emph(opt.ljust(25))
+ twrap.subsequent_indent = " " * 25
+ result.append(twrap.fill(desc))
+
+ return '\n'.join(result)
+
+
+def format_filetype(path, fdesc, show_type=False, show_md5=False,
+ show_timestamp=False):
+ """Format a path for printing.
+
+ @type path: str
+ @param path: the path
+ @type fdesc: list
+ @param fdesc: [file_type, timestamp, MD5 sum/symlink target]
+ file_type is one of dev, dir, obj, sym.
+ If file_type is dir, there is no timestamp or MD5 sum.
+ If file_type is sym, fdesc[2] is the target of the symlink.
+ @type show_type: bool
+ @param show_type: if True, prepend the file's type to the formatted string
+ @type show_md5: bool
+ @param show_md5: if True, append MD5 sum to the formatted string
+ @type show_timestamp: bool
+ @param show_timestamp: if True, append time-of-creation after pathname
+ @rtype: str
+ @return: formatted pathname with optional added information
+ """
+
+ ftype = fpath = stamp = md5sum = ""
+
+ if fdesc[0] == "obj":
+ ftype = "file"
+ fpath = path
+ stamp = format_timestamp(fdesc[1])
+ md5sum = fdesc[2]
+ elif fdesc[0] == "dir":
+ ftype = "dir"
+ fpath = pp.path(path)
+ elif fdesc[0] == "sym":
+ ftype = "sym"
+ stamp = format_timestamp(fdesc[1])
+ tgt = fdesc[2].split()[0]
+ if CONFIG["piping"]:
+ fpath = path
+ else:
+ fpath = pp.path_symlink(path + " -> " + tgt)
+ elif fdesc[0] == "dev":
+ ftype = "dev"
+ fpath = path
+ else:
+ sys.stderr.write(
+ pp.error("%s has unknown type: %s" % (path, fdesc[0]))
+ )
+
+ result = ""
+ if show_type:
+ result += "%4s " % ftype
+ result += fpath
+ if show_timestamp:
+ result += " " + stamp
+ if show_md5:
+ result += " " + md5sum
+
+ return result
+
+
+def format_timestamp(timestamp):
+ """Format a timestamp into, e.g., '2009-01-31 21:19:44' format"""
+
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(timestamp)))
+
+
+def initialize_configuration():
+ """Setup the standard equery config"""
+
+ # Get terminal size
+ term_width = pp.output.get_term_size()[1]
+ if term_width == -1:
+ # get_term_size() failed. Set a sane default width:
+ term_width = 80
+
+ # Terminal size, minus a 1-char margin for text wrapping
+ CONFIG['termWidth'] = term_width - 1
+
+ # Guess color output
+ if (CONFIG['color'] == -1 and (not sys.stdout.isatty() or
+ os.getenv("NOCOLOR") in ("yes", "true")) or CONFIG['color'] == 0):
+ pp.output.nocolor()
+
+ CONFIG['verbose'] = not CONFIG['piping']
+
+
+def main_usage():
+ """Return the main usage message for equery"""
+
+ return "%(usage)s %(product)s [%(g_opts)s] %(mod_name)s [%(mod_opts)s]" % {
+ 'usage': pp.emph("Usage:"),
+ 'product': pp.productname(__productname__),
+ 'g_opts': pp.globaloption("global-options"),
+ 'mod_name': pp.command("module-name"),
+ 'mod_opts': pp.localoption("module-options")
+ }
+
+
+def mod_usage(mod_name="module", arg="pkgspec", optional=False):
+ """Provide a consistent usage message to the calling module.
+
+ @type arg: string
+ @param arg: what kind of argument the module takes (pkgspec, filename, etc)
+ @type optional: bool
+ @param optional: is the argument optional?
+ """
+
+ return "%(usage)s: %(mod_name)s [%(opts)s] %(arg)s" % {
+ 'usage': pp.emph("Usage"),
+ 'mod_name': pp.command(mod_name),
+ 'opts': pp.localoption("options"),
+ 'arg': ("[%s]" % pp.emph(arg)) if optional else pp.emph(arg)
+ }
+
+
+def parse_global_options(global_opts, args):
+ """Parse global input args and return True if we should display help for
+ the called module, else False (or display help and exit from here).
+ """
+
+ need_help = False
+ opts = (opt[0] for opt in global_opts)
+ for opt in opts:
+ if opt in ('-h', '--help'):
+ if args:
+ need_help = True
+ else:
+ print_help()
+ sys.exit(0)
+ elif opt in ('-q','--quiet'):
+ CONFIG['quiet'] = True
+ elif opt in ('-C', '--no-color', '--nocolor'):
+ CONFIG['color'] = 0
+ pp.output.nocolor()
+ elif opt in ('-N', '--no-pipe'):
+ CONFIG['piping'] = False
+ CONFIG['verbose'] = True
+ elif opt in ('-V', '--version'):
+ print_version()
+ sys.exit(0)
+ elif opt in ('--debug'):
+ CONFIG['debug'] = True
+
+ return need_help
+
+
+def print_version():
+ """Print the version of this tool to the console."""
+
+ print "%(product)s (%(version)s) - %(docstring)s" % {
+ "product": pp.productname(__productname__),
+ "version": __version__,
+ "docstring": __doc__
+ }
+
+
+def split_arguments(args):
+ """Separate module name from module arguments"""
+
+ return args.pop(0), args
+
+
+def main():
+ """Parse input and run the program."""
+
+ short_opts = "hqCNV"
+ long_opts = (
+ 'help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version', 'debug'
+ )
+
+ initialize_configuration()
+
+ try:
+ global_opts, args = getopt(sys.argv[1:], short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Global %s" % err))
+ print_help(with_description=False)
+ sys.exit(2)
+
+ # Parse global options
+ need_help = parse_global_options(global_opts, args)
+
+ # FIXME: There are a few places that make use of both quiet and verbose.
+ # Consider combining.
+ if CONFIG['quiet']:
+ CONFIG['verbose'] = False
+
+ try:
+ module_name, module_args = split_arguments(args)
+ except IndexError:
+ print_help()
+ sys.exit(2)
+
+ if need_help:
+ module_args.append('--help')
+
+ try:
+ expanded_module_name = expand_module_name(module_name)
+ except KeyError:
+ sys.stderr.write(pp.error("Unknown module '%s'" % module_name))
+ print_help(with_description=False)
+ sys.exit(2)
+
+ try:
+ loaded_module = __import__(
+ expanded_module_name, globals(), locals(), [], -1
+ )
+ loaded_module.main(module_args)
+ except portage.exception.AmbiguousPackageName, err:
+ raise errors.GentoolkitAmbiguousPackage(err)
+ except IOError, err:
+ if err.errno != errno.EPIPE:
+ raise
+
+if __name__ == '__main__':
+ main()
diff --git a/gentoolkit/pym/gentoolkit/equery/belongs.py b/gentoolkit/pym/gentoolkit/equery/belongs.py
new file mode 100644
index 0000000..3845b9d
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/belongs.py
@@ -0,0 +1,156 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""List all packages owning a particular file
+
+Note: Normally, only one package will own a file. If multiple packages own
+ the same file, it usually constitutes a problem, and should be reported.
+"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit.equery import (format_filetype, format_options, mod_usage,
+ CONFIG)
+from gentoolkit.helpers import FileOwner
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "fullRegex": False,
+ "earlyOut": False,
+ "nameOnly": False
+}
+
+# =======
+# Classes
+# =======
+
+class BelongsPrinter(object):
+ """Outputs a formatted list of packages that claim to own a files."""
+
+ def __init__(self, verbose=True, name_only=False):
+ if verbose:
+ self.print_fn = self.print_verbose
+ else:
+ self.print_fn = self.print_quiet
+
+ self.name_only = name_only
+
+ def __call__(self, pkg, cfile):
+ self.print_fn(pkg, cfile)
+
+ # W0613: *Unused argument %r*
+ # pylint: disable-msg=W0613
+ def print_quiet(self, pkg, cfile):
+ "Format for minimal output."
+ if self.name_only:
+ name = pkg.cpv.cp
+ else:
+ name = str(pkg.cpv)
+ print name
+
+ def print_verbose(self, pkg, cfile):
+ "Format for full output."
+ file_str = pp.path(format_filetype(cfile, pkg.parsed_contents()[cfile]))
+ if self.name_only:
+ name = pkg.cpv.cp
+ else:
+ name = str(pkg.cpv)
+ print pp.cpv(name), "(" + file_str + ")"
+
+
+# =========
+# Functions
+# =========
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ for opt in opts:
+ if opt in ('-h','--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-e', '--early-out', '--earlyout'):
+ if opt == '--earlyout':
+ sys.stderr.write(pp.warn("Use of --earlyout is deprecated."))
+ sys.stderr.write(pp.warn("Please use --early-out."))
+ print
+ QUERY_OPTS['earlyOut'] = True
+ elif opt in ('-f', '--full-regex'):
+ QUERY_OPTS['fullRegex'] = True
+ elif opt in ('-n', '--name-only'):
+ QUERY_OPTS['nameOnly'] = True
+
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ print mod_usage(mod_name="belongs", arg="filename")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -f, --full-regex", "supplied query is a regex" ),
+ (" -e, --early-out", "stop when first match is found"),
+ (" -n, --name-only", "don't print the version")
+ ))
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "h:fen"
+ long_opts = ('help', 'full-regex', 'early-out', 'earlyout',
+ 'name-only')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ if CONFIG['verbose']:
+ print " * Searching for %s ... " % (pp.regexpquery(",".join(queries)))
+
+ printer_fn = BelongsPrinter(
+ verbose=CONFIG['verbose'], name_only=QUERY_OPTS['nameOnly']
+ )
+
+ find_owner = FileOwner(
+ is_regex=QUERY_OPTS['fullRegex'],
+ early_out=QUERY_OPTS['earlyOut'],
+ printer_fn=printer_fn
+ )
+
+ find_owner(queries)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/changes.py b/gentoolkit/pym/gentoolkit/equery/changes.py
new file mode 100644
index 0000000..8adf246
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/changes.py
@@ -0,0 +1,205 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""Displays the ChangeLog entry for the latest installable version of an atom"""
+
+# Move to Imports sections when Python 2.6 is stable
+from __future__ import with_statement
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.atom import Atom
+from gentoolkit.equery import format_options, mod_usage
+from gentoolkit.helpers import ChangeLog, find_best_match, find_packages
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ 'onlyLatest': False,
+ 'showFullLog': False,
+ 'limit': None,
+ 'from': None,
+ 'to': None
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ print mod_usage(mod_name="changes")
+ print
+ print pp.emph("examples")
+ print (" c portage # show latest visible "
+ "version's entry")
+ print " c portage --full --limit=3 # show 3 latest entries"
+ print " c '=sys-apps/portage-2.1.6*' # use atom syntax"
+ print " c portage --from=2.2_rc20 --to=2.2_rc30 # use version ranges"
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -l, --latest", "display only the latest ChangeLog entry"),
+ (" -f, --full", "display the full ChangeLog"),
+ (" --limit=NUM",
+ "limit the number of entries displayed (with --full)"),
+ (" --from=VER", "set which version to display from"),
+ (" --to=VER", "set which version to display to"),
+ ))
+
+
+def get_match(query):
+ """Find a valid package from which to get the ChangeLog path.
+
+ @raise GentoolkitNoMatches: if no matches found
+ """
+
+ match = matches = None
+ match = find_best_match(query)
+
+ if not match:
+ matches = find_packages(query, include_masked=True)
+ else:
+ matches = [match]
+
+ if not matches:
+ raise errors.GentoolkitNoMatches(query)
+
+ return matches[0]
+
+
+def is_ranged(atom):
+ """Return True if an atom string appears to be ranged, else False."""
+
+ return atom.startswith(('~', '<', '>')) or atom.endswith('*')
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ posargs = (x[1] for x in module_opts)
+ for opt, posarg in zip(opts, posargs):
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-f', '--full'):
+ QUERY_OPTS['showFullLog'] = True
+ elif opt in ('-l', '--latest'):
+ QUERY_OPTS['onlyLatest'] = True
+ elif opt in ('--limit',):
+ set_limit(posarg)
+ elif opt in ('--from',):
+ QUERY_OPTS['from'] = posarg
+ elif opt in ('--to',):
+ QUERY_OPTS['to'] = posarg
+
+
+def print_entries(entries):
+ """Print entries and strip trailing whitespace from the last entry."""
+
+ len_entries = len(entries)
+ for i, entry in enumerate(entries): # , start=1): in py2.6
+ i += 1
+ if i < len_entries:
+ print entry
+ else:
+ print entry.strip()
+
+
+def set_limit(posarg):
+ """Set a limit in QUERY_OPTS on how many ChangeLog entries to display.
+
+ Die if posarg is not an integer.
+ """
+
+ if posarg.isdigit():
+ QUERY_OPTS['limit'] = int(posarg)
+ else:
+ err = "Module option --limit requires integer (got '%s')"
+ sys.stderr.write(pp.error(err % posarg))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "hlf"
+ long_opts = ('help', 'full', 'from=', 'latest', 'limit=', 'to=')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ first_run = True
+ for query in queries:
+ if not first_run:
+ print
+
+ match = get_match(query)
+ changelog_path = os.path.join(match.package_path(), 'ChangeLog')
+ changelog = ChangeLog(changelog_path)
+
+ #
+ # Output
+ #
+
+ if (QUERY_OPTS['onlyLatest'] or (
+ changelog.entries and not changelog.indexed_entries
+ )):
+ print changelog.latest.strip()
+ else:
+ end = QUERY_OPTS['limit'] or len(changelog.indexed_entries)
+ if QUERY_OPTS['to'] or QUERY_OPTS['from']:
+ print_entries(
+ changelog.entries_matching_range(
+ from_ver=QUERY_OPTS['from'],
+ to_ver=QUERY_OPTS['to']
+ )[:end]
+ )
+ elif QUERY_OPTS['showFullLog']:
+ print_entries(changelog.full[:end])
+ else:
+ # Raises GentoolkitInvalidAtom here if invalid
+ atom = Atom(query) if is_ranged(query) else '=' + str(match.cpv)
+ print_entries(changelog.entries_matching_atom(atom)[:end])
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/check.py b/gentoolkit/pym/gentoolkit/equery/check.py
new file mode 100644
index 0000000..5969804
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/check.py
@@ -0,0 +1,291 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Checks timestamps and MD5 sums for files owned by a given installed package"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from functools import partial
+from getopt import gnu_getopt, GetoptError
+
+import portage.checksum as checksum
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "includeInstalled": True,
+ "includeOverlayTree": False,
+ "includePortTree": False,
+ "checkMD5sum": True,
+ "checkTimestamp" : True,
+ "isRegex": False,
+ "onlyFailures": False,
+ "printMatchInfo": False,
+ "showSummary" : True,
+ "showPassedFiles" : False,
+ "showFailedFiles" : True
+}
+
+# =======
+# Classes
+# =======
+
+class VerifyContents(object):
+ """Verify installed packages' CONTENTS files.
+
+ The CONTENTS file contains timestamps and MD5 sums for each file owned
+ by a package.
+ """
+ def __init__(self, printer_fn=None):
+ """Create a VerifyObjects instance.
+
+ @type printer_fn: callable
+ @param printer_fn: if defined, will be applied to each result as found
+ """
+ self.check_sums = True
+ self.check_timestamps = True
+ self.printer_fn = printer_fn
+
+ self.is_regex = False
+
+ def __call__(
+ self,
+ pkgs,
+ is_regex=False,
+ check_sums=True,
+ check_timestamps=True
+ ):
+ self.is_regex = is_regex
+ self.check_sums = check_sums
+ self.check_timestamps = check_timestamps
+
+ result = {}
+ for pkg in pkgs:
+ # _run_checks returns tuple(n_passed, n_checked, err)
+ check_results = self._run_checks(pkg.parsed_contents())
+ result[pkg.cpv] = check_results
+ if self.printer_fn is not None:
+ self.printer_fn(pkg.cpv, check_results)
+
+ return result
+
+ def _run_checks(self, files):
+ """Run some basic sanity checks on a package's contents.
+
+ If the file type (ftype) is not a directory or symlink, optionally
+ verify MD5 sums or mtimes via L{self._verify_obj}.
+
+ @see: gentoolkit.packages.get_contents()
+ @type files: dict
+ @param files: in form {'PATH': ['TYPE', 'TIMESTAMP', 'MD5SUM']}
+ @rtype: tuple
+ @return:
+ n_passed (int): number of files that passed all checks
+ n_checked (int): number of files checked
+ errs (list): check errors' descriptions
+ """
+ n_checked = 0
+ n_passed = 0
+ errs = []
+ for cfile in files:
+ n_checked += 1
+ ftype = files[cfile][0]
+ if not os.path.exists(cfile):
+ errs.append("%s does not exist" % cfile)
+ continue
+ elif ftype == "dir":
+ if not os.path.isdir(cfile):
+ err = "%(cfile)s exists, but is not a directory"
+ errs.append(err % locals())
+ continue
+ elif ftype == "obj":
+ obj_errs = self._verify_obj(files, cfile, errs)
+ if len(obj_errs) > len(errs):
+ errs = obj_errs[:]
+ continue
+ elif ftype == "sym":
+ target = files[cfile][2].strip()
+ if not os.path.islink(cfile):
+ err = "%(cfile)s exists, but is not a symlink"
+ errs.append(err % locals())
+ continue
+ tgt = os.readlink(cfile)
+ if tgt != target:
+ err = "%(cfile)s does not point to %(target)s"
+ errs.append(err % locals())
+ continue
+ else:
+ err = "%(cfile)s has unknown type %(ftype)s"
+ errs.append(err % locals())
+ continue
+ n_passed += 1
+
+ return n_passed, n_checked, errs
+
+ def _verify_obj(self, files, cfile, errs):
+ """Verify the MD5 sum and/or mtime and return any errors."""
+
+ obj_errs = errs[:]
+ if self.check_sums:
+ md5sum = files[cfile][2]
+ try:
+ cur_checksum = checksum.perform_md5(cfile, calc_prelink=1)
+ except IOError:
+ err = "Insufficient permissions to read %(cfile)s"
+ obj_errs.append(err % locals())
+ return obj_errs
+ if cur_checksum != md5sum:
+ err = "%(cfile)s has incorrect MD5sum"
+ obj_errs.append(err % locals())
+ return obj_errs
+ if self.check_timestamps:
+ mtime = int(files[cfile][1])
+ st_mtime = int(os.lstat(cfile).st_mtime)
+ if st_mtime != mtime:
+ err = (
+ "%(cfile)s has wrong mtime (is %(st_mtime)d, should be "
+ "%(mtime)d)"
+ )
+ obj_errs.append(err % locals())
+ return obj_errs
+
+ return obj_errs
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+
+ # Deprecation warning added by djanderson, 12/2008
+ depwarning = (
+ "Default action for this module has changed in Gentoolkit 0.3.",
+ "Use globbing to simulate the old behavior (see man equery).",
+ "Use '*' to check all installed packages.",
+ "Use 'foo-bar/*' to filter by category."
+ )
+ for line in depwarning:
+ sys.stderr.write(pp.warn(line))
+ print
+
+ print mod_usage(mod_name="check")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -f, --full-regex", "query is a regular expression"),
+ (" -o, --only-failures", "only display packages that do not pass"),
+ ))
+
+
+def checks_printer(cpv, data, verbose=True, only_failures=False):
+ """Output formatted results of pkg file(s) checks"""
+ seen = []
+
+ n_passed, n_checked, errs = data
+ n_failed = n_checked - n_passed
+ if only_failures and not n_failed:
+ return
+ else:
+ if verbose:
+ if not cpv in seen:
+ print "* Checking %s ..." % (pp.emph(str(cpv)))
+ seen.append(cpv)
+ else:
+ print "%s:" % cpv,
+
+ if verbose:
+ for err in errs:
+ sys.stderr.write(pp.error(err))
+
+ if verbose:
+ n_passed = pp.number(str(n_passed))
+ n_checked = pp.number(str(n_checked))
+ info = " %(n_passed)s out of %(n_checked)s files passed"
+ print info % locals()
+ else:
+ print "failed(%s)" % n_failed
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ for opt in opts:
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-f', '--full-regex'):
+ QUERY_OPTS['isRegex'] = True
+ elif opt in ('-o', '--only-failures'):
+ QUERY_OPTS['onlyFailures'] = True
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "hof"
+ long_opts = ('help', 'only-failures', 'full-regex')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ first_run = True
+ for query in queries:
+ if not first_run:
+ print
+
+ matches = do_lookup(query, QUERY_OPTS)
+
+ if not matches:
+ raise errors.GentoolkitNoMatches(query, in_installed=True)
+
+ matches.sort()
+
+ printer = partial(
+ checks_printer,
+ verbose=CONFIG['verbose'],
+ only_failures=QUERY_OPTS['onlyFailures']
+ )
+ check = VerifyContents(printer_fn=printer)
+ check(matches)
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/depends.py b/gentoolkit/pym/gentoolkit/equery/depends.py
new file mode 100644
index 0000000..a1061fc
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/depends.py
@@ -0,0 +1,193 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""List all packages that depend on a atom given query"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit.dependencies import Dependencies
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import (get_cpvs, get_installed_cpvs,
+ compare_package_strings)
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "includeMasked": False,
+ "onlyDirect": True,
+ "maxDepth": -1,
+}
+
+# =======
+# Classes
+# =======
+
+class DependPrinter(object):
+ """Output L{gentoolkit.dependencies.Dependencies} objects."""
+ def __init__(self, verbose=True):
+ if verbose:
+ self.print_fn = self.print_verbose
+ else:
+ self.print_fn = self.print_quiet
+
+ def __call__(self, dep, dep_is_displayed=False):
+ self.format_depend(dep, dep_is_displayed)
+
+ @staticmethod
+ def print_verbose(indent, cpv, use_conditional, depatom):
+ """Verbosely prints a set of dep strings."""
+
+ sep = ' ? ' if (depatom and use_conditional) else ''
+ print indent + pp.cpv(cpv), "(" + use_conditional + sep + depatom + ")"
+
+ # W0613: *Unused argument %r*
+ # pylint: disable-msg=W0613
+ @staticmethod
+ def print_quiet(indent, cpv, use_conditional, depatom):
+ """Quietly prints a subset set of dep strings."""
+
+ print indent + pp.cpv(cpv)
+
+ def format_depend(self, dep, dep_is_displayed):
+ """Format a dependency for printing.
+
+ @type dep: L{gentoolkit.dependencies.Dependencies}
+ @param dep: the dependency to display
+ """
+
+ depth = getattr(dep, 'depth', 0)
+ indent = " " * depth
+ mdep = dep.matching_dep
+ use_conditional = ""
+ if mdep.use_conditional:
+ use_conditional = " & ".join(
+ pp.useflag(u) for u in mdep.use_conditional.split()
+ )
+ if mdep.operator == '=*':
+ formatted_dep = '=%s*' % str(mdep.cpv)
+ else:
+ formatted_dep = mdep.operator + str(mdep.cpv)
+ if mdep.slot:
+ formatted_dep += pp.emph(':') + pp.slot(mdep.slot)
+ if mdep.use:
+ useflags = pp.useflag(','.join(mdep.use.tokens))
+ formatted_dep += (pp.emph('[') + useflags + pp.emph(']'))
+
+ if dep_is_displayed:
+ indent = indent + " " * len(str(dep.cpv))
+ self.print_fn(indent, '', use_conditional, formatted_dep)
+ else:
+ self.print_fn(indent, str(dep.cpv), use_conditional, formatted_dep)
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ print mod_usage(mod_name="depends")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -a, --all-packages",
+ "include dependencies that are not installed (slow)"),
+ (" -D, --indirect",
+ "search both direct and indirect dependencies"),
+ (" --depth=N", "limit indirect dependency tree to specified depth")
+ ))
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ posargs = (x[1] for x in module_opts)
+ for opt, posarg in zip(opts, posargs):
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-a', '--all-packages'):
+ QUERY_OPTS['includeMasked'] = True
+ elif opt in ('-D', '--indirect'):
+ QUERY_OPTS['onlyDirect'] = False
+ elif opt in ('--depth'):
+ if posarg.isdigit():
+ depth = int(posarg)
+ else:
+ err = "Module option --depth requires integer (got '%s')"
+ sys.stdout.write(pp.error(err % posarg))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+ QUERY_OPTS["maxDepth"] = depth
+
+
+def main(input_args):
+ """Parse input and run the program"""
+ short_opts = "hadD" # -d, --direct was old option for default action
+ long_opts = ('help', 'all-packages', 'direct', 'indirect', 'depth=')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ #
+ # Output
+ #
+
+ dep_print = DependPrinter(verbose=CONFIG['verbose'])
+ first_run = True
+ for query in queries:
+ if not first_run:
+ print
+
+ pkg = Dependencies(query)
+ if QUERY_OPTS['includeMasked']:
+ pkggetter = get_cpvs
+ else:
+ pkggetter = get_installed_cpvs
+
+ if CONFIG['verbose']:
+ print " * These packages depend on %s:" % pp.emph(str(pkg.cpv))
+ pkg.graph_reverse_depends(
+ pkgset=sorted(pkggetter(), cmp=compare_package_strings),
+ max_depth=QUERY_OPTS["maxDepth"],
+ only_direct=QUERY_OPTS["onlyDirect"],
+ printer_fn=dep_print
+ )
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/depgraph.py b/gentoolkit/pym/gentoolkit/equery/depgraph.py
new file mode 100644
index 0000000..9c7e346
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/depgraph.py
@@ -0,0 +1,223 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Display a direct dependency graph for a given package"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from functools import partial
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "depth": 1,
+ "noAtom": False,
+ "noIndent": False,
+ "noUseflags": False,
+ "includeInstalled": True,
+ "includePortTree": True,
+ "includeOverlayTree": True,
+ "includeMasked": True,
+ "isRegex": False,
+ "matchExact": True,
+ "printMatchInfo": (not CONFIG['quiet'])
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ print "Default depth is set to 1 (direct only). Use --depth=0 for no max."
+ print
+ print mod_usage(mod_name="depgraph")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -A, --no-atom", "do not show dependency atom"),
+ (" -U, --no-useflags", "do not show USE flags"),
+ (" -l, --linear", "do not format the graph by indenting dependencies"),
+ (" --depth=N", "limit dependency graph to specified depth")
+ ))
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ posargs = (x[1] for x in module_opts)
+ for opt, posarg in zip(opts, posargs):
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ if opt in ('-A', '--no-atom'):
+ QUERY_OPTS["noAtom"] = True
+ if opt in ('-U', '--no-useflags'):
+ QUERY_OPTS["noUseflags"] = True
+ if opt in ('-l', '--linear'):
+ QUERY_OPTS["noIndent"] = True
+ if opt in ('--depth'):
+ if posarg.isdigit():
+ depth = int(posarg)
+ else:
+ err = "Module option --depth requires integer (got '%s')"
+ sys.stderr.write(pp.error(err % posarg))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+ QUERY_OPTS["depth"] = depth
+
+
+def depgraph_printer(
+ depth,
+ pkg,
+ dep,
+ no_use=False,
+ no_atom=False,
+ no_indent=False,
+ initial_pkg=False
+):
+ """Display L{gentoolkit.dependencies.Dependencies.graph_depends} results.
+
+ @type depth: int
+ @param depth: depth of indirection, used to calculate indent
+ @type pkg: L{gentoolkit.package.Package}
+ @param pkg: "best match" package matched by B{dep}
+ @type dep: L{gentoolkit.atom.Atom}
+ @param dep: dependency that matched B{pkg}
+ @type no_use: bool
+ @param no_use: don't output USE flags
+ @type no_atom: bool
+ @param no_atom: don't output dep atom
+ @type no_indent: bool
+ @param no_indent: don't output indent based on B{depth}
+ @type initial_pkg: bool
+ @param initial_pkg: somewhat of a hack used to print the root package of
+ the graph with absolutely no indent
+ """
+ indent = '' if no_indent or initial_pkg else ' ' + (' ' * depth)
+ decorator = '[%3d] ' % depth if no_indent else '`-- '
+ use = ''
+ try:
+ atom = '' if no_atom else ' (%s)' % dep.atom
+ if not no_use and dep is not None and dep.use:
+ use = ' [%s]' % ' '.join(
+ pp.useflag(x, enabled=True) for x in dep.use.tokens
+ )
+ except AttributeError:
+ # 'NoneType' object has no attribute 'atom'
+ atom = ''
+ try:
+ print ''.join((indent, decorator, pp.cpv(str(pkg.cpv)), atom, use))
+ except AttributeError:
+ # 'NoneType' object has no attribute 'cpv'
+ print ''.join((indent, decorator, "(no match for %r)" % dep.atom))
+
+
+def make_depgraph(pkg, printer_fn):
+ """Create and display depgraph for each package."""
+
+ if CONFIG['verbose']:
+ print " * direct dependency graph for %s:" % pp.cpv(str(pkg.cpv))
+ else:
+ print "%s:" % str(pkg.cpv)
+
+ # Print out the first package
+ printer_fn(0, pkg, None, initial_pkg=True)
+
+ deps = pkg.deps.graph_depends(
+ max_depth=QUERY_OPTS['depth'],
+ printer_fn=printer_fn,
+ # Use this to set this pkg as the graph's root; better way?
+ result=[(0, pkg)]
+ )
+
+ if CONFIG['verbose']:
+ pkgname = pp.cpv(str(pkg.cpv))
+ n_packages = pp.number(str(len(deps)))
+ max_seen = pp.number(str(max(x[0] for x in deps)))
+ info = "[ %s stats: packages (%s), max depth (%s) ]"
+ print info % (pkgname, n_packages, max_seen)
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "hAUl"
+ long_opts = ('help', 'no-atom', 'no-useflags', 'depth=')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ #
+ # Output
+ #
+
+ first_run = True
+ for query in queries:
+ if not first_run:
+ print
+
+ matches = do_lookup(query, QUERY_OPTS)
+
+ if not matches:
+ raise errors.GentoolkitNoMatches(query)
+
+ if CONFIG['verbose']:
+ printer = partial(
+ depgraph_printer,
+ no_atom=QUERY_OPTS['noAtom'],
+ no_indent=QUERY_OPTS['noIndent'],
+ no_use=QUERY_OPTS['noUseflags']
+ )
+ else:
+ printer = partial(
+ depgraph_printer,
+ no_atom=True,
+ no_indent=True,
+ no_use=True
+ )
+
+ for pkg in matches:
+ make_depgraph(pkg, printer)
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/files.py b/gentoolkit/pym/gentoolkit/equery/files.py
new file mode 100644
index 0000000..f0c40ee
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/files.py
@@ -0,0 +1,320 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""List files owned by a given package"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import portage
+
+import gentoolkit.pprinter as pp
+from gentoolkit.equery import (format_filetype, format_options, mod_usage,
+ CONFIG)
+from gentoolkit.helpers import do_lookup
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "categoryFilter": None,
+ "includeInstalled": True,
+ "includePortTree": False,
+ "includeOverlayTree": False,
+ "includeMasked": True,
+ "isRegex": False,
+ "matchExact": True,
+ "outputTree": False,
+ "printMatchInfo": (not CONFIG['quiet']),
+ "showType": False,
+ "showTimestamp": False,
+ "showMD5": False,
+ "typeFilter": None
+}
+
+FILTER_RULES = (
+ 'dir', 'obj', 'sym', 'dev', 'path', 'conf', 'cmd', 'doc', 'man', 'info'
+)
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ print mod_usage(mod_name="files")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -m, --md5sum", "include MD5 sum in output"),
+ (" -s, --timestamp", "include timestamp in output"),
+ (" -t, --type", "include file type in output"),
+ (" --tree", "display results in a tree (turns off other options)"),
+ (" -f, --filter=RULES", "filter output by file type"),
+ (" RULES",
+ "a comma-separated list (no spaces); choose from:")
+ ))
+ print " " * 24, ', '.join(pp.emph(x) for x in FILTER_RULES)
+
+
+# R0912: *Too many branches (%s/%s)*
+# pylint: disable-msg=R0912
+def display_files(contents):
+ """Display the content of an installed package.
+
+ @see: gentoolkit.package.Package.parsed_contents
+ @type contents: dict
+ @param contents: {'path': ['filetype', ...], ...}
+ """
+
+ filenames = contents.keys()
+ filenames.sort()
+ last = []
+
+ for name in filenames:
+ if QUERY_OPTS["outputTree"]:
+ dirdepth = name.count('/')
+ indent = " "
+ if dirdepth == 2:
+ indent = " "
+ elif dirdepth > 2:
+ indent = " " * (dirdepth - 1)
+
+ basename = name.rsplit("/", dirdepth - 1)
+ if contents[name][0] == "dir":
+ if len(last) == 0:
+ last = basename
+ print pp.path(indent + basename[0])
+ continue
+ for i, directory in enumerate(basename):
+ try:
+ if directory in last[i]:
+ continue
+ except IndexError:
+ pass
+ last = basename
+ if len(last) == 1:
+ print pp.path(indent + last[0])
+ continue
+ print pp.path(indent + "> /" + last[-1])
+ elif contents[name][0] == "sym":
+ print pp.path(indent + "+"),
+ print pp.path_symlink(basename[-1] + " -> " + contents[name][2])
+ else:
+ print pp.path(indent + "+ ") + basename[-1]
+ else:
+ print format_filetype(
+ name,
+ contents[name],
+ show_type=QUERY_OPTS["showType"],
+ show_md5=QUERY_OPTS["showMD5"],
+ show_timestamp=QUERY_OPTS["showTimestamp"]
+ )
+
+
+def filter_by_doc(contents, content_filter):
+ """Return a copy of content filtered by documentation."""
+
+ filtered_content = {}
+ for doctype in ('doc' ,'man' ,'info'):
+ # List only files from /usr/share/{doc,man,info}
+ if doctype in content_filter:
+ docpath = os.path.join(os.sep, 'usr', 'share', doctype)
+ for path in contents:
+ if contents[path][0] == 'obj' and path.startswith(docpath):
+ filtered_content[path] = contents[path]
+
+ return filtered_content
+
+
+def filter_by_command(contents):
+ """Return a copy of content filtered by executable commands."""
+
+ filtered_content = {}
+ userpath = os.environ["PATH"].split(os.pathsep)
+ userpath = [os.path.normpath(x) for x in userpath]
+ for path in contents:
+ if (contents[path][0] in ['obj', 'sym'] and
+ os.path.dirname(path) in userpath):
+ filtered_content[path] = contents[path]
+
+ return filtered_content
+
+
+def filter_by_path(contents):
+ """Return a copy of content filtered by file paths."""
+
+ filtered_content = {}
+ paths = list(reversed(sorted(contents.keys())))
+ while paths:
+ basepath = paths.pop()
+ if contents[basepath][0] == 'dir':
+ check_subdirs = False
+ for path in paths:
+ if (contents[path][0] != "dir" and
+ os.path.dirname(path) == basepath):
+ filtered_content[basepath] = contents[basepath]
+ check_subdirs = True
+ break
+ if check_subdirs:
+ while (paths and paths[-1].startswith(basepath)):
+ paths.pop()
+
+ return filtered_content
+
+
+def filter_by_conf(contents):
+ """Return a copy of content filtered by configuration files."""
+
+ filtered_content = {}
+ conf_path = portage.settings["CONFIG_PROTECT"].split()
+ conf_path = tuple(os.path.normpath(x) for x in conf_path)
+ conf_mask_path = portage.settings["CONFIG_PROTECT_MASK"].split()
+ conf_mask_path = tuple(os.path.normpath(x) for x in conf_mask_path)
+ for path in contents:
+ if contents[path][0] == 'obj' and path.startswith(conf_path):
+ if not path.startswith(conf_mask_path):
+ filtered_content[path] = contents[path]
+
+ return filtered_content
+
+
+def filter_contents(contents):
+ """Filter files by type if specified by the user.
+
+ @see: gentoolkit.package.Package.parsed_contents
+ @type contents: dict
+ @param contents: {'path': ['filetype', ...], ...}
+ @rtype: dict
+ @return: contents with unrequested filetypes stripped
+ """
+
+ if QUERY_OPTS['typeFilter']:
+ content_filter = QUERY_OPTS['typeFilter']
+ else:
+ return contents
+
+ filtered_content = {}
+ if frozenset(('dir', 'obj', 'sym', 'dev')).intersection(content_filter):
+ # Filter elements by type (as recorded in CONTENTS)
+ for path in contents:
+ if contents[path][0] in content_filter:
+ filtered_content[path] = contents[path]
+ if "cmd" in content_filter:
+ filtered_content.update(filter_by_command(contents))
+ if "path" in content_filter:
+ filtered_content.update(filter_by_path(contents))
+ if "conf" in content_filter:
+ filtered_content.update(filter_by_conf(contents))
+ if frozenset(('doc' ,'man' ,'info')).intersection(content_filter):
+ filtered_content.update(filter_by_doc(contents, content_filter))
+
+ return filtered_content
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ content_filter = []
+ opts = (x[0] for x in module_opts)
+ posargs = (x[1] for x in module_opts)
+ for opt, posarg in zip(opts, posargs):
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-e', '--exact-name'):
+ QUERY_OPTS["matchExact"] = True
+ elif opt in ('-m', '--md5sum'):
+ QUERY_OPTS["showMD5"] = True
+ elif opt in ('-s', '--timestamp'):
+ QUERY_OPTS["showTimestamp"] = True
+ elif opt in ('-t', '--type'):
+ QUERY_OPTS["showType"] = True
+ elif opt in ('--tree'):
+ QUERY_OPTS["outputTree"] = True
+ elif opt in ('-f', '--filter'):
+ f_split = posarg.split(',')
+ content_filter.extend(x.lstrip('=') for x in f_split)
+ for rule in content_filter:
+ if not rule in FILTER_RULES:
+ sys.stderr.write(
+ pp.error("Invalid filter rule '%s'" % rule)
+ )
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+ QUERY_OPTS["typeFilter"] = content_filter
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ # -e, --exact-name is legacy option. djanderson '09
+ short_opts = "hemstf:"
+ long_opts = ('help', 'exact-name', 'md5sum', 'timestamp', 'type', 'tree',
+ 'filter=')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ # Turn off filtering for tree output
+ if QUERY_OPTS["outputTree"]:
+ QUERY_OPTS["typeFilter"] = None
+
+ #
+ # Output files
+ #
+
+ first_run = True
+ for query in queries:
+ if not first_run:
+ print
+
+ matches = do_lookup(query, QUERY_OPTS)
+
+ if not matches:
+ sys.stderr.write(
+ pp.error("No matching packages found for %s" % query)
+ )
+
+ for pkg in matches:
+ if CONFIG['verbose']:
+ print " * Contents of %s:" % pp.cpv(str(pkg.cpv))
+
+ contents = pkg.parsed_contents()
+ display_files(filter_contents(contents))
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/hasuse.py b/gentoolkit/pym/gentoolkit/equery/hasuse.py
new file mode 100644
index 0000000..8d51013
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/hasuse.py
@@ -0,0 +1,156 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""List all installed packages that have a given USE flag"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup
+from gentoolkit.package import PackageFormatter
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "categoryFilter": None,
+ "includeInstalled": True,
+ "includePortTree": False,
+ "includeOverlayTree": False,
+ "includeMasked": True,
+ "isRegex": False, # Necessary for do_lookup, don't change
+ "printMatchInfo": False
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ print mod_usage(mod_name="hasuse", arg="USE-flag")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -I, --exclude-installed",
+ "exclude installed packages from search path"),
+ (" -o, --overlay-tree", "include overlays in search path"),
+ (" -p, --portage-tree", "include entire portage tree in search path")
+ ))
+
+
+def display_useflags(query, pkg):
+ """Display USE flag information for a given package."""
+
+ try:
+ useflags = [x.lstrip("+-") for x in pkg.environment("IUSE").split()]
+ except errors.GentoolkitFatalError:
+ # aux_get KeyError or other unexpected result
+ return
+
+ if query not in useflags:
+ return
+
+ if CONFIG['verbose']:
+ fmt_pkg = PackageFormatter(pkg, do_format=True)
+ else:
+ fmt_pkg = PackageFormatter(pkg, do_format=False)
+
+ if (QUERY_OPTS["includeInstalled"] and
+ not QUERY_OPTS["includePortTree"] and
+ not QUERY_OPTS["includeOverlayTree"]):
+ if not 'I' in fmt_pkg.location:
+ return
+ if (QUERY_OPTS["includePortTree"] and
+ not QUERY_OPTS["includeOverlayTree"]):
+ if not 'P' in fmt_pkg.location:
+ return
+ if (QUERY_OPTS["includeOverlayTree"] and
+ not QUERY_OPTS["includePortTree"]):
+ if not 'O' in fmt_pkg.location:
+ return
+ print fmt_pkg
+
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ # Parse module options
+ opts = (x[0] for x in module_opts)
+ for opt in opts:
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-I', '--exclue-installed'):
+ QUERY_OPTS['includeInstalled'] = False
+ elif opt in ('-p', '--portage-tree'):
+ QUERY_OPTS['includePortTree'] = True
+ elif opt in ('-o', '--overlay-tree'):
+ QUERY_OPTS['includeOverlayTree'] = True
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "hiIpo" # -i was option for default action
+ # --installed is no longer needed, kept for compatibility (djanderson '09)
+ long_opts = ('help', 'installed', 'exclude-installed', 'portage-tree',
+ 'overlay-tree')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ matches = do_lookup("*", QUERY_OPTS)
+ matches.sort()
+
+ #
+ # Output
+ #
+
+ first_run = True
+ for query in queries:
+ if not first_run:
+ print
+
+ if CONFIG['verbose']:
+ print " * Searching for USE flag %s ... " % pp.emph(query)
+
+ for pkg in matches:
+ display_useflags(query, pkg)
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/list_.py b/gentoolkit/pym/gentoolkit/equery/list_.py
new file mode 100644
index 0000000..3de8355
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/list_.py
@@ -0,0 +1,224 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""List installed packages matching the query pattern"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit
+import gentoolkit.pprinter as pp
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup, get_installed_cpvs
+from gentoolkit.package import Package, PackageFormatter
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "duplicates": False,
+ "includeInstalled": True,
+ "includePortTree": False,
+ "includeOverlayTree": False,
+ "includeMasked": True,
+ "includeMaskReason": False,
+ "isRegex": False,
+ "printMatchInfo": (not CONFIG['quiet'])
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+
+ # Deprecation warning added by djanderson, 12/2008
+ depwarning = (
+ "Default action for this module has changed in Gentoolkit 0.3.",
+ "Use globbing to simulate the old behavior (see man equery).",
+ "Use '*' to check all installed packages.",
+ "Use 'foo-bar/*' to filter by category."
+ )
+ for line in depwarning:
+ sys.stderr.write(pp.warn(line))
+ print
+
+ print mod_usage(mod_name="list")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -d, --duplicates", "list only installed duplicate packages"),
+ (" -f, --full-regex", "query is a regular expression"),
+ (" -m, --mask-reason", "include reason for package mask"),
+ (" -I, --exclude-installed",
+ "exclude installed packages from output"),
+ (" -o, --overlay-tree", "list packages in overlays"),
+ (" -p, --portage-tree", "list packages in the main portage tree")
+ ))
+
+
+def get_duplicates(matches):
+ """Return only packages that have more than one version installed."""
+
+ dups = {}
+ result = []
+ for pkg in matches:
+ if pkg.cpv.cp in dups:
+ dups[pkg.cpv.cp].append(pkg)
+ else:
+ dups[pkg.cpv.cp] = [pkg]
+
+ for cpv in dups.values():
+ if len(cpv) > 1:
+ result.extend(cpv)
+
+ return result
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ posargs = (x[1] for x in module_opts)
+ for opt, posarg in zip(opts, posargs):
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-I', '--exclude-installed'):
+ QUERY_OPTS['includeInstalled'] = False
+ elif opt in ('-p', '--portage-tree'):
+ QUERY_OPTS['includePortTree'] = True
+ elif opt in ('-o', '--overlay-tree'):
+ QUERY_OPTS['includeOverlayTree'] = True
+ elif opt in ('-f', '--full-regex'):
+ QUERY_OPTS['isRegex'] = True
+ elif opt in ('-m', '--mask-reason'):
+ QUERY_OPTS['includeMaskReason'] = True
+ elif opt in ('-e', '--exact-name'):
+ sys.stderr.write(pp.warn("-e, --exact-name is now default."))
+ sys.stderr.write(
+ pp.warn("Use globbing to simulate the old behavior.")
+ )
+ print
+ elif opt in ('-d', '--duplicates'):
+ QUERY_OPTS['duplicates'] = True
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "hdefiImop" # -i, -e were options for default actions
+
+ # 04/09: djanderson
+ # --all is no longer needed. Kept for compatibility.
+ # --installed is no longer needed. Kept for compatibility.
+ # --exact-name is no longer needed. Kept for compatibility.
+ long_opts = ('help', 'all', 'installed', 'exclude-installed',
+ 'mask-reason', 'portage-tree', 'overlay-tree', 'full-regex', 'exact-name',
+ 'duplicates')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ # Only search installed packages when listing duplicate packages
+ if QUERY_OPTS["duplicates"]:
+ QUERY_OPTS["includeInstalled"] = True
+ QUERY_OPTS["includePortTree"] = False
+ QUERY_OPTS["includeOverlayTree"] = False
+ QUERY_OPTS["includeMaskReason"] = False
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ first_run = True
+ for query in queries:
+ if not first_run:
+ print
+
+ matches = do_lookup(query, QUERY_OPTS)
+
+ # Find duplicate packages
+ if QUERY_OPTS["duplicates"]:
+ matches = get_duplicates(matches)
+
+ matches.sort()
+
+ #
+ # Output
+ #
+
+ for pkg in matches:
+ if CONFIG['verbose']:
+ pkgstr = PackageFormatter(pkg, do_format=True)
+ else:
+ pkgstr = PackageFormatter(pkg, do_format=False)
+
+ if (QUERY_OPTS["includeInstalled"] and
+ not QUERY_OPTS["includePortTree"] and
+ not QUERY_OPTS["includeOverlayTree"]):
+ if not 'I' in pkgstr.location:
+ continue
+ if (QUERY_OPTS["includePortTree"] and
+ not QUERY_OPTS["includeOverlayTree"]):
+ if not 'P' in pkgstr.location:
+ continue
+ if (QUERY_OPTS["includeOverlayTree"] and
+ not QUERY_OPTS["includePortTree"]):
+ if not 'O' in pkgstr.location:
+ continue
+ print pkgstr
+
+ if QUERY_OPTS["includeMaskReason"]:
+ ms_int, ms_orig = pkgstr.format_mask_status()
+ if not ms_int > 2:
+ # ms_int is a number representation of mask level.
+ # Only 2 and above are "hard masked" and have reasons.
+ continue
+ mask_reason = pkg.mask_reason()
+ if not mask_reason:
+ # Package not on system or not masked
+ continue
+ elif not any(mask_reason):
+ print " * No mask reason given"
+ else:
+ status = ', '.join(ms_orig)
+ explanation = mask_reason[0]
+ mask_location = mask_reason[1]
+ print " * Masked by %r" % status
+ print " * %s:" % mask_location
+ print '\n'.join(
+ [' * %s' % line.lstrip(' #')
+ for line in explanation.splitlines()]
+ )
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/meta.py b/gentoolkit/pym/gentoolkit/equery/meta.py
new file mode 100644
index 0000000..19c23a6
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/meta.py
@@ -0,0 +1,494 @@
+# Copyright 2009-2010 Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""Display metadata about a given package"""
+
+# Move to Imports section after Python-2.6 is stable
+from __future__ import with_statement
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import re
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import find_packages, print_sequence, print_file
+from gentoolkit.textwrap_ import TextWrapper
+
+# =======
+# Globals
+# =======
+
+# E1101: Module 'portage.output' has no $color member
+# portage.output creates color functions dynamically
+# pylint: disable-msg=E1101
+
+QUERY_OPTS = {
+ 'current': False,
+ 'description': False,
+ 'herd': False,
+ 'keywords': False,
+ 'maintainer': False,
+ 'useflags': False,
+ 'upstream': False,
+ 'xml': False
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True, with_usage=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ if with_usage:
+ print mod_usage(mod_name="meta")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -d, --description", "show an extended package description"),
+ (" -H, --herd", "show the herd(s) for the package"),
+ (" -k, --keywords", "show keywords for all matching package versions"),
+ (" -m, --maintainer", "show the maintainer(s) for the package"),
+ (" -u, --useflags", "show per-package USE flag descriptions"),
+ (" -U, --upstream", "show package's upstream information"),
+ (" -x, --xml", "show the plain metadata.xml file")
+ ))
+
+
+def filter_keywords(matches):
+ """Filters non-unique keywords per slot.
+
+ Does not filter arch mask keywords (-). Besides simple non-unique keywords,
+ also remove unstable keywords (~) if a higher version in the same slot is
+ stable. This view makes version bumps easier for package maintainers.
+
+ @type matches: array
+ @param matches: set of L{gentoolkit.package.Package} instances whose
+ 'key' are all the same.
+ @rtype: dict
+ @return: a dict with L{gentoolkit.package.Package} instance keys and
+ 'array of keywords not found in a higher version of pkg within the
+ same slot' values.
+ """
+ def del_archmask(keywords):
+ """Don't add arch_masked to filter set."""
+ return [x for x in keywords if not x.startswith('-')]
+
+ def add_unstable(keywords):
+ """Add unstable keyword for all stable keywords to filter set."""
+ result = list(keywords)
+ result.extend(
+ ['~%s' % x for x in keywords if not x.startswith(('-', '~'))]
+ )
+ return result
+
+ result = {}
+ slot_map = {}
+ # Start from the newest
+ rev_matches = reversed(matches)
+ for pkg in rev_matches:
+ keywords_str, slot = pkg.environment(('KEYWORDS', 'SLOT'),
+ prefer_vdb=False)
+ keywords = keywords_str.split()
+ result[pkg] = [x for x in keywords if x not in slot_map.get(slot, [])]
+ try:
+ slot_map[slot].update(del_archmask(add_unstable(keywords)))
+ except KeyError:
+ slot_map[slot] = set(del_archmask(add_unstable(keywords)))
+
+ return result
+
+
+def format_herds(herds):
+ """Format herd information for display."""
+
+ result = []
+ for herd in herds:
+ herdstr = ''
+ email = "(%s)" % herd[1] if herd[1] else ''
+ herdstr = herd[0]
+ if CONFIG['verbose']:
+ herdstr += " %s" % (email,)
+ result.append(herdstr)
+
+ return result
+
+
+def format_maintainers(maints):
+ """Format maintainer information for display."""
+
+ result = []
+ for maint in maints:
+ maintstr = ''
+ maintstr = maint.email
+ if CONFIG['verbose']:
+ maintstr += " (%s)" % (maint.name,) if maint.name else ''
+ maintstr += " - %s" % (maint.restrict,) if maint.restrict else ''
+ maintstr += "\n%s" % (
+ (maint.description,) if maint.description else ''
+ )
+ result.append(maintstr)
+
+ return result
+
+
+def format_upstream(upstream):
+ """Format upstream information for display."""
+
+ def _format_upstream_docs(docs):
+ result = []
+ for doc in docs:
+ doc_location = doc[0]
+ doc_lang = doc[1]
+ docstr = doc_location
+ if doc_lang is not None:
+ docstr += " (%s)" % (doc_lang,)
+ result.append(docstr)
+ return result
+
+ def _format_upstream_ids(ids):
+ result = []
+ for id_ in ids:
+ site = id_[0]
+ proj_id = id_[1]
+ idstr = "%s ID: %s" % (site, proj_id)
+ result.append(idstr)
+ return result
+
+ result = []
+ for up in upstream:
+ upmaints = format_maintainers(up.maintainers)
+ for upmaint in upmaints:
+ result.append(format_line(upmaint, "Maintainer: ", " " * 13))
+
+ for upchange in up.changelogs:
+ result.append(format_line(upchange, "ChangeLog: ", " " * 13))
+
+ updocs = _format_upstream_docs(up.docs)
+ for updoc in updocs:
+ result.append(format_line(updoc, "Docs: ", " " * 13))
+
+ for upbug in up.bugtrackers:
+ result.append(format_line(upbug, "Bugs-to: ", " " * 13))
+
+ upids = _format_upstream_ids(up.remoteids)
+ for upid in upids:
+ result.append(format_line(upid, "Remote-ID: ", " " * 13))
+
+ return result
+
+
+def format_useflags(useflags):
+ """Format USE flag information for display."""
+
+ result = []
+ for flag in useflags:
+ result.append(pp.useflag(flag.name))
+ result.append(flag.description)
+ result.append("")
+
+ return result
+
+
+def format_keywords(keywords):
+ """Sort and colorize keywords for display."""
+
+ result = []
+
+ for kw in sorted(keywords):
+ if kw.startswith('-'):
+ # arch masked
+ kw = pp.keyword(kw, stable=False, hard_masked=True)
+ elif kw.startswith('~'):
+ # keyword masked
+ kw = pp.keyword(kw, stable=False, hard_masked=False)
+ else:
+ # stable
+ kw = pp.keyword(kw, stable=True, hard_masked=False)
+ result.append(kw)
+
+ return ' '.join(result)
+
+
+def format_keywords_line(pkg, fmtd_keywords, slot, verstr_len):
+ """Format the entire keywords line for display."""
+
+ ver = pkg.fullversion
+ result = "%s:%s: %s" % (ver, pp.slot(slot), fmtd_keywords)
+ if CONFIG['verbose'] and fmtd_keywords:
+ result = format_line(fmtd_keywords, "%s:%s: " % (ver, pp.slot(slot)),
+ " " * (verstr_len + 2))
+
+ return result
+
+
+# R0912: *Too many branches (%s/%s)*
+# pylint: disable-msg=R0912
+def call_format_functions(matches):
+ """Call information gathering functions and display the results."""
+
+ # Choose a good package to reference metadata from
+ ref_pkg = get_reference_pkg(matches)
+
+ if CONFIG['verbose']:
+ repo = ref_pkg.repo_name()
+ print " * %s [%s]" % (pp.cpv(ref_pkg.cp), pp.section(repo))
+
+ got_opts = False
+ if any(QUERY_OPTS.values()):
+ # Specific information requested, less formatting
+ got_opts = True
+
+ if QUERY_OPTS["herd"] or not got_opts:
+ herds = format_herds(ref_pkg.metadata.herds(include_email=True))
+ if QUERY_OPTS["herd"]:
+ print_sequence(format_list(herds))
+ else:
+ for herd in herds:
+ print format_line(herd, "Herd: ", " " * 13)
+
+ if QUERY_OPTS["maintainer"] or not got_opts:
+ maints = format_maintainers(ref_pkg.metadata.maintainers())
+ if QUERY_OPTS["maintainer"]:
+ print_sequence(format_list(maints))
+ else:
+ if not maints:
+ print format_line([], "Maintainer: ", " " * 13)
+ else:
+ for maint in maints:
+ print format_line(maint, "Maintainer: ", " " * 13)
+
+ if QUERY_OPTS["upstream"] or not got_opts:
+ upstream = format_upstream(ref_pkg.metadata.upstream())
+ if QUERY_OPTS["upstream"]:
+ upstream = format_list(upstream)
+ else:
+ upstream = format_list(upstream, "Upstream: ", " " * 13)
+ print_sequence(upstream)
+
+ if not got_opts:
+ pkg_loc = ref_pkg.package_path()
+ print format_line(pkg_loc, "Location: ", " " * 13)
+
+ if QUERY_OPTS["keywords"] or not got_opts:
+ # Get {<Package 'dev-libs/glib-2.20.5'>: [u'ia64', u'm68k', ...], ...}
+ keyword_map = filter_keywords(matches)
+
+ for match in matches:
+ slot = match.environment('SLOT')
+ verstr_len = len(match.fullversion) + len(slot)
+ fmtd_keywords = format_keywords(keyword_map[match])
+ keywords_line = format_keywords_line(
+ match, fmtd_keywords, slot, verstr_len
+ )
+ if QUERY_OPTS["keywords"]:
+ print keywords_line
+ else:
+ indent = " " * (16 + verstr_len)
+ print format_line(keywords_line, "Keywords: ", indent)
+
+ if QUERY_OPTS["description"]:
+ desc = ref_pkg.metadata.descriptions()
+ print_sequence(format_list(desc))
+
+ if QUERY_OPTS["useflags"]:
+ useflags = format_useflags(ref_pkg.metadata.use())
+ print_sequence(format_list(useflags))
+
+ if QUERY_OPTS["xml"]:
+ print_file(os.path.join(ref_pkg.package_path(), 'metadata.xml'))
+
+
+def format_line(line, first="", subsequent="", force_quiet=False):
+ """Wrap a string at word boundaries and optionally indent the first line
+ and/or subsequent lines with custom strings.
+
+ Preserve newlines if the longest line is not longer than
+ CONFIG['termWidth']. To force the preservation of newlines and indents,
+ split the string into a list and feed it to format_line via format_list.
+
+ @see: format_list()
+ @type line: string
+ @param line: text to format
+ @type first: string
+ @param first: text to prepend to the first line
+ @type subsequent: string
+ @param subsequent: text to prepend to subsequent lines
+ @type force_quiet: boolean
+ @rtype: string
+ @return: A wrapped line
+ """
+
+ if line:
+ line = line.expandtabs().strip("\n").splitlines()
+ else:
+ if force_quiet:
+ return
+ else:
+ return first + "None specified"
+
+ if len(first) > len(subsequent):
+ wider_indent = first
+ else:
+ wider_indent = subsequent
+
+ widest_line_len = len(max(line, key=len)) + len(wider_indent)
+
+ if widest_line_len > CONFIG['termWidth']:
+ twrap = TextWrapper(width=CONFIG['termWidth'], expand_tabs=False,
+ initial_indent=first, subsequent_indent=subsequent)
+ line = " ".join(line)
+ line = re.sub("\s+", " ", line)
+ line = line.lstrip()
+ result = twrap.fill(line)
+ else:
+ # line will fit inside CONFIG['termWidth'], so preserve whitespace and
+ # newlines
+ line[0] = first + line[0] # Avoid two newlines if len == 1
+
+ if len(line) > 1:
+ line[0] = line[0] + "\n"
+ for i in range(1, (len(line[1:-1]) + 1)):
+ line[i] = subsequent + line[i] + "\n"
+ line[-1] = subsequent + line[-1] # Avoid two newlines on last line
+
+ if line[-1].isspace():
+ del line[-1] # Avoid trailing blank lines
+
+ result = "".join(line)
+
+ return result.encode("utf-8")
+
+
+def format_list(lst, first="", subsequent="", force_quiet=False):
+ """Feed elements of a list to format_line().
+
+ @see: format_line()
+ @type lst: list
+ @param lst: list to format
+ @type first: string
+ @param first: text to prepend to the first line
+ @type subsequent: string
+ @param subsequent: text to prepend to subsequent lines
+ @rtype: list
+ @return: list with element text wrapped at CONFIG['termWidth']
+ """
+
+ result = []
+ if lst:
+ # Format the first line
+ line = format_line(lst[0], first, subsequent, force_quiet)
+ result.append(line)
+ # Format subsequent lines
+ for elem in lst[1:]:
+ if elem:
+ result.append(format_line(elem, subsequent, subsequent,
+ force_quiet))
+ else:
+ # We don't want to send a blank line to format_line()
+ result.append("")
+ else:
+ if CONFIG['verbose']:
+ if force_quiet:
+ result = None
+ else:
+ # Send empty list, we'll get back first + `None specified'
+ result.append(format_line(lst, first, subsequent))
+
+ return result
+
+
+def get_reference_pkg(matches):
+ """Find a package in the Portage tree to reference."""
+
+ pkg = None
+ rev_matches = list(reversed(matches))
+ while rev_matches:
+ pkg = rev_matches.pop()
+ if not pkg.is_overlay():
+ break
+
+ return pkg
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ for opt in opts:
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-d', '--description'):
+ QUERY_OPTS["description"] = True
+ elif opt in ('-H', '--herd'):
+ QUERY_OPTS["herd"] = True
+ elif opt in ('-m', '--maintainer'):
+ QUERY_OPTS["maintainer"] = True
+ elif opt in ('-k', '--keywords'):
+ QUERY_OPTS["keywords"] = True
+ elif opt in ('-u', '--useflags'):
+ QUERY_OPTS["useflags"] = True
+ elif opt in ('-U', '--upstream'):
+ QUERY_OPTS["upstream"] = True
+ elif opt in ('-x', '--xml'):
+ QUERY_OPTS["xml"] = True
+
+
+def main(input_args):
+ """Parse input and run the program."""
+
+ short_opts = "hdHkmuUx"
+ long_opts = ('help', 'description', 'herd', 'keywords', 'maintainer',
+ 'useflags', 'upstream', 'xml')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ # Find queries' Portage directory and throw error if invalid
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ first_run = True
+ for query in queries:
+ matches = find_packages(query, include_masked=True)
+ if not matches:
+ raise errors.GentoolkitNoMatches(query)
+
+ if not first_run:
+ print
+
+ matches.sort()
+ call_format_functions(matches)
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/size.py b/gentoolkit/pym/gentoolkit/equery/size.py
new file mode 100644
index 0000000..4d2a686
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/size.py
@@ -0,0 +1,193 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Print total size of files contained in a given package"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+ "includeInstalled": True,
+ "includePortTree": False,
+ "includeOverlayTree": False,
+ "includeMasked": True,
+ "isRegex": False,
+ "matchExact": False,
+ "printMatchInfo": False,
+ "sizeInBytes": False
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+
+ # Deprecation warning added by djanderson, 12/2008
+ depwarning = (
+ "Default action for this module has changed in Gentoolkit 0.3.",
+ "Use globbing to simulate the old behavior (see man equery).",
+ "Use '*' to check all installed packages.",
+ "Use 'foo-bar/*' to filter by category."
+ )
+ for line in depwarning:
+ sys.stderr.write(pp.warn(line))
+ print
+
+ print mod_usage(mod_name="size")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -b, --bytes", "report size in bytes"),
+ (" -f, --full-regex", "query is a regular expression")
+ ))
+
+
+def display_size(match_set):
+ """Display the total size of all accessible files owned by packages.
+
+ @type match_set: list
+ @param match_set: package cat/pkg-ver strings
+ """
+
+ for pkg in match_set:
+ size, files, uncounted = pkg.size()
+
+ if CONFIG['verbose']:
+ print " * %s" % pp.cpv(str(pkg.cpv))
+ print "Total files : %s".rjust(25) % pp.number(str(files))
+
+ if uncounted:
+ print ("Inaccessible files : %s".rjust(25) %
+ pp.number(str(uncounted)))
+
+ if QUERY_OPTS["sizeInBytes"]:
+ size_str = pp.number(str(size))
+ else:
+ size_str = "%s %s" % format_bytes(size)
+
+ print "Total size : %s".rjust(25) % size_str
+ else:
+ info = "%s: total(%d), inaccessible(%d), size(%s)"
+ print info % (str(pkg.cpv), files, uncounted, size)
+
+
+def format_bytes(bytes_, precision=2):
+ """Format bytes into human-readable format (IEC naming standard).
+
+ @see: http://mail.python.org/pipermail/python-list/2008-August/503423.html
+ @rtype: tuple
+ @return: (str(num), str(label))
+ """
+
+ labels = (
+ (1<<40L, 'TiB'),
+ (1<<30L, 'GiB'),
+ (1<<20L, 'MiB'),
+ (1<<10L, 'KiB'),
+ (1, 'bytes')
+ )
+
+ if bytes_ == 0:
+ return (pp.number('0'), 'bytes')
+ elif bytes_ == 1:
+ return (pp.number('1'), 'byte')
+
+ for factor, label in labels:
+ if not bytes_ >= factor:
+ continue
+
+ float_split = str(bytes_/float(factor)).split('.')
+ integer = float_split[0]
+ decimal = float_split[1]
+ if int(decimal[0:precision]):
+ float_string = '.'.join([integer, decimal[0:precision]])
+ else:
+ float_string = integer
+
+ return (pp.number(float_string), label)
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ for opt in opts:
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-b', '--bytes'):
+ QUERY_OPTS["sizeInBytes"] = True
+ elif opt in ('-e', '--exact-name'):
+ sys.stderr.write(pp.warn("-e, --exact-name is now default."))
+ warning = pp.warn("Use globbing to simulate the old behavior.")
+ sys.stderr.write(warning)
+ print
+ elif opt in ('-f', '--full-regex'):
+ QUERY_OPTS['isRegex'] = True
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ # -e, --exact-name is no longer needed. Kept for compatibility.
+ # 04/09 djanderson
+ short_opts = "hbfe"
+ long_opts = ('help', 'bytes', 'full-regex', 'exact-name')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ first_run = True
+ for query in queries:
+ if not first_run:
+ print
+
+ matches = do_lookup(query, QUERY_OPTS)
+
+ if not matches:
+ sys.stderr.write(pp.error("No package found matching %s" % query))
+
+ display_size(matches)
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/uses.py b/gentoolkit/pym/gentoolkit/equery/uses.py
new file mode 100644
index 0000000..8358d49
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/uses.py
@@ -0,0 +1,317 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Display USE flags for a given package"""
+
+# Move to imports section when Python 2.6 is stable
+from __future__ import with_statement
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from functools import partial
+from getopt import gnu_getopt, GetoptError
+from glob import glob
+
+from portage import settings
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import find_best_match, find_packages, uniqify
+from gentoolkit.textwrap_ import TextWrapper
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {"allVersions" : False}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ print mod_usage(mod_name=__name__.split('.')[-1])
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -a, --all", "include all package versions")
+ ))
+
+
+def display_useflags(output):
+ """Print USE flag descriptions and statuses.
+
+ @type output: list
+ @param output: [(inuse, inused, flag, desc, restrict), ...]
+ inuse (int) = 0 or 1; if 1, flag is set in make.conf
+ inused (int) = 0 or 1; if 1, package is installed with flag enabled
+ flag (str) = the name of the USE flag
+ desc (str) = the flag's description
+ restrict (str) = corresponds to the text of restrict in metadata
+ """
+
+ maxflag_len = len(max([t[2] for t in output], key=len))
+
+ twrap = TextWrapper()
+ twrap.width = CONFIG['termWidth']
+ twrap.subsequent_indent = " " * (maxflag_len + 8)
+
+ markers = ("-", "+")
+ color = (
+ partial(pp.useflag, enabled=False), partial(pp.useflag, enabled=True)
+ )
+ for in_makeconf, in_installed, flag, desc, restrict in output:
+ if CONFIG['verbose']:
+ flag_name = ""
+ if in_makeconf != in_installed:
+ flag_name += pp.emph(" %s %s" %
+ (markers[in_makeconf], markers[in_installed]))
+ else:
+ flag_name += (" %s %s" %
+ (markers[in_makeconf], markers[in_installed]))
+
+ flag_name += " " + color[in_makeconf](flag.ljust(maxflag_len))
+ flag_name += " : "
+
+ # print description
+ if restrict:
+ restrict = "(%s %s)" % (pp.emph("Restricted to"),
+ pp.cpv(restrict))
+ twrap.initial_indent = flag_name
+ print twrap.fill(restrict)
+ if desc:
+ twrap.initial_indent = twrap.subsequent_indent
+ print twrap.fill(desc)
+ else:
+ print " : <unknown>"
+ else:
+ if desc:
+ twrap.initial_indent = flag_name
+ desc = twrap.fill(desc)
+ print desc
+ else:
+ twrap.initial_indent = flag_name
+ print twrap.fill("<unknown>")
+ else:
+ print markers[in_makeconf] + flag
+
+
+def get_global_useflags():
+ """Get global and expanded USE flag variables from
+ PORTDIR/profiles/use.desc and PORTDIR/profiles/desc/*.desc respectively.
+
+ @rtype: dict
+ @return: {'flag_name': 'flag description', ...}
+ """
+
+ global_usedesc = {}
+ # Get global USE flag descriptions
+ try:
+ path = os.path.join(settings["PORTDIR"], 'profiles', 'use.desc')
+ with open(path) as open_file:
+ for line in open_file:
+ if line.startswith('#'):
+ continue
+ # Ex. of fields: ['syslog', 'Enables support for syslog\n']
+ fields = line.split(" - ", 1)
+ if len(fields) == 2:
+ global_usedesc[fields[0]] = fields[1].rstrip()
+ except IOError:
+ sys.stderr.write(
+ pp.warn(
+ "Could not load USE flag descriptions from %s" % pp.path(path)
+ )
+ )
+
+ del path, open_file
+ # Add USE_EXPANDED variables to usedesc hash -- Bug #238005
+ for path in glob(os.path.join(settings["PORTDIR"],
+ 'profiles', 'desc', '*.desc')):
+ try:
+ with open(path) as open_file:
+ for line in open_file:
+ if line.startswith('#'):
+ continue
+ fields = [field.strip() for field in line.split(" - ", 1)]
+ if len(fields) == 2:
+ expanded_useflag = "%s_%s" % \
+ (path.split("/")[-1][0:-5], fields[0])
+ global_usedesc[expanded_useflag] = fields[1]
+ except IOError:
+ sys.stderr.write(
+ pp.warn("Could not load USE flag descriptions from %s" % path)
+ )
+
+ return global_usedesc
+
+
+def get_matches(query):
+ """Get packages matching query."""
+
+ if not QUERY_OPTS["allVersions"]:
+ matches = [find_best_match(query)]
+ if None in matches:
+ matches = find_packages(query, include_masked=False)
+ if matches:
+ matches.sort()
+ else:
+ matches = find_packages(query, include_masked=True)
+
+ if not matches:
+ raise errors.GentoolkitNoMatches(query)
+
+ return matches
+
+
+def get_output_descriptions(pkg, global_usedesc):
+ """Prepare descriptions and usage information for each USE flag."""
+
+ local_usedesc = pkg.metadata.use()
+ iuse = pkg.environment("IUSE")
+
+ if iuse:
+ usevar = uniqify([x.lstrip('+-') for x in iuse.split()])
+ usevar.sort()
+ else:
+ usevar = []
+
+ if pkg.is_installed():
+ used_flags = pkg.use().split()
+ else:
+ used_flags = settings["USE"].split()
+
+ # store (inuse, inused, flag, desc, restrict)
+ output = []
+ for flag in usevar:
+ inuse = False
+ inused = False
+
+ local_use = None
+ for use in local_usedesc:
+ if use.name == flag:
+ local_use = use
+ break
+
+ try:
+ desc = local_use.description
+ except AttributeError:
+ try:
+ desc = global_usedesc[flag]
+ except KeyError:
+ desc = ""
+
+ try:
+ restrict = local_use.restrict
+ restrict = restrict if restrict is not None else ""
+ except AttributeError:
+ restrict = ""
+
+ if flag in pkg.settings("USE").split():
+ inuse = True
+ if flag in used_flags:
+ inused = True
+
+ output.append((inuse, inused, flag, desc, restrict))
+
+ return output
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ for opt in opts:
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-a', '--all'):
+ QUERY_OPTS['allVersions'] = True
+
+
+def print_legend():
+ """Print a legend to explain the output format."""
+
+ print "[ Legend : %s - flag is set in make.conf ]" % pp.emph("U")
+ print "[ : %s - package is installed with flag ]" % pp.emph("I")
+ print "[ Colors : %s, %s ]" % (
+ pp.useflag("set", enabled=True), pp.useflag("unset", enabled=False))
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "ha"
+ long_opts = ('help', 'all')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ #
+ # Output
+ #
+
+ first_run = True
+ legend_printed = False
+ for query in queries:
+ if not first_run:
+ print
+
+ if CONFIG['verbose']:
+ print " * Searching for %s ..." % pp.pkgquery(query)
+
+ matches = get_matches(query)
+ matches.sort()
+
+ global_usedesc = get_global_useflags()
+ for pkg in matches:
+
+ output = get_output_descriptions(pkg, global_usedesc)
+ if output:
+ if CONFIG['verbose']:
+ if not legend_printed:
+ print_legend()
+ legend_printed = True
+ print (" * Found these USE flags for %s:" %
+ pp.cpv(str(pkg.cpv)))
+ print pp.emph(" U I")
+ display_useflags(output)
+ else:
+ if CONFIG['verbose']:
+ sys.stderr.write(
+ pp.warn("No USE flags found for %s" % pp.cpv(pkg.cpv))
+ )
+
+ first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/which.py b/gentoolkit/pym/gentoolkit/equery/which.py
new file mode 100644
index 0000000..be4f5e8
--- /dev/null
+++ b/gentoolkit/pym/gentoolkit/equery/which.py
@@ -0,0 +1,102 @@
+# Copyright(c) 2009-2010, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Display the path to the ebuild that would be used by Portage with the current
+configuration
+"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage
+from gentoolkit.helpers import find_packages
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {"includeMasked": False}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+ """Print description, usage and a detailed help message.
+
+ @type with_description: bool
+ @param with_description: if true, print module's __doc__ string
+ """
+
+ if with_description:
+ print __doc__.strip()
+ print
+ print mod_usage(mod_name="which")
+ print
+ print pp.command("options")
+ print format_options((
+ (" -h, --help", "display this help message"),
+ (" -m, --include-masked", "return highest version ebuild available")
+ ))
+
+
+def parse_module_options(module_opts):
+ """Parse module options and update QUERY_OPTS"""
+
+ opts = (x[0] for x in module_opts)
+ for opt in opts:
+ if opt in ('-h', '--help'):
+ print_help()
+ sys.exit(0)
+ elif opt in ('-m', '--include-masked'):
+ QUERY_OPTS['includeMasked'] = True
+
+
+def main(input_args):
+ """Parse input and run the program"""
+
+ short_opts = "hm"
+ long_opts = ('help', 'include-masked')
+
+ try:
+ module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+ except GetoptError, err:
+ sys.stderr.write(pp.error("Module %s" % err))
+ print
+ print_help(with_description=False)
+ sys.exit(2)
+
+ parse_module_options(module_opts)
+
+ if not queries:
+ print_help()
+ sys.exit(2)
+
+ for query in queries:
+
+ matches = find_packages(query, QUERY_OPTS['includeMasked'])
+ if matches:
+ pkg = sorted(matches).pop()
+ ebuild_path = pkg.ebuild_path()
+ if ebuild_path:
+ print os.path.normpath(ebuild_path)
+ else:
+ sys.stderr.write(
+ pp.warn("No ebuilds to satisfy %s" % pkg.cpv.name)
+ )
+ else:
+ raise errors.GentoolkitNoMatches(query)
+
+# vim: set ts=4 sw=4 tw=79: