aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/equery')
-rw-r--r--src/equery/AUTHORS3
-rw-r--r--src/equery/Makefile20
-rw-r--r--src/equery/README0
-rw-r--r--src/equery/TODO63
-rwxr-xr-xsrc/equery/equery1865
-rw-r--r--src/equery/equery.1278
-rw-r--r--src/equery/tests/common-functions.sh52
-rwxr-xr-xsrc/equery/tests/run-all-tests.sh12
-rw-r--r--src/equery/tests/test-belongs-help.out11
-rwxr-xr-xsrc/equery/tests/test-belongs.sh24
-rw-r--r--src/equery/tests/test-changes-help.out0
-rw-r--r--src/equery/tests/test-check-help.out3
-rwxr-xr-xsrc/equery/tests/test-check.sh39
-rw-r--r--src/equery/tests/test-depends-help.out8
-rwxr-xr-xsrc/equery/tests/test-depends.sh27
-rw-r--r--src/equery/tests/test-depgraph-help.out7
-rwxr-xr-xsrc/equery/tests/test-depgraph.sh27
-rw-r--r--src/equery/tests/test-files-help.out11
-rwxr-xr-xsrc/equery/tests/test-files.sh61
-rw-r--r--src/equery/tests/test-glsa-help.out0
-rw-r--r--src/equery/tests/test-hasuses-help.out9
-rw-r--r--src/equery/tests/test-help.out21
-rwxr-xr-xsrc/equery/tests/test-help.sh105
-rw-r--r--src/equery/tests/test-list-help.out9
-rwxr-xr-xsrc/equery/tests/test-list.sh40
-rw-r--r--src/equery/tests/test-size-help.out6
-rwxr-xr-xsrc/equery/tests/test-size.sh27
-rw-r--r--src/equery/tests/test-stats-help.out0
-rw-r--r--src/equery/tests/test-uses-help.out7
-rwxr-xr-xsrc/equery/tests/test-uses.sh39
-rw-r--r--src/equery/tests/test-which-help.out3
-rwxr-xr-xsrc/equery/tests/test-which.sh22
32 files changed, 2799 insertions, 0 deletions
diff --git a/src/equery/AUTHORS b/src/equery/AUTHORS
new file mode 100644
index 0000000..9935ef7
--- /dev/null
+++ b/src/equery/AUTHORS
@@ -0,0 +1,3 @@
+Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Initial version
+
diff --git a/src/equery/Makefile b/src/equery/Makefile
new file mode 100644
index 0000000..177427d
--- /dev/null
+++ b/src/equery/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2003 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+ echo "YADDLETHORPE (vb.) (Of offended pooves.) To exit huffily from a boutique."
+
+dist:
+ mkdir -p ../../$(distdir)/src/equery/
+ cp Makefile AUTHORS README TODO equery equery.1 ../../$(distdir)/src/equery/
+
+install:
+ install -m 0755 equery $(bindir)/
+ install -d $(docdir)/equery
+ install -m 0644 README AUTHORS $(docdir)/equery/
+ install -m 0644 equery.1 $(mandir)/
diff --git a/src/equery/README b/src/equery/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/equery/README
diff --git a/src/equery/TODO b/src/equery/TODO
new file mode 100644
index 0000000..5f38e60
--- /dev/null
+++ b/src/equery/TODO
@@ -0,0 +1,63 @@
+- sqlite
+ - regexp comparisons
+ - check /var/log/emerge.log for database up-to-dateness
+
+
+-------------------------
+- pkgquery language:
+ Query ::= NewStyle | OldStyle | with OldStyle NewStyle
+ NewStyle ::= NameVar in /RegExp/
+ | VerVar in [ VerExpr ]
+ | SetVar in [ SetExpr ]
+ NameVar ::= PC | PN | DESCRIPTION | SRC_URI | HOMEPAGE
+
+ SetVar ::= LICENSE | KEYWORDS | IUSE
+ VerVar ::= SLOT | PV | DEPEND | RDEPEND
+
+ BinaryOp ::= and | or
+ UnaryOp ::= not
+
+ VerExpr ::= SingleVer
+ | VerExpr BinOp VerExpr
+ | UnaryOp UnaryOp
+
+ SetExpr ::= Element
+ | Element BinOp Element
+ | UnaryOp Element
+
+ SingleVer ::= PrefixOp VersionBody ( VersionSuffix )? ( - Revision )?
+ PrefixOp ::= ! | < | > | <= | >= | = | ~
+ VersionBody ::= Number ( . Number )+ ( . * )?
+ VersionSuffix ::= _ ( pre | beta | alpha | rc ) Number?
+ | [a-z]
+ Revision ::= r Number
+
+------
+
+ PC in /dev-java/ and
+ PN in /ant/ and
+ PV in [ >=1.0 or <=2.3 and =2.0.* ] and
+ IUSE in [ java or junit ]
+
+
+--
+ with >=dev-java/ant-1.0*
+ IUSE in [ java or junit ] and
+ SLOT in [ >=1.0 ]
+
+
+----------
+
+old cruft:
+
+ SingleVer ::= ( Operator )? ( Category / ) PackageName ( - Version )?
+ Operator ::= = | > | >= | < | <= | ~ | !
+ Category ::= PackageName
+ PackageName ::= NamePart ( - NamePart )+
+ NamePart ::= [a-zA-Z+]+
+ Version ::= VersionPart ( - VersionPart )+ ( _ VersionSuffix )? ( - Revision )?
+ VersionSuffix ::= ( pre | rc | beta | alpha ) ( Number ) ?
+
+ old style: >=dev-java/ant-1.0*
+
+
diff --git a/src/equery/equery b/src/equery/equery
new file mode 100755
index 0000000..fd8fa4f
--- /dev/null
+++ b/src/equery/equery
@@ -0,0 +1,1865 @@
+#!/usr/bin/python
+#
+# Copyright 2003-2004 Karl Trygve Kalleberg
+# Copyright 2003-2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+# Author: Karl Trygve Kalleberg <karltk@gentoo.org>
+
+__author__ = "Karl Trygve Kalleberg"
+__email__ = "karltk@gentoo.org"
+__version__ = "0.1.4"
+__productname__ = "equery"
+__description__ = "Gentoo Package Query Tool"
+
+import os
+import re
+import sys
+import time
+from glob import glob
+
+# portage (output module) and gentoolkit need special path modifications
+sys.path.insert(0, "/usr/lib/gentoolkit/pym")
+
+import gentoolkit
+try:
+ import portage
+except ImportError:
+ sys.path.insert(0, "/usr/lib/portage/pym")
+ import portage
+
+try:
+ import portage.checksum as checksum
+ from portage.util import unique_array
+except ImportError:
+ import portage_checksum as checksum
+ from portage_util import unique_array
+
+import gentoolkit.pprinter as pp
+from gentoolkit.pprinter import print_info, print_error, print_warn, die
+
+# Auxiliary functions
+
+def fileAsStr(name, fdesc, showType=0, showMD5=0, showTimestamp=0):
+ """
+ Return file in fdesc as a filename
+ @param name:
+ @param fdesc:
+ @param showType:
+ @param showMD5:
+ @param showTimestamp:
+ @rtype: string
+ """
+ type = ""; fname = ""; stamp = ""; md5sum = ""
+
+ if fdesc[0] == 'obj':
+ type = "file"
+ fname = name
+ stamp = timestampAsStr(int(fdesc[1]))
+ md5sum = fdesc[2]
+ elif fdesc[0] == "dir":
+ type = "dir"
+ fname = pp.path(name)
+ elif fdesc[0] == "sym":
+ type = "symlink"
+ stamp = timestampAsStr(int(fdesc[1].replace(")","")))
+ tgt = fdesc[2].split()[0]
+ if Config["piping"]:
+ fname = name
+ else:
+ fname = pp.path_symlink(name + " -> " + tgt)
+ elif fdesc[0] == "fif":
+ type = "fifo"
+ fname = name
+ elif fdesc[0] == "dev":
+ type = "device"
+ fname = name
+ else:
+ raise Exception(name + " has unknown type: " + fdesc[0])
+
+ s = ""
+ if showType:
+ s += "%6s " % type
+ s += fname
+ if showTimestamp:
+ s += " " + stamp + " "
+ if showMD5:
+ s += " " + md5sum + " "
+ return s
+
+def timestampAsStr(timestamp):
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
+
+
+class Command:
+ """
+ Abstract root class for all equery commands
+ """
+ def __init__(self):
+ pass
+ def shortHelp(self):
+ """Return a help formatted to fit a single line, approx 70 characters.
+ Must be overridden in the subclass."""
+ return " - not implemented yet"
+ def longHelp(self):
+ """Return full, multiline, color-formatted help.
+ Must be overridden in the subclass."""
+ return "help for syntax and options"
+ def perform(self, args):
+ """Stub code for performing the command.
+ Must be overridden in the subclass"""
+ pass
+ def parseArgs(self, args):
+ """Stub code for parsing command line arguments for this command.
+ Must be overridden in the subclass."""
+ pass
+
+
+class CmdListFiles(Command):
+ """List files owned by a particular package"""
+ def __init__(self):
+ self.default_options = {
+ "showType": 0,
+ "showTimestamp": 0,
+ "showMD5": 0,
+ "tree": 0,
+ "filter": None
+ }
+
+ def parseArgs(self,args):
+ query = ""
+ need_help = 0
+ opts = self.default_options
+ for x in args:
+ if x in ["-h", "--help"]:
+ need_help = 1
+ elif x in ["--md5sum"]:
+ opts["showMD5"] = 1
+ elif x in ["--timestamp"]:
+ opts["showTimestamp"] = 1
+ elif x in ["--type"]:
+ opts["showType"] = 1
+ elif x in ["--tree"]:
+ opts["tree"] = 1
+ elif x[:9] == "--filter=":
+ opts["filter"] = x[9:].split(',')
+ elif x[0] == "/":
+ die(2, "The query '" + pp.pkgquery(x) + "' does not appear to be a valid package specification")
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def filterContents(self, cnt, filter):
+ if filter in [None,[]]:
+ return cnt
+
+ mycnt = {}
+
+ for mytype in filter:
+ # Filter elements by type (as recorded in CONTENTS).
+ if mytype in ["dir","obj","sym","dev","fif"]:
+ for mykey in cnt.keys():
+ if cnt[mykey][0] == mytype:
+ mycnt[mykey] = cnt[mykey]
+
+ if "cmd" in filter:
+ # List files that are in $PATH.
+ userpath = map(os.path.normpath,os.environ["PATH"].split(os.pathsep))
+ for mykey in cnt.keys():
+ if cnt[mykey][0] in ['obj','sym'] \
+ and os.path.dirname(mykey) in userpath:
+ mycnt[mykey] = cnt[mykey]
+
+ if "path" in filter:
+ # List only dirs where some files where actually installed,
+ # and also skip their subdirs.
+ mykeys = cnt.keys()
+ mykeys.sort()
+ while len(mykeys):
+ mykey = mykeys.pop(0)
+ if cnt[mykey][0] == 'dir':
+ i = 0
+ while i < len(mykeys) :
+ if cnt[mykeys[i]][0] != "dir" \
+ and os.path.dirname(mykeys[i]) == mykey:
+ mycnt[mykey] = cnt[mykey]
+ break
+ i += 1
+ if i < len(mykeys):
+ while len(mykeys) \
+ and len(mykey+"/") < len(mykeys[0]) \
+ and mykey+"/" == mykeys[0][:len(mykey)+1]:
+ mykeys.pop(0)
+
+ if "conf" in filter:
+ # List configuration files.
+ conf_path = gentoolkit.settings["CONFIG_PROTECT"].split()
+ conf_mask_path = gentoolkit.settings["CONFIG_PROTECT_MASK"].split()
+ conf_path = map(os.path.normpath, conf_path)
+ conf_mask_path = map(os.path.normpath, conf_mask_path)
+ for mykey in cnt.keys():
+ is_conffile = False
+ if cnt[mykey][0] == 'obj':
+ for conf_dir in conf_path:
+ if conf_dir+"/" == mykey[:len(conf_dir)+1]:
+ is_conffile = True
+ for conf_mask_dir in conf_mask_path:
+ if conf_mask_dir+"/" == mykey[:len(conf_mask_dir)+1]:
+ is_conffile = False
+ break
+ break
+ if is_conffile:
+ mycnt[mykey] = cnt[mykey]
+
+
+ for mydoctype in ["doc","man","info"]:
+ # List only files from /usr/share/{doc,man,info}
+ mydocpath = "/usr/share/"+mydoctype+"/"
+ if mydoctype in filter:
+ for mykey in cnt.keys():
+ if cnt[mykey][0] == 'obj' \
+ and mykey[:len(mydocpath)] == mydocpath :
+ mycnt[mykey] = cnt[mykey]
+
+ return mycnt
+
+ def perform(self, args):
+
+ (query, opts) = self.parseArgs(args)
+
+ # Turn off filtering for tree output
+ if opts["tree"]:
+ opts["filter"] = None
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
+
+ pkgs = gentoolkit.find_installed_packages(query, True)
+ for x in pkgs:
+ if not x.is_installed():
+ continue
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(1, pp.section("* ") + "Contents of " + pp.cpv(x.get_cpv()) + ":")
+
+ cnt = self.filterContents(x.get_contents(),opts["filter"])
+
+ filenames = cnt.keys()
+ filenames.sort()
+
+ last=[]
+ for name in filenames:
+ if not opts["tree"]:
+ print_info(0, fileAsStr(name,
+ cnt[name],
+ showType=opts["showType"],
+ showTimestamp=opts["showTimestamp"],
+ showMD5=opts["showMD5"]))
+ else:
+ c = name.split( "/" )[1:]
+ if cnt[name][0] == "dir":
+ if len(last) == 0:
+ last = c
+ print pp.path(" /" + c[0])
+ continue
+ numol = 0
+ for d in c:
+ if d in last:
+ numol = last.index(d) + 1
+ continue
+ last = c
+ if len(last) == 1:
+ print pp.path(" " + last[0])
+ continue
+ print pp.path(" " * ( numol * 3 ) + "> " + "/" + last[-1])
+ elif cnt[name][0] == "sym":
+ print pp.path(" " * ( bl * 3 ) + "+ ") + pp.path_symlink(c[-1] + " -> " + cnt[name][2])
+ else:
+ bl = len(last)
+ print pp.path(" " * ( bl * 3 ) + "+ ") + c[-1]
+
+ def longHelp(self):
+ return "List files owned by a particular package\n" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("files") + pp.localoption(" <local-opts> ") + pp.pkgquery("<cat/>packagename<-version>") + "\n" + \
+ "\n" + \
+ "Note: category and version parts are optional. \n" + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("--timestamp") + " - append timestamp\n" + \
+ " " + pp.localoption("--md5sum") + " - append md5sum\n" + \
+ " " + pp.localoption("--type") + " - prepend file type\n" + \
+ " " + pp.localoption("--tree") + " - display results in a tree (turns off other options)\n" + \
+ " " + pp.localoption("--filter=<rules>") + " - filter output\n" + \
+ " " + pp.localoption("<rules>") + " is a comma separated list of elements you want to see:\n" + \
+ " " + " " + pp.localoption("dir") + \
+ ", " + pp.localoption("obj") + \
+ ", " + pp.localoption("sym") + \
+ ", " + pp.localoption("dev") + \
+ ", " + pp.localoption("fifo") + \
+ ", " + pp.localoption("path") + \
+ ", " + pp.localoption("conf") + \
+ ", " + pp.localoption("cmd") + \
+ ", " + pp.localoption("doc") + \
+ ", " + pp.localoption("man") + \
+ ", " + pp.localoption("info")
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list files owned by " + pp.pkgquery("pkgspec")
+
+
+class CmdListBelongs(Command):
+ """List all packages owning a particular file"""
+ def __init__(self):
+ self.default_opts = {
+ "category": "*",
+ "fullRegex": 0,
+ "earlyOut": 0,
+ "nameOnly": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = []
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-c", "--category"]:
+ opts["category"] = args[i+1]
+ skip = 1
+ elif x in ["-e", "--earlyout"]:
+ opts["earlyOut"] = 1
+ elif x in ["-f", "--full-regex"]:
+ opts["fullRegex"] = 1
+ elif x in ["-n", "--name-only"]:
+ opts["nameOnly"] = 1
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query.append(x)
+
+ if need_help or query == []:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ if opts["fullRegex"]:
+ q = query
+ else:
+ # Trim trailing and multiple slashes from query
+ for i in range(0, len(query)):
+ query[i] = re.compile('/+').sub('/', query[i])
+ query[i] = query[i].rstrip('/')
+ q = map(lambda x: ((len(x) and x[0] == "/") and "^" or "/")
+ + re.escape(x) + "$", query)
+ try:
+ q = "|".join(q)
+ rx = re.compile(q)
+ except:
+ die(2, "The query '" + pp.regexpquery(q) + "' does not appear to be a valid regular expression")
+
+ # Pick out only selected categories
+ cat = opts["category"]
+ filter_fn = None
+ if cat != "*":
+ filter_fn = lambda x: x.find(cat+"/")==0
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for file(s) " + pp.regexpquery(",".join(query)) + " in " + pp.cpv(cat) + "... ]")
+
+ matches = portage.db["/"]["vartree"].dbapi.cpv_all()
+ #matches = gentoolkit.find_all_installed_packages(filter_fn)
+
+ found = 0
+
+ def dumpToPipe(pkg):
+ mysplit = pkg.split("/")
+ cnt = portage.dblink(mysplit[0], mysplit[1], "/", gentoolkit.settings).getcontents()
+ #cnt = pkg.get_contents()
+ if not cnt: return
+ for file in cnt.keys():
+ if rx.search(file) and (opts["category"] == "*" or portage.catpkgsplit(pkg)[0] == opts["category"]):
+ if opts["nameOnly"]:
+ x = portage.catpkgsplit(pkg)
+ print x[0]+"/"+x[1]
+ else:
+ print pkg
+ return
+
+ class DummyExp:
+ pass
+
+ def dumpToScreen(pkg):
+ mysplit = pkg.split("/")
+ cnt = portage.dblink(mysplit[0], mysplit[1], "/", gentoolkit.settings).getcontents()
+ #cnt = pkg.get_contents()
+ if not cnt: return
+ for file in cnt.keys():
+ if rx.search(file) and (opts["category"] == "*" or portage.catpkgsplit(pkg)[0] == opts["category"]):
+ if opts["nameOnly"]:
+ x = portage.catpkgsplit(pkg)
+ s = x[0]+"/"+x[1]
+ else:
+ s = pkg
+ s += " (" + pp.path(fileAsStr(file, cnt[file])) + ")"
+ print_info(0, s)
+ if opts["earlyOut"]:
+ raise DummyExp
+
+ try:
+ if Config["piping"]:
+ map(dumpToPipe, matches)
+ else:
+ map(dumpToScreen, matches)
+ except DummyExp:
+ pass
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.path("files...") + " - list all packages owning " + pp.path("files...")
+ def longHelp(self):
+ return "List all packages owning a particular set of files" + \
+ "\n" + \
+ "\n" + \
+ pp.emph("Note: ") + "Normally, only one package will own a file. If multiple packages own the same file, it usually consitutes a problem, and should be reported.\n" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("belongs") + pp.localoption(" <local-opts> ") + pp.path("filename") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-c, --category cat") + " - only search in category " + \
+ pp.pkgquery("cat") + "\n" + \
+ " " + pp.localoption("-f, --full-regex") + " - supplied query is a regex\n" + \
+ " " + pp.localoption("-e, --earlyout") + " - stop when first match is found\n" + \
+ " " + pp.localoption("-n, --name-only") + " - don't print the version."
+
+class CmdDisplayUSEs(Command):
+ """Advanced report of a package's USE flags"""
+ def __init__(self):
+ self.default_opts = {
+ "allPackages" : False
+ }
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-a", "--all"]:
+ opts["allPackages"] = True
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+
+ (query, opts) = self.parseArgs(args)
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
+
+ if not opts["allPackages"]:
+ matches = gentoolkit.find_installed_packages(query, True)
+ if not matches:
+ matches = gentoolkit.find_packages(query, False)
+ if matches:
+ matches = gentoolkit.sort_package_list(matches)
+ matches = matches[-1:]
+ else:
+ matches = gentoolkit.find_packages(query, True)
+
+ if not matches:
+ die(3, "No matching packages found for \"" + pp.pkgquery(query) + "\"")
+
+
+ useflags = gentoolkit.settings["USE"].split()
+ usedesc = {}
+ uselocaldesc = {}
+
+ # Load global USE flag descriptions
+ try:
+ fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.desc")
+ usedesc = {}
+ for line in fd.readlines():
+ if line[0] == "#":
+ continue
+ fields = line.split(" - ", 1)
+ if len(fields) == 2:
+ usedesc[fields[0].strip()] = fields[1].strip()
+ except IOError:
+ print_warn(5, "Could not load USE flag descriptions from " + ppath(gentoolkit.settings["PORTDIR"] + "/profiles/use.desc"))
+
+ # TODO: Add USE_EXPANDED variables to usedesc hash -- Bug #238005
+ # Pseudo-code:
+ # for all files in gentoolkit.settings["PORTDIR"]+"/desc/*.desc
+ # variable name = <filename>_<field1>
+ # description = <field 2>
+ for descfile in glob(gentoolkit.settings["PORTDIR"]+"/profiles/desc/*.desc"):
+ try:
+ fd = open(descfile)
+ for line in fd.readlines():
+ if line[0] == "#":
+ continue
+ fields = [field.strip() for field in line.split(" - ", 1)]
+ if len(fields) == 2:
+ usedesc["%s_%s" % (descfile.split("/")[-1][0:-5], fields[0],)] = fields[1]
+ except IOError:
+ print_warn(5, "Could not load USE flag descriptions from " + descfile)
+
+ # Load local USE flag descriptions
+ try:
+ fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.local.desc")
+ for line in fd.readlines():
+ if line[0] == "#":
+ continue
+ fields = line.split(" - ", 1)
+ if len(fields) == 2:
+ catpkguse = re.search("(.*):(.*)", fields[0])
+ if catpkguse:
+ if not uselocaldesc.has_key(catpkguse.group(1).strip()):
+ uselocaldesc[catpkguse.group(1).strip()] = {catpkguse.group(2).strip() : fields[1].strip()}
+ else:
+ uselocaldesc[catpkguse.group(1).strip()][catpkguse.group(2).strip()] = fields[1].strip()
+ except IOError:
+ print_warn(5, "Could not load USE flag descriptions from " + path(gentoolkit.settings["PORTDIR"] + "/profiles/use.local.desc"))
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Colour Code : " + pp.useflagon("set") + " " + pp.useflagoff("unset") + " ]")
+ print_info(3, "[ Legend : Left column (U) - USE flags from make.conf ]")
+ print_info(3, "[ : Right column (I) - USE flags packages was installed with ]")
+
+ # Iterate through matches, printing a report for each package
+ matches = gentoolkit.sort_package_list(matches)
+ matches_found = 0
+ for p in matches:
+
+ matches_found += 1
+
+ bestver = p.get_cpv()
+ iuse = p.get_env_var("IUSE")
+
+ if iuse:
+ # Fix Bug #91623 by making sure the list of USE flags is unique
+ # Added sort to make output prettier
+ usevar = unique_array(iuse.split())
+
+ # Remove prefixed +/- from flags in IUSE, Bug #232019
+ for i in range(len(usevar)):
+ if usevar[i][0] == "+" or usevar[i][0] == "-":
+ usevar[i] = usevar[i][1:]
+
+ usevar.sort()
+ else:
+ usevar = []
+
+ inuse = []
+ if p.is_installed():
+ used = p.get_use_flags().split()
+ else:
+ # cosmetic issue here as noninstalled packages don't have "used" flags
+ used = useflags
+
+ # store (inuse, inused, flag, desc)
+ output = []
+
+ for u in usevar:
+ inuse = 0
+ inused = 0
+ try:
+ desc = usedesc[u]
+ except KeyError:
+ try:
+ desc = uselocaldesc[p.get_category() + "/" + p.get_name()][u]
+ except KeyError:
+ desc = ""
+
+ if u in p.get_settings("USE").split():
+ inuse = 1
+ if u in used:
+ inused = 1
+
+ output.append((inuse, inused, u, desc))
+
+ # pretty print
+ if output:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(0, "[ Found these USE variables for " + pp.cpv(bestver) + " ]")
+ print_info(3, pp.emph(" U I"))
+ maxflag_len = 0
+ for inuse, inused, u, desc in output:
+ if len(u) > maxflag_len:
+ maxflag_len = len(u)
+
+ for in_makeconf, in_installed, flag, desc in output:
+ markers = ["-","+"]
+ colour = [pp.useflagoff, pp.useflagon]
+ if Config["piping"]:
+ print_info(0, markers[in_makeconf] + flag)
+ else:
+ if in_makeconf != in_installed:
+ print_info(0, pp.emph(" %s %s" % (markers[in_makeconf], markers[in_installed])), False)
+ else:
+ print_info(0, " %s %s" % (markers[in_makeconf], markers[in_installed]), False)
+
+ print_info(0, " " + colour[in_makeconf](flag.ljust(maxflag_len)), False)
+
+ # print description
+ if desc:
+ print_info(0, " : " + desc)
+ else:
+ print_info(0, " : <unknown>")
+ else:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(1, "[ No USE flags found for " + pp.cpv(p.get_cpv()) + "]")
+
+ if Config["verbosityLevel"] >= 2:
+ if matches_found == 0:
+ s = ""
+ die(3, "No " + s + "packages found for " + pp.pkgquery(query))
+
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - display USE flags for " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ return "Display USE flags for a given package\n" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("uses") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is: \n" + \
+ " " + pp.localoption("-a, --all") + " - include all package versions\n"
+
+
+class CmdDisplayDepGraph(Command):
+ """Display tree graph of dependencies for a query"""
+
+ def __init__(self):
+ self.default_opts = {
+ "displayUSEFlags": 1,
+ "fancyFormatting": 1,
+ "depth": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-U","--no-useflags"]:
+ opts["displayUSEFlags"] = 0
+ elif x in ["-l","--linear"]:
+ opts["fancyFormatting"] = 0
+ elif x[:8] == "--depth=":
+ opts["depth"] = int(x[8:])
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
+
+ matches = gentoolkit.find_packages(query, True)
+
+ for pkg in matches:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, pp.section("* ") + "dependency graph for " + pp.cpv(pkg.get_cpv()))
+ else:
+ print_info(0, pkg.get_cpv() + ":")
+
+ stats = { "maxdepth": 0, "packages": 0 }
+ self._graph(pkg, opts, stats, 0, [], "")
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(0, "[ " + pp.cpv(pkg.get_cpv()) + " stats: packages (" + pp.number(str(stats["packages"])) + \
+ "), max depth (" + pp.number(str(stats["maxdepth"])) + ") ]")
+
+ def _graph(self, pkg, opts, stats, level=0, pkgtbl=[], suffix=""):
+
+ stats["packages"] += 1
+ stats["maxdepth"] = max(stats["maxdepth"], level)
+
+ cpv = pkg.get_cpv()
+
+ pfx = ""
+ if opts["fancyFormatting"]:
+ pfx = level * " " + "`-- "
+ print_info(0, pfx + cpv + suffix)
+
+ pkgtbl.append(cpv)
+
+ pkgdeps = pkg.get_runtime_deps() + pkg.get_compiletime_deps() + pkg.get_postmerge_deps()
+ for x in pkgdeps:
+ suffix = ""
+ cpv = x[2]
+ pkg = gentoolkit.find_best_match(x[0] + cpv)
+ if not pkg:
+ print pfx + x[0] + cpv + " (unable to resolve to a package / package masked or removed)"
+ continue
+ if pkg.get_cpv() in pkgtbl:
+ continue
+ if cpv.find("virtual") == 0:
+ suffix += " (" + pp.cpv(cpv) + ")"
+ if len(x[1]) and opts["displayUSEFlags"]:
+ suffix += " [ " + pp.useflagon(' '.join(x[1])) + " ]"
+ if (level < opts["depth"] or opts["depth"] <= 0):
+ pkgtbl = self._graph(pkg, opts, stats, level+1, pkgtbl, suffix)
+ return pkgtbl
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - display a dependency tree for " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ return "Display a dependency tree for a given package\n" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("depgraph") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-U, --no-useflags") + " - do not show USE flags\n" + \
+ " " + pp.localoption("-l, --linear") + " - do not use fancy formatting\n" + \
+ " " + pp.localoption(" --depth=n") + " - limit dependency graph to specified depth"
+
+
+class CmdDisplaySize(Command):
+ """Display disk size consumed by a package"""
+ def __init__(self):
+ self.default_opts = {
+ "regex": 0,
+ "exact": 0,
+ "reportSizeInBytes": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-b","--bytes"]:
+ opts["reportSizeInBytes"] = 1
+ elif x in ["-f", "--full-regex"]:
+ opts["regex"] = 1
+ elif x in ["-e", "--exact-name"]:
+ opts["exact"] = 1
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+# if need_help or query == "":
+ if need_help:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ rev = ""
+ name = ""
+ ver = ""
+ cat = ""
+
+ if query != "":
+ (cat, name, ver, rev) = gentoolkit.split_package_name(query)
+ if rev == "r0": rev = ""
+
+ # replace empty strings with .* and escape regular expression syntax
+ if query != "":
+ if not opts["regex"]:
+ cat, name, ver, rev = [re.sub('^$', ".*", re.escape(x)) for x in cat, name, ver, rev]
+ else:
+ cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
+
+ try:
+ if opts["exact"]:
+ filter_fn = lambda x: re.match(cat+"/"+name, x)
+ else:
+ filter_fn = lambda x: re.match(cat+"/.*"+name, x)
+ matches = gentoolkit.find_all_installed_packages(filter_fn)
+ except:
+ die(2, "The query '" + pp.regexpquery(query) + "' does not appear to be a valid regular expression")
+ else:
+ cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
+ matches = gentoolkit.find_all_installed_packages()
+
+ matches = gentoolkit.sort_package_list(matches)
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
+
+ # If no version supplied, fix regular expression
+ if ver == ".*": ver = "[0-9]+[^-]*"
+
+ if rev != ".*": # revision supplied
+ ver = ver + "-" + rev
+
+ if opts["exact"]:
+ rx = re.compile(cat + "/" + name + "-" + ver)
+ else:
+ rx = re.compile(cat + "/.*" + name + ".*-" + ver)
+
+ for pkg in matches:
+ if rx.search(pkg.get_cpv()):
+ (size, files, uncounted) = pkg.size()
+
+ if Config["piping"]:
+ print_info(0, pkg.get_cpv() + ": total(" + str(files) + "), inaccessible(" + str(uncounted) + \
+ "), size(" + str(size) + ")")
+ else:
+ print_info(0, pp.section("* ") + "size of " + pp.cpv(pkg.get_cpv()))
+ print_info(0, " Total files : ".rjust(25) + pp.number(str(files)))
+
+ if uncounted:
+ print_info(0, " Inaccessible files : ".rjust(25) + pp.number(str(uncounted)))
+
+ sz = "%.2f KiB" % (size/1024.0)
+ if opts["reportSizeInBytes"]:
+ sz = pp.number(str(size)) + " bytes"
+
+ print_info(0, "Total size : ".rjust(25) + pp.number(sz))
+
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - print size of files contained in package " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ return "Print size total size of files contained in a given package" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("size") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is: \n" + \
+ " " + pp.localoption("-b, --bytes") + " - report size in bytes\n" \
+ " " + pp.localoption("-f, --full-regex") + " - query is a regular expression\n" + \
+ " " + pp.localoption("-e, --exact-name") + " - list only those packages that exactly match\n"
+
+class CmdDisplayChanges(Command):
+ """Display changes for pkgQuery"""
+ pass
+
+class CheckException:
+ def __init__(self, s):
+ self.s = s
+
+class CmdCheckIntegrity(Command):
+ """Check timestamps and md5sums for files owned by pkgspec"""
+ def __init__(self):
+ self.default_opts = {
+ "showSummary" : 1,
+ "showGoodFiles" : 0,
+ "showBadFiles" : 1,
+ "checkTimestamp" : 1,
+ "checkMD5sum": 1
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def getMD5sum(self, file):
+ return checksum.perform_md5(file, calc_prelink=1)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ if query == "":
+ matches=gentoolkit.find_all_installed_packages()
+ else:
+ matches = gentoolkit.find_packages(query, True)
+
+ matches = gentoolkit.sort_package_list(matches)
+
+ for pkg in matches:
+ if not pkg.is_installed():
+ continue
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(1, "[ Checking " + pp.cpv(pkg.get_cpv()) + " ]")
+ else:
+ print_info(0, pkg.get_cpv() + ":")
+
+ files = pkg.get_contents()
+ checked_files = 0
+ good_files = 0
+ for file in files.keys():
+ type = files[file][0]
+ try:
+ st = os.lstat(file)
+ if type == "dir":
+ if not os.path.isdir(file):
+ raise CheckException(file + " exists, but is not a directory")
+ elif type == "obj":
+ mtime = files[file][1]
+ md5sum = files[file][2]
+ if opts["checkMD5sum"]:
+ try:
+ actual_checksum = self.getMD5sum(file)
+ except:
+ raise CheckException("Failed to calculate MD5 sum for " + file)
+
+ if self.getMD5sum(file) != md5sum:
+ raise CheckException(file + " has incorrect md5sum")
+ if opts["checkTimestamp"]:
+ if int(st.st_mtime) != int(mtime):
+ raise CheckException(file + (" has wrong mtime (is %d, should be %s)" % (st.st_mtime, mtime)))
+ elif type == "sym":
+ # FIXME: nastry strippery; portage should have this fixed!
+ t = files[file][2]
+ # target = os.path.normpath(t.strip())
+ target = t.strip()
+ if not os.path.islink(file):
+ raise CheckException(file + " exists, but is not a symlink")
+ tgt = os.readlink(file)
+ if tgt != target:
+ raise CheckException(file + " does not point to " + target)
+ elif type == "fif":
+ pass
+ else:
+ pp.print_error(file)
+ pp.print_error(files[file])
+ pp.print_error(type)
+ raise CheckException(file + " has unknown type " + type)
+ good_files += 1
+ except CheckException, (e):
+ print_error(e.s)
+ except OSError:
+ print_error(file + " does not exist")
+ checked_files += 1
+ print_info(0, pp.section(" * ") + pp.number(str(good_files)) + " out of " + pp.number(str(checked_files)) + " files good")
+
+ def shortHelp(self):
+ return pp.pkgquery("pkgspec") + " - check MD5sums and timestamps of " + pp.pkgquery("pkgspec") + "'s files"
+ def longHelp(self):
+ return "Check package's files against recorded MD5 sums and timestamps" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("check") + pp.pkgquery(" pkgspec")
+
+class CmdDisplayStatistics(Command):
+ """Display statistics about installed and uninstalled packages"""
+ pass
+
+class CmdWhich(Command):
+ """Display the filename of the ebuild for a given package
+ that would be used by Portage with the current configuration."""
+ def __init__(self):
+ self.default_opts = {
+ "includeMasked": False
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-m", "--include-masked"]:
+ opts["includeMasked"] = True
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ matches = gentoolkit.find_packages(query, opts["includeMasked"])
+ matches = gentoolkit.sort_package_list(matches)
+
+ if matches:
+ pkg = matches[-1]
+ ebuild_path = pkg.get_ebuild_path()
+ if ebuild_path:
+ print_info(0, os.path.normpath(ebuild_path))
+ else:
+ print_warn("There are no ebuilds to satisfy %s" % pkg.get_name())
+ else:
+ print_error("No masked or unmasked packages found for " + pp.pkgquery(query))
+
+ def shortHelp(self):
+ return pp.pkgquery("pkgspec") + " - print full path to ebuild for package " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ # Not documenting --include-masked at this time, since I'm not sure that it is needed. - FuzzyRay
+ return "Print full path to ebuild for a given package" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("which ") + pp.pkgquery("pkgspec")
+
+class CmdListGLSAs(Command):
+ """List outstanding GLSAs."""
+ pass
+
+class CmdListDepends(Command):
+ """List all packages directly or indirectly depending on pkgQuery"""
+ def __init__(self):
+ self.default_opts = {
+ "onlyDirect": 1,
+ "onlyInstalled": 1,
+ "spacing": 0,
+ "depth": -1
+ }
+ # Used to cache and detect looping
+ self.pkgseen = []
+ self.pkglist = []
+ self.pkgdeps = {}
+ self.deppkgs = {}
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-d", "--direct"]:
+ opts["onlyDirect"] = 1
+ elif x in ["-D", "--indirect"]:
+ opts["onlyDirect"] = 0
+ elif x in ["-a", "--all-packages"]:
+ opts["onlyInstalled"] = 0
+ elif x[:10] == "--spacing=":
+ opts["spacing"] = int(x[10:])
+ elif x[:8] == "--depth=":
+ opts["depth"] = int(x[8:])
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print self.longHelp()
+ sys.exit(-1)
+ return (query, opts)
+
+ def perform(self, args):
+
+ (query, opts) = self.parseArgs(args)
+
+ # We call ourself recursively if --indirect specified. spacing is used to control printing the tree.
+ spacing = opts["spacing"]
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3 and not spacing:
+ print_info(3, "[ Searching for packages depending on " + pp.pkgquery(query) + "... ]")
+
+ isdepend = gentoolkit.split_package_name(query)
+ isdepends = map((lambda x: x.get_cpv()), gentoolkit.find_packages(query))
+ if not isdepends:
+ print_warn("Warning: No packages found matching %s" % query)
+
+ # Cache the list of packages
+ if not self.pkglist:
+ if opts["onlyInstalled"]:
+ packages = gentoolkit.find_all_installed_packages()
+ else:
+ packages = gentoolkit.find_all_packages()
+
+ packages = gentoolkit.sort_package_list(packages)
+ self.pkglist = packages
+ else:
+ packages = self.pkglist
+
+ for pkg in packages:
+ pkgcpv = pkg.get_cpv()
+ if not pkgcpv in self.pkgdeps:
+ try:
+ deps = pkg.get_runtime_deps() + pkg.get_compiletime_deps() + pkg.get_postmerge_deps()
+ except KeyError, e:
+ # If the ebuild is not found...
+ continue
+ # Remove duplicate deps
+ deps = unique_array(deps)
+ self.pkgdeps[pkgcpv] = deps
+ else:
+ deps = self.pkgdeps[pkgcpv]
+ isdep = 0
+ for dependency in deps:
+ # TODO determine if dependency is enabled by USE flag
+ # Find all packages matching the dependency
+ depstr = dependency[0]+dependency[2]
+ if not depstr in self.deppkgs:
+ try:
+ depcpvs = map((lambda x: x.get_cpv()), gentoolkit.find_packages(depstr))
+ self.deppkgs[depstr] = depcpvs
+ except KeyError, e:
+ print_warn("")
+ print_warn("Package: " + pkgcpv + " contains invalid dependency specification.")
+ print_warn("Portage error: " + str(e))
+ print_warn("")
+ continue
+ else:
+ depcpvs = self.deppkgs[depstr]
+ for x in depcpvs:
+ cpvs=gentoolkit.split_package_name(x)
+ if x in isdepends:
+ cat_match=1
+ name_match=1
+ ver_match=1
+ else:
+ cat_match=0
+ name_match=0
+ ver_match=0
+ # Match Category
+ if not isdepend[0] or cpvs[0] == isdepend[0]:
+ cat_match=1
+ # Match Name
+ if cpvs[1] == isdepend[1]:
+ name_match=1
+ # Match Version
+ if not isdepend[2] or ( cpvs[2] == isdepend[2] and (isdepend[3] \
+ or isdepend[3] == "r0" or cpvs[3] == isdepend[3])):
+ ver_match=1
+
+ if cat_match and ver_match and name_match:
+ if not isdep:
+ if dependency[1]:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print " " * (spacing * 2) + pp.cpv(pkg.get_cpv()),
+ print "(" + \
+ pp.useflag(" & ".join(dependency[1]) + "? ") + \
+ pp.pkgquery(dependency[0]+dependency[2]) + ")"
+ else:
+ print " " * (spacing * 2) + pkg.get_cpv()
+ else:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print " " * (spacing * 2) + pp.cpv(pkg.get_cpv()),
+ print "(" + pp.pkgquery(dependency[0]+dependency[2]) + ")"
+ else:
+ print " " * (spacing * 2) + pkg.get_cpv()
+ isdep = 1
+ elif not Config["piping"] and Config["verbosityLevel"] >= 3:
+ if dependency[1]:
+ print " "*len(pkg.get_cpv()) + " " * (spacing * 2) + \
+ " (" + pp.useflag("&".join(dependency[1]) + "? ") + \
+ pp.pkgquery(dependency[0]+dependency[2]) + ")"
+ else:
+ print " "*len(pkg.get_cpv()) + " " * (spacing * 2) + " (" + \
+ pp.pkgquery(dependency[0]+dependency[2]) + ")"
+
+ break
+
+ # if --indirect specified, call ourselves again with the dependency
+ # Do not call, if we have already called ourselves.
+ if isdep and not opts["onlyDirect"] and pkg.get_cpv() not in self.pkgseen \
+ and (spacing < opts["depth"] or opts["depth"] == -1):
+ self.pkgseen.append(pkg.get_cpv())
+ self.perform(['=' + pkg.get_cpv(), '--indirect', '--spacing=' + str(int(opts["spacing"]+1))])
+ opts["spacing"] = spacing;
+
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list all direct dependencies matching " + \
+ pp.pkgquery("pkgspec")
+
+ def longHelp(self):
+ return "List all direct dependencies matching a query pattern" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("depends") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-a, --all-packages") + " - search in all available packages (slow)\n" + \
+ " " + pp.localoption("-d, --direct") + " - search direct dependencies only (default)\n" + \
+ " " + pp.localoption("-D, --indirect") + " - search indirect dependencies (VERY slow)\n" + \
+ " " + pp.localoption(" --depth=n") + " - limit indirect dependency tree to specified depth"
+
+
+class CmdListPackages(Command):
+ """List packages satisfying pkgQuery"""
+ def __init__(self):
+ self.default_opts = {
+ "category": "*",
+ "includeInstalled": 1,
+ "includePortTree": 0,
+ "includeOverlayTree": 0,
+ "includeMasked": 1,
+ "regex": 0,
+ "exact": 0,
+ "duplicates": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-i", "--installed"]:
+ opts["includeInstalled"] = 1
+ elif x in ["-I", "--exclude-installed"]:
+ # If -I is the only option, warn
+ # (warning located in perform())
+ opts["includeInstalled"] = 0
+ elif x in ["-p", "--portage-tree"]:
+ opts["includePortTree"] = 1
+ elif x in ["-o", "--overlay-tree"]:
+ opts["includeOverlayTree"] = 1
+ elif x in ["-m", "--exclude-masked"]:
+ opts["includeMasked"] = 0
+ elif x in ["-f", "--full-regex"]:
+ opts["regex"] = 1
+ elif x in ["-e", "--exact-name"]:
+ opts["exact"] = 1
+ elif x in ["-d", "--duplicates"]:
+ opts["duplicates"] = 1
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ # Only search installed packages when listing duplicated packages
+ if opts["duplicates"]:
+ opts["includeInstalled"] = 1
+ opts["includePortTree"] = 0
+ opts["includeOverlayTree"] = 0
+
+ if need_help:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ rev = ""
+ name = ""
+ ver = ""
+ cat = ""
+
+ if query != "":
+ try: (cat, name, ver, rev) = gentoolkit.split_package_name(query)
+ except ValueError, e:
+ if str(e) == 'too many values to unpack':
+ print_error("A pattern to match against package names was expected, ")
+ warn_msg = "but %s has too many slashes ('/') to match any package."
+ die (1, warn_msg % query)
+ else: raise ValueError(e)
+ if rev == "r0": rev = ""
+
+ package_finder = None
+
+ if opts["includeInstalled"]:
+ if opts["includePortTree"] or opts["includeOverlayTree"]:
+ package_finder = gentoolkit.find_all_packages
+ else:
+ package_finder = gentoolkit.find_all_installed_packages
+ elif opts["includePortTree"] or opts["includeOverlayTree"]:
+ package_finder = gentoolkit.find_all_uninstalled_packages
+ else:
+ # -I was specified, and no selection of what packages to list was made
+ print_warn("With -I you must specify one of -i, -p or -o. Assuming -p")
+ opts["includePortTree"] = 1
+ package_finder = gentoolkit.find_all_uninstalled_packages
+
+ filter_fn = None
+
+ if Config["verbosityLevel"] >= 3:
+ scat = "'" + cat + "'"
+ if not cat:
+ scat = "all categories"
+ sname = "package '" + name + "'"
+ if not name:
+ sname = "all packages"
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(1, "[ Searching for " + pp.cpv(sname) + " in " + pp.cpv(scat) + " among: ]")
+
+ # replace empty strings with .* and escape regular expression syntax
+ if query != "":
+ if not opts["regex"]:
+ cat, name, ver, rev = [re.sub('^$', ".*", re.escape(x)) for x in cat, name, ver, rev]
+ else:
+ cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
+
+ try:
+ if opts["exact"]:
+ filter_fn = lambda x: re.match(cat+"/"+name, x)
+ else:
+ filter_fn = lambda x: re.match(cat+"/.*"+name, x)
+ matches = package_finder(filter_fn)
+ except:
+ die(2, "The query '" + pp.regexpquery(query) + "' does not appear to be a valid regular expression")
+ else:
+ cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
+ filter_fn = lambda x: True
+ matches = package_finder(filter_fn)
+
+ # Find duplicate packages
+ if opts["duplicates"]:
+ dups = {}
+ newmatches = []
+ for pkg in matches:
+ mykey = pkg.get_category() + "/" + pkg.get_name()
+ if dups.has_key(mykey):
+ dups[mykey].append(pkg)
+ else:
+ dups[mykey] = [pkg]
+
+ for mykey in dups.keys():
+ if len(dups[mykey]) > 1:
+ newmatches += dups[mykey]
+
+ matches = newmatches
+
+ matches = gentoolkit.sort_package_list(matches)
+
+ # If no version supplied, fix regular expression
+ if ver == ".*": ver = "[0-9]+[^-]*"
+
+ if rev != ".*": # revision supplied
+ ver = ver + "-" + rev
+
+ if opts["exact"]:
+ rx = re.compile(cat + "/" + name + "-" + ver)
+ else:
+ rx = re.compile(cat + "/.*" + name + ".*-" + ver)
+
+ if opts["includeInstalled"]:
+ self._print_installed(matches, rx)
+
+ if opts["includePortTree"]:
+ self._print_porttree(matches, rx)
+
+ if opts["includeOverlayTree"]:
+ self._print_overlay(matches, rx)
+
+ def _get_mask_status(self, pkg):
+ pkgmask = 0
+ if pkg.is_masked():
+ # Uncomment to only have package masked files show an 'M'
+ # maskreasons = portage.getmaskingstatus(pkg.get_cpv())
+ # if "package.mask" in maskreasons:
+ pkgmask = pkgmask + 3
+ keywords = pkg.get_env_var("KEYWORDS").split()
+ if gentoolkit.settings["ARCH"] not in keywords:
+ if "~" + gentoolkit.settings["ARCH"] in keywords:
+ pkgmask = pkgmask + 1
+ elif "-" + gentoolkit.settings["ARCH"] in keywords or "-*" in keywords:
+ pkgmask = pkgmask + 2
+ return pkgmask
+
+ def _generic_print(self, header, exclude, matches, rx, status):
+ if Config["verbosityLevel"] >= 3:
+ print_info(1, header)
+
+ pfxmodes = [ "---", "I--", "-P-", "--O" ]
+ maskmodes = [ " ", " ~", " -", "M ", "M~", "M-" ]
+
+ for pkg in matches:
+ if exclude(pkg):
+ continue
+
+ pkgmask = self._get_mask_status(pkg)
+
+ slot = pkg.get_env_var("SLOT")
+
+ if rx.search(pkg.get_cpv()):
+ if Config["piping"]:
+ print_info(0, pkg.get_cpv())
+ else:
+ print_info(0, "[" + pp.installedflag(pfxmodes[status]) + "] [" + pp.maskflag(maskmodes[pkgmask]) + "] " + pp.cpv(pkg.get_cpv()) + " (" + pp.slot(slot) + ")")
+
+ def _print_overlay(self, matches, rx):
+ self._generic_print(
+ pp.section(" *") + " overlay tree (" + pp.path(gentoolkit.settings["PORTDIR_OVERLAY"]) + ")",
+ lambda x: not x.is_overlay(),
+ matches, rx, 3)
+
+ def _print_porttree(self, matches, rx):
+ self._generic_print(
+ pp.section(" *") + " Portage tree (" + pp.path(gentoolkit.settings["PORTDIR"]) + ")",
+ lambda x: x.is_overlay() or x.is_installed(),
+ matches, rx, 2)
+
+ def _print_installed(self, matches, rx):
+ self._generic_print(
+ pp.section(" *") + " installed packages",
+ lambda x: not x.is_installed(),
+ matches, rx, 1)
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list all packages matching " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ return "List all packages matching a query pattern" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("list") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-i, --installed") + " - search installed packages (default)\n" + \
+ " " + pp.localoption("-I, --exclude-installed") + " - do not search installed packages\n" + \
+ " " + pp.localoption("-p, --portage-tree") + " - also search in portage tree (" + gentoolkit.settings["PORTDIR"] + ")\n" + \
+ " " + pp.localoption("-o, --overlay-tree") + " - also search in overlay tree (" + gentoolkit.settings["PORTDIR_OVERLAY"] + ")\n" + \
+ " " + pp.localoption("-f, --full-regex") + " - query is a regular expression\n" + \
+ " " + pp.localoption("-e, --exact-name") + " - list only those packages that exactly match\n" + \
+ " " + pp.localoption("-d, --duplicates") + " - list only installed duplicate packages\n"
+
+class CmdFindUSEs(Command):
+ """Find all packages with a particular USE flag."""
+ def __init__(self):
+ self.default_opts = {
+ "category": "*",
+ "includeInstalled": 1,
+ "includePortTree": 0,
+ "includeOverlayTree": 0,
+ "includeMasked": 1,
+ "regex": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-i", "--installed"]:
+ opts["includeInstalled"] = 1
+ elif x in ["-I", "--exclude-installed"]:
+ opts["includeInstalled"] = 0
+ elif x in ["-p", "--portage-tree"]:
+ opts["includePortTree"] = 1
+ elif x in ["-o", "--overlay-tree"]:
+ opts["includeOverlayTree"] = 1
+ elif x in ["-m", "--exclude-masked"]:
+ opts["includeMasked"] = 0
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ rev = ".*"
+ name = ".*"
+ ver = ".*"
+ cat = ".*"
+
+ package_finder = None
+
+ if opts["includeInstalled"] and (opts["includePortTree"] or opts["includeOverlayTree"]):
+ package_finder = gentoolkit.find_all_packages
+ elif opts["includeInstalled"]:
+ package_finder = gentoolkit.find_all_installed_packages
+ elif opts["includePortTree"] or opts["includeOverlayTree"]:
+ package_finder = gentoolkit.find_all_uninstalled_packages
+
+ if not package_finder:
+ die(2,"You must specify one of -i, -p or -o")
+
+ filter_fn = lambda x: True
+
+ if Config["verbosityLevel"] >= 3:
+ scat = "'" + cat + "'"
+ if cat == ".*":
+ scat = "all categories"
+ if not Config["piping"]:
+ print_info(2, "[ Searching for USE flag " + pp.useflag(query) + " in " + pp.cpv(scat) + " among: ]")
+ if opts["includeInstalled"]:
+ print_info(1, pp.section(" *") + " installed packages")
+ if opts["includePortTree"]:
+ print_info(1, pp.section(" *") + " Portage tree (" + pp.path(gentoolkit.settings["PORTDIR"]) + ")")
+ if opts["includeOverlayTree"]:
+ print_info(1, pp.section(" *") + " overlay tree (" + pp.path(gentoolkit.settings["PORTDIR_OVERLAY"]) + ")")
+
+ matches = package_finder(filter_fn)
+
+ rx = re.compile(cat + "/" + name + "-" + ver + "(-" + rev + ")?")
+ pfxmodes = [ "---", "I--", "-P-", "--O" ]
+ maskmodes = [ " ", " ~", " -", "M ", "M~", "M-" ]
+ for pkg in matches:
+ status = 0
+ if pkg.is_installed():
+ status = 1
+ elif pkg.is_overlay():
+ status = 3
+ else:
+ status = 2
+
+ useflags = pkg.get_env_var("IUSE").split()
+ useflags = [f.lstrip("+-") for f in useflags]
+
+ if query not in useflags:
+ continue
+
+ # Determining mask status
+ pkgmask = 0
+ if pkg.is_masked():
+ pkgmask = pkgmask + 3
+ keywords = pkg.get_env_var("KEYWORDS").split()
+ if "~"+gentoolkit.settings["ARCH"] in keywords:
+ pkgmask = pkgmask + 1
+ elif "-*" in keywords or "-"+gentoolkit.settings["ARCH"] in keywords:
+ pkgmask = pkgmask + 2
+
+ # Determining SLOT value
+ slot = pkg.get_env_var("SLOT")
+
+ if (status == 1 and opts["includeInstalled"]) or \
+ (status == 2 and opts["includePortTree"]) or \
+ (status == 3 and opts["includeOverlayTree"]):
+ if Config["piping"]:
+ print_info(0, pkg.get_cpv())
+ else:
+ print_info(0, "[" + pp.installedflag(pfxmodes[status]) + "] [" + pp.maskflag(maskmodes[pkgmask]) + "] " + pp.cpv(pkg.get_cpv()) + " (" + pp.slot(slot) + ")")
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("useflag") + " - list all packages with " + pp.pkgquery("useflag")
+ def longHelp(self):
+ return "List all packages with a particular USE flag" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("list") + pp.localoption(" <local-opts> ") + pp.pkgquery("useflag") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-i, --installed") + " - search installed packages (default)\n" + \
+ " " + pp.localoption("-I, --exclude-installed") + " - do not search installed packages\n" + \
+ " " + pp.localoption("-p, --portage-tree") + " - also search in portage tree (" + gentoolkit.settings["PORTDIR"] + ")\n" + \
+ " " + pp.localoption("-o, --overlay-tree") + " - also search in overlay tree (" + gentoolkit.settings["PORTDIR_OVERLAY"] + ")\n"
+
+#
+# Command line tokens to commands mapping
+#
+
+Known_commands = {
+ "list" : CmdListPackages(),
+ "files" : CmdListFiles(),
+ "belongs" : CmdListBelongs(),
+ "depends" : CmdListDepends(),
+ "hasuse" : CmdFindUSEs(),
+ "uses" : CmdDisplayUSEs(),
+ "depgraph" : CmdDisplayDepGraph(),
+ "changes" : CmdDisplayChanges(),
+ "size" : CmdDisplaySize(),
+ "check" : CmdCheckIntegrity(),
+ "stats" : CmdDisplayStatistics(),
+ "glsa" : CmdListGLSAs(),
+ "which": CmdWhich()
+ }
+
+# Short command line tokens
+
+Short_commands = {
+ "a" : "glsa",
+ "b" : "belongs",
+ "c" : "changes",
+ "d" : "depends",
+ "f" : "files",
+ "g" : "depgraph",
+ "h" : "hasuse",
+ "k" : "check",
+ "l" : "list",
+ "s" : "size",
+ "t" : "stats",
+ "u" : "uses",
+ "w" : "which"
+}
+
+from gentoolkit import Config
+
+Config = {
+ # Query will include packages installed on the system
+ "installedPackages": 1,
+ # Query will include packages available for installation
+ "uninstalledPackages": 0,
+ # Query will include overlay packages (iff uninstalledPackages==1)
+ "overlayPackages": 1,
+ # Query will include masked packages (iff uninstalledPackages==1)
+ "maskedPackages": 0,
+ # Query will only consider packages in the following categories, empty means all.
+ "categoryFilter": [],
+ # Enable color output (-1 = use Portage setting, 0 = force off, 1 = force on)
+ "color": -1,
+ # Level of detail on the output
+ "verbosityLevel": 3,
+ # Query will display info for multiple SLOTed versions
+ "considerDuplicates": 1,
+ # Are we writing to a pipe?
+ "piping": 0
+}
+
+def printVersion():
+ """Print the version of this tool to the console."""
+ print_info(0, __productname__ + "(" + __version__ + ") - " + \
+ __description__)
+ print_info(0, "Author(s): " + __author__)
+
+def buildReverseMap(m):
+ r = {}
+ for x in m.keys():
+ r[m[x]] = x
+ return r
+
+def printUsage():
+ """Print full usage information for this tool to the console."""
+
+ short_cmds = buildReverseMap(Short_commands);
+
+ print_info(0, pp.emph("Usage: ") + pp.productname(__productname__) + pp.globaloption(" <global-opts> ") + pp.command("command") + pp.localoption(" <local-opts>"))
+ print_info(0, "where " + pp.globaloption("<global-opts>") + " is one of")
+ print_info(0, pp.globaloption(" -q, --quiet") + " - minimal output")
+ print_info(0, pp.globaloption(" -C, --nocolor") + " - turn off colours")
+ print_info(0, pp.globaloption(" -h, --help") + " - this help screen")
+ print_info(0, pp.globaloption(" -V, --version") + " - display version info")
+ print_info(0, pp.globaloption(" -N, --no-pipe") + " - turn off pipe detection")
+
+ print_info(0, "where " + pp.command("command") + "(" + pp.command("short") + ") is one of")
+ keys = Known_commands.keys()
+ keys.sort()
+ for x in keys:
+ print_info(0, " " + pp.command(x) + "(" + pp.command(short_cmds[x]) + ") " + \
+ Known_commands[x].shortHelp())
+ print
+
+def configure():
+ """Set up default configuration.
+ """
+
+ # Guess colour output
+ if (Config["color"] == -1 and \
+ ((not sys.stdout.isatty()) or (gentoolkit.settings["NOCOLOR"] in ["yes","true"]))):
+ pp.output.nocolor()
+
+ # Guess piping output
+ if not sys.stdout.isatty():
+ Config["piping"] = True
+ else:
+ Config["piping"] = False
+
+
+def parseArgs(args):
+ """Parse tool-specific arguments.
+
+ Arguments are on the form equery <tool-specific> [command] <command-specific>
+
+ This function will only parse the <tool-specific> bit.
+ """
+ command = None
+ local_opts = []
+ showhelp = 0
+
+ def expand(x):
+ if x in Short_commands.keys():
+ return Short_commands[x]
+ return x
+
+ for i in xrange(len(args)):
+ x = args[i]
+ if 0:
+ pass
+ elif x in ["-h", "--help"]:
+ showhelp = True
+ elif x in ["-V", "--version"]:
+ printVersion()
+ sys.exit(0)
+ elif x in ["-C", "--nocolor"]:
+ Config["color"] = 0
+ pp.output.nocolor()
+ elif x in ["-N", "--no-pipe"]:
+ Config["piping"] = False
+ elif x in ["-q","--quiet"]:
+ Config["verbosityLevel"] = 0
+ elif expand(x) in Known_commands.keys():
+ command = Known_commands[expand(x)]
+ local_opts.extend(args[i+1:])
+ if showhelp:
+ local_opts.append("--help")
+ break
+ else:
+ print_warn("unknown global option %s, reusing as local option" % x)
+ local_opts.append(x)
+
+ if not command and showhelp:
+ printUsage()
+ sys.exit(0)
+
+ return (command, local_opts)
+
+if __name__ == "__main__":
+ configure()
+ (cmd, local_opts) = parseArgs(sys.argv[1:])
+ if cmd:
+ try:
+ cmd.perform(local_opts)
+ except KeyError, e:
+ if e and e[0].find("Specific key requires an operator") >= 0:
+ print_error("Invalid syntax: missing operator")
+ print_error("If you want only specific versions please use one of")
+ print_error("the following operators as prefix for the package name:")
+ print_error(" > >= = <= <")
+ print_error("Example to only match gcc versions greater or equal 3.2:")
+ print_error(" >=sys-devel/gcc-3.2")
+ print_error("")
+ print_error("Note: The symbols > and < are used for redirection in the shell")
+ print_error("and must be quoted if either one is used.")
+
+ else:
+ print_error("Internal portage error, terminating")
+ if len(e[0]):
+ print_error(str(e))
+ sys.exit(2)
+ except ValueError, e:
+ if isinstance(e[0], list):
+ print_error("Ambiguous package name " + pp.emph("\"" + local_opts[0] + "\""))
+ print_error("Please use one of the following long names:")
+ for p in e[0]:
+ print_error(" " + str(p))
+ else:
+ print_error("Internal portage error, terminating")
+ if len(e[0]):
+ print_error(str(e[0]))
+ sys.exit(2)
+ except KeyboardInterrupt:
+ print_info(0, "Interrupted by user, aborting.")
+ else:
+ print_error("No command or unknown command given")
+ printUsage()
+
diff --git a/src/equery/equery.1 b/src/equery/equery.1
new file mode 100644
index 0000000..27b8078
--- /dev/null
+++ b/src/equery/equery.1
@@ -0,0 +1,278 @@
+.TH "equery" "1" "Oct 2005" "gentoolkit" ""
+.SH "NAME"
+equery \- Gentoo: Package Query Tool
+.SH "SYNOPSIS"
+.B equery
+.I [global\-opts] command [local\-opts]
+.PP
+
+.SH "DESCRIPTION"
+equery is a flexible utility which may display various information about
+packages, such as the files they own, their USE flags, the md5sum
+of each file owned by a given package, and many other things.
+
+.SH "OPTIONS"
+The 'command' is the only mandatory option to equery. Most commands require
+a 'pkgspec' option, which is described by <cat/>packagename<\-version>;
+namely, the package name is mandatory, while the category and version are
+optional.
+
+[global\-opts] may be one of:
+
+.B \-q, \-\-quiet
+causes minimal output to be emitted
+.PP
+.B \-C, \-\-nocolor
+turns off colours
+.PP
+.B \-h, \-\-help
+displays a help summary
+.PP
+.B \-V, \-\-version
+displays the equery version
+.PP
+.B \-N, \-\-no\-pipe
+turns off pipe detection
+.PP
+
+Only one command will actually be run, at most. The possible commands are:
+.TP
+.B belongs <local\-opts> file
+This command lists all packages owning the specified file.
+.br
+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 (http://bugs.gentoo.org).
+.br
+.IP
+<local\-opts> is either or both of:
+.br
+.B \-c, \-\-category cat
+only search in category cat
+.br
+.B \-f, \-\-full\-regex
+supplied query is a regex
+.br
+.B \-e, \-\-earlyout
+stop when first match found
+
+.PP
+.B check pkgspec
+This command checks the files of the specified package against recorded MD5
+sums and timestamps.
+.PP
+.TP
+.B depends <local\-opts> pkgspec
+This command displays all dependencies matching pkgspec.
+.br
+<local\-opts> is either or both of:
+.br
+.B \-a, \-\-all\-packages
+search in all available packages (slow)
+.br
+.B \-d, \-\-direct
+search direct dependencies only (default)
+.br
+.B \-D, \-\-indirect
+search indirect dependencies (very slow)
+.br
+.B \-\-depth=n
+Limit depth of indirect dependency tree to n levels. Setting \-\-depth=0 is the same as not specifing \-\-indirect.
+.PP
+.TP
+.B depgraph <local\-opts> pkgspec
+This command display a dependency tree for pkgspec, by default indented to reflect
+how dependancies relate to each other.
+.br
+.IP
+<local\-opts> is either or both of:
+.br
+.B \-U, \-\-no\-useflags
+do not show USE flags.
+.br
+.B \-l, \-\-linear
+do not use fancy formatting
+.br
+.B \-\-depth=n
+Limit depth of dependency graph to n levels.
+.PP
+.TP
+.B files <local\-opts> pkgspec
+This lists files owned by a particular package, optionally with extra
+information specified by <local\-opts>
+.br
+
+<local\-opts> is any combination of:
+.br
+.B \-\-timestamp
+output the timestamp of each file
+.br
+.B \-\-md5sum
+output the md5sum of each file
+.br
+.B \-\-type
+output the type of each file
+.br
+.B \-\-tree
+display results in a tree (turns off all other options)
+.br
+.B \-\-filter=<rules>
+filter output based on files type or path
+.br
+.B \t<rules>
+is a comma separated list of filtering rules. Available rules are:
+.br
+.B \t\tdir\
+regular directories
+.br
+.B \t\tobj\
+regular files
+.br
+.B \t\tsym\
+symbolic links
+.br
+.B \t\tdev\
+device nodes
+.br
+.B \t\tfifo
+named pipes
+.br
+.B \t\tpath
+shortest paths where some files where installed
+.br
+.B \t\tconf
+configuration files (based on $CONFIG_PROTECT)
+.br
+.B \t\tcmd\
+user commands (based on $PATH)
+.br
+.B \t\tdoc\
+documentation files (from /usr/share/doc)
+.br
+.B \t\tman\
+manpages (from /usr/share/man)
+.br
+.B \t\tinfo
+info pages (from /usr/share/info)
+.PP
+.TP
+.B hasuse <local\-opts> useflag
+This command lists packages matching a particular USE flag in a user\-specified combination
+of installed packages, packages which are not installed, the portage tree, and
+the portage overlay tree.
+
+<local\-opts> must not include only \-I;
+if \-I is used, \-p and/or \-o must be also be present. By default, only installed
+packages are searched. \-o searches only the overlay tree [and possibly
+installed packages],
+.I not
+the main portage tree.
+
+.B \-i, \-\-installed
+search installed packages (default)
+.br
+.B \-I, \-\-exclude\-installed
+do not search installed packages
+.br
+.B \-p, \-\-portage\-tree
+also search in portage tree (/usr/portage)
+.br
+.B \-o, \-\-overlay\-tree
+also search in overlay tree (/usr/local/portage)
+.PP
+.TP
+.B list <local\-opts> pkgspec
+This command lists packages matching pkgspec in a user\-specified combination
+of installed packages, packages which are not installed, the portage tree, and
+the portage overlay tree. By default the list command searches for partial name matches.
+
+<local\-opts> \-I cannot be used by itself;
+if \-I is used, \-p and/or \-o must be also be present. By default, only installed
+packages are searched. \-o searches only the overlay tree [and possibly
+installed packages],
+\fInot\fR the main portage tree.
+
+.B \-i, \-\-installed
+search installed packages (default)
+.br
+.B \-I, \-\-exclude\-installed
+do not search installed packages
+.br
+.B \-p, \-\-portage\-tree
+also search in portage tree (/usr/portage)
+.br
+.B \-o, \-\-overlay\-tree
+also search in overlay tree (/usr/local/portage)
+.br
+.B \-f, \-\-full\-regex
+query is a regular expression
+.br
+.B \-e, \-\-exact\-name
+list only those packages that exactly match
+.br
+.B \-d, \-\-duplicates
+only list installed duplicate packages
+.br
+
+\fBOutput:\fR
+
+.br
+The list command searches packages for the name given. If found, the following info will be displayed: the package location between the first square brackets (I for Installed packages, P for Portage, O for Overlay), the possible masks between the second (~ by keyword, - by arch or M hard masked), then the category and complete name and last of all, the slot in which the package is stored.
+
+\fBExamples:\fR
+
+equery list zilla \- list all installed versions of packages containing the string 'zilla'
+
+equery list \-\-exact\-name x11\-libs/gtk+ \- list all installed versions of x11\-libs/gtk+
+
+equery list \-\-full\-regex '(mozilla\-firefox|mozilla\-thunderbird)' \- list all installed versions of mozilla\-firefox and mozilla\-thunderbird
+
+equery list \-\-duplicates \- list all installed slotted packages
+.PP
+.TP
+.B size <local\-opts> pkgspec
+This command outputs the number of files in the specified package, as well as
+their total size in an appropriate unit.
+
+The possible values for <local\-opts>, if specified, are:
+.br
+.B \-b, \-\-bytes
+report size in bytes
+.br
+.B \-f, \-\-full\-regex
+query is a regular expression
+.br
+.B \-e, \-\-exact\-name
+list only those packages that exactly match
+.PP
+.TP
+.B uses <local\-opts> pkgspec
+display USE flags for pkgspec.
+
+The only possible value for <local\-opts>, if specified, is:
+.br
+.B \-a, \-\-all
+include all package versions
+.PP
+.B which pkgspec
+print full path to ebuild for package pkgspec
+.PP
+
+.SH "Unimplemented Options"
+.PP
+.B changes
+.PP
+.B glsa \fR \- use glsa\-check for the time being.
+.PP
+.B stats
+
+
+
+.SH "BUGS"
+Many options aren't implemented. Command\-line parsing could use some work.
+.br
+Submit bug reports to http://bugs.gentoo.org
+.SH "AUTHORS"
+equery, original man page: Karl Trygve Kalleberg <karltk@gentoo.org>, 2003.
+.br
+Massive man page updates: Katerina Barone\-Adesi <katerinab@gmail.com>, 2004.
+
diff --git a/src/equery/tests/common-functions.sh b/src/equery/tests/common-functions.sh
new file mode 100644
index 0000000..f065a0a
--- /dev/null
+++ b/src/equery/tests/common-functions.sh
@@ -0,0 +1,52 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+function tempfilename() {
+ fn=$(date "+%s")
+ if [ ! -f ${fn}.tmp ] ; then
+ echo ${fn}.tmp
+ fi
+}
+
+function report_pass() {
+ printf "%-40s - passed\n" ${1}
+}
+
+function report_failure() {
+ printf "%-40s - FAILED!\n" ${1}
+}
+
+function assert_samefile() {
+ diff $2 $3 && report_pass $1 || report_failure $1
+}
+
+function assert_eq() {
+ if [ $2 -eq $3 ] ; then
+ report_pass $1
+ else
+ printf "FAIL: $2 ! -eq $3\n"
+ report_failure $1
+ fi
+}
+
+function assert_ge() {
+ if [ $2 -ge $3 ] ; then
+ report_pass $1
+ else
+ printf "FAIL: $2 ! -ge $3\n"
+ report_failure $1
+ fi
+}
+
+function assert_exists() {
+ if [ -f $2 ] ; then
+ report_pass $1
+ else
+ printf "FAIL: $2 does not exist\n"
+ report_failure $1
+ fi
+} \ No newline at end of file
diff --git a/src/equery/tests/run-all-tests.sh b/src/equery/tests/run-all-tests.sh
new file mode 100755
index 0000000..4fe2dfe
--- /dev/null
+++ b/src/equery/tests/run-all-tests.sh
@@ -0,0 +1,12 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+for x in belongs check depends depgraph files help list size uses which ; do
+ ./test-${x}.sh
+done
diff --git a/src/equery/tests/test-belongs-help.out b/src/equery/tests/test-belongs-help.out
new file mode 100644
index 0000000..0d2f583
--- /dev/null
+++ b/src/equery/tests/test-belongs-help.out
@@ -0,0 +1,11 @@
+List all packages owning a particular set of files
+
+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.
+
+Syntax:
+ belongs <local-opts> filename
+<local-opts> is either of:
+ -c, --category cat - only search in category cat
+ -f, --full-regex - supplied query is a regex
+ -e, --earlyout - stop when first match is found
+
diff --git a/src/equery/tests/test-belongs.sh b/src/equery/tests/test-belongs.sh
new file mode 100755
index 0000000..bd0eac4
--- /dev/null
+++ b/src/equery/tests/test-belongs.sh
@@ -0,0 +1,24 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_belongs() {
+ equery belongs $(which gcc) > ${tmpfile}
+
+ x=$(grep "gcc-config" ${tmpfile} | wc -l)
+
+ assert_eq ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+test_belongs
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-changes-help.out b/src/equery/tests/test-changes-help.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/equery/tests/test-changes-help.out
diff --git a/src/equery/tests/test-check-help.out b/src/equery/tests/test-check-help.out
new file mode 100644
index 0000000..1e6afcf
--- /dev/null
+++ b/src/equery/tests/test-check-help.out
@@ -0,0 +1,3 @@
+Check package's files against recorded MD5 sums and timestamps
+Syntax:
+ size pkgspec
diff --git a/src/equery/tests/test-check.sh b/src/equery/tests/test-check.sh
new file mode 100755
index 0000000..803299f
--- /dev/null
+++ b/src/equery/tests/test-check.sh
@@ -0,0 +1,39 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_check() {
+ equery check gcc > ${tmpfile}
+
+ x=$(grep "sys-devel/gcc" ${tmpfile} | wc -l)
+
+ assert_ge ${FUNCNAME} ${x} 1
+
+ x=$(egrep "[0-9]+ out of [0-9]+" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} ${x} 1
+}
+
+test_check_permissions() {
+ equery check sudo > ${tmpfile}
+
+ x=$(grep "app-admin/sudo" ${tmpfile} | wc -l)
+
+ assert_ge ${FUNCNAME} ${x} 1
+
+ x=$(egrep "[0-9]+ out of [0-9]+" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+test_check
+test_check_permissions
+
+rm -f ${tmpfile}
diff --git a/src/equery/tests/test-depends-help.out b/src/equery/tests/test-depends-help.out
new file mode 100644
index 0000000..356cd53
--- /dev/null
+++ b/src/equery/tests/test-depends-help.out
@@ -0,0 +1,8 @@
+List all direct dependencies matching a query pattern
+Syntax:
+ depends <local-opts> pkgspec
+<local-opts> is either of:
+ -d, --direct - search direct dependencies only (default)
+ -D, --indirect - search indirect dependencies (VERY slow)
+ -i, --only-installed - search installed in installed packages only
+
diff --git a/src/equery/tests/test-depends.sh b/src/equery/tests/test-depends.sh
new file mode 100755
index 0000000..e46d614
--- /dev/null
+++ b/src/equery/tests/test-depends.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_depends() {
+# equery skel gcc > ${tmpfile}
+
+# x=$(grep "app-shells/bash" ${tmpfile} | wc -l)
+ true
+# assert_eq ${FUNCNAME} ${x} 1
+
+# x=$(grep "virtual/libc" ${tmpfile} | wc -l)
+# assert_eq ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+#test_skel
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-depgraph-help.out b/src/equery/tests/test-depgraph-help.out
new file mode 100644
index 0000000..5b9fd22
--- /dev/null
+++ b/src/equery/tests/test-depgraph-help.out
@@ -0,0 +1,7 @@
+Display a dependency tree for a given package
+
+Syntax:
+ depgraph <local-opts> pkgspec
+<local-opts> is either of:
+ -U, --no-useflags - do not show USE flags
+ -l, --linear - do not use fancy formatting
diff --git a/src/equery/tests/test-depgraph.sh b/src/equery/tests/test-depgraph.sh
new file mode 100755
index 0000000..016bb37
--- /dev/null
+++ b/src/equery/tests/test-depgraph.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_depgraph() {
+ equery depgraph gcc > ${tmpfile}
+
+ x=$(grep "app-shells/bash" ${tmpfile} | wc -l)
+
+ assert_eq ${FUNCNAME} ${x} 1
+
+ x=$(grep "virtual/libc" ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+test_depgraph
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-files-help.out b/src/equery/tests/test-files-help.out
new file mode 100644
index 0000000..846151f
--- /dev/null
+++ b/src/equery/tests/test-files-help.out
@@ -0,0 +1,11 @@
+List files owned by a particular package
+
+Syntax:
+ files <local-opts> <cat/>packagename<-version>
+
+Note: category and version parts are optional.
+
+<local-opts> is either of:
+ --timestamp - append timestamp
+ --md5sum - append md5sum
+ --type - prepend file type
diff --git a/src/equery/tests/test-files.sh b/src/equery/tests/test-files.sh
new file mode 100755
index 0000000..ad0a5ea
--- /dev/null
+++ b/src/equery/tests/test-files.sh
@@ -0,0 +1,61 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+strip_versioned_files() {
+ grep -v "/usr/share/doc"
+}
+
+test_files() {
+ equery files bash > ${tmpfile}
+
+ x=$(grep man ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 5
+
+ x=$(cat ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 25
+}
+
+test_files_timestamp() {
+ equery files --timestamp bash > ${tmpfile}
+
+ x=$(grep "/bin/bash .*....-..-.. ..:..:.." ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} $x 1
+}
+
+test_files_md5sum() {
+ equery files --md5sum bash > ${tmpfile}
+
+ x=$(egrep "/bin/bash .*[0-9a-z]{30}" ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} $x 1
+}
+
+test_files_type() {
+
+ equery files --type bash > ${tmpfile}
+
+ x=$(grep "file.*/bin/bash$" ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} $x 1
+
+ x=$(grep "symlink.*/bin/rbash" ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} $x 1
+
+ x=$(grep "dir.*/usr/share/man" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 1
+}
+
+# Run tests
+
+test_files
+test_files_timestamp
+test_files_md5sum
+test_files_type
+
+rm ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-glsa-help.out b/src/equery/tests/test-glsa-help.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/equery/tests/test-glsa-help.out
diff --git a/src/equery/tests/test-hasuses-help.out b/src/equery/tests/test-hasuses-help.out
new file mode 100644
index 0000000..1a05645
--- /dev/null
+++ b/src/equery/tests/test-hasuses-help.out
@@ -0,0 +1,9 @@
+List all packages with a particular USE flag
+Syntax:
+ list <local-opts> useflag
+<local-opts> is either of:
+ -i, --installed - search installed packages (default)
+ -I, --exclude-installed - do not search installed packages
+ -p, --portage-tree - also search in portage tree (/usr/portage)
+ -o, --overlay-tree - also search in overlay tree (/usr/local/portage /usr/local/overlays/gentoo-boblycat)
+
diff --git a/src/equery/tests/test-help.out b/src/equery/tests/test-help.out
new file mode 100644
index 0000000..26517f4
--- /dev/null
+++ b/src/equery/tests/test-help.out
@@ -0,0 +1,21 @@
+Usage: equery <global-opts> command <local-opts>
+where <global-opts> is one of
+ -q, --quiet - minimal output
+ -C, --nocolor - turn off colours
+ -h, --help - this help screen
+ -V, --version - display version info
+where command(short) is one of
+ belongs(b) <local-opts> files... - list all packages owning files...
+ changes(c) - not implemented yet
+ check(k) pkgspec - check MD5sums and timestamps of pkgspec's files
+ depends(d) <local-opts> pkgspec - list all direct dependencies matching pkgspec
+ depgraph(g) <local-opts> pkgspec - display a dependency tree for pkgspec
+ files(f) <local-opts> pkgspec - list files owned by pkgspec
+ glsa(a) - not implemented yet
+ hasuses(h) <local-opts> pkgspec - list all packages with useflag
+ list(l) <local-opts> pkgspec - list all packages matching pkgspec
+ size(s) <local-opts> pkgspec - print size of files contained in package pkgspec
+ stats(t) - not implemented yet
+ uses(u) <local-opts> pkgspec - display USE flags for pkgspec
+ which(w) pkgspec - print full path to ebuild for package pkgspec
+
diff --git a/src/equery/tests/test-help.sh b/src/equery/tests/test-help.sh
new file mode 100755
index 0000000..618aac1
--- /dev/null
+++ b/src/equery/tests/test-help.sh
@@ -0,0 +1,105 @@
+#! /bin/sh
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_equery_help() {
+ equery --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-help.out
+
+}
+
+test_belongs_help() {
+ equery belongs --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-belongs-help.out
+}
+
+test_changes_help() {
+ equery changes --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-changes-help.out
+}
+
+test_check_help() {
+ equery check --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-check-help.out
+}
+
+test_depends_help() {
+ equery depends --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-depends-help.out
+}
+
+test_depgraph_help() {
+ equery depgraph --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-depgraph-help.out
+}
+
+test_files_help() {
+ equery files --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-files-help.out
+}
+
+test_glsa_help() {
+ equery glsa --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-glsa-help.out
+}
+
+test_hasuses_help() {
+ equery hasuses --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-hasuses-help.out
+}
+
+test_list_help() {
+ equery list --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-list-help.out
+}
+
+test_size_help() {
+ equery size --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-size-help.out
+}
+
+test_stats_help() {
+ equery stats --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-stats-help.out
+}
+
+test_uses_help() {
+ equery uses --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-uses-help.out
+}
+
+test_which_help() {
+ equery which --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-which-help.out
+}
+
+
+# run tests
+
+if [ "`hostname`" != "sky" ] ; then
+ echo "Testing framework is beta and machine dependent; some tests will fail!"
+fi
+
+test_equery_help
+test_belongs_help
+test_check_help
+test_changes_help
+test_depends_help
+test_depgraph_help
+test_files_help
+test_glsa_help
+test_hasuses_help
+test_list_help
+test_size_help
+test_stats_help
+test_uses_help
+test_which_help
+
+rm -f *.tmp \ No newline at end of file
diff --git a/src/equery/tests/test-list-help.out b/src/equery/tests/test-list-help.out
new file mode 100644
index 0000000..82d1fb0
--- /dev/null
+++ b/src/equery/tests/test-list-help.out
@@ -0,0 +1,9 @@
+List all packages matching a query pattern
+Syntax:
+ list <local-opts> pkgspec
+<local-opts> is either of:
+ -i, --installed - search installed packages (default)
+ -I, --exclude-installed - do not search installed packages
+ -p, --portage-tree - also search in portage tree (/usr/portage)
+ -o, --overlay-tree - also search in overlay tree (/usr/local/portage /usr/local/overlays/gentoo-boblycat)
+
diff --git a/src/equery/tests/test-list.sh b/src/equery/tests/test-list.sh
new file mode 100755
index 0000000..a43048b
--- /dev/null
+++ b/src/equery/tests/test-list.sh
@@ -0,0 +1,40 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_list() {
+ equery list > ${tmpfile}
+
+# should test tty output as well
+# pkgs=$(cat ${tmpfile} | wc -l)
+# x=$(grep "[I--]" ${tmpfile} | wc -l)
+# assert_eq ${FUNCNAME} ${pkgs} ${x}
+
+ x=$(grep "app-shells/bash" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 1
+}
+
+test_list_installed() {
+ test_list
+}
+
+test_list_portage_tree() {
+ equery list -I -p > ${tmpfile}
+}
+
+test_list_overlay_tree() {
+ equery list -I -o > ${tmpfile}
+}
+
+# Run tests
+
+test_list
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-size-help.out b/src/equery/tests/test-size-help.out
new file mode 100644
index 0000000..c0c63a4
--- /dev/null
+++ b/src/equery/tests/test-size-help.out
@@ -0,0 +1,6 @@
+Print size total size of files contained in a given package
+Syntax:
+ size <local-opts> pkgspec
+<local-opts> is either of:
+ -b, --bytes - report size in bytes
+
diff --git a/src/equery/tests/test-size.sh b/src/equery/tests/test-size.sh
new file mode 100755
index 0000000..126a5db
--- /dev/null
+++ b/src/equery/tests/test-size.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_size() {
+ equery size gcc > ${tmpfile}
+
+ x=$(grep "sys-devel/gcc" ${tmpfile} | wc -l)
+
+ assert_ge ${FUNCNAME} ${x} 1
+
+ x=$(egrep "size\([0-9]+\)" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+test_size
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-stats-help.out b/src/equery/tests/test-stats-help.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/equery/tests/test-stats-help.out
diff --git a/src/equery/tests/test-uses-help.out b/src/equery/tests/test-uses-help.out
new file mode 100644
index 0000000..d350e00
--- /dev/null
+++ b/src/equery/tests/test-uses-help.out
@@ -0,0 +1,7 @@
+Display USE flags for a given package
+
+Syntax:
+ uses <local-opts> pkgspec
+<local-opts> is either of:
+ -a, --all - include non-installed packages
+
diff --git a/src/equery/tests/test-uses.sh b/src/equery/tests/test-uses.sh
new file mode 100755
index 0000000..d694483
--- /dev/null
+++ b/src/equery/tests/test-uses.sh
@@ -0,0 +1,39 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_uses() {
+ equery uses gcc > ${tmpfile}
+
+ x=$(grep "static" ${tmpfile} | wc -l)
+
+ assert_eq ${FUNCNAME} ${x} 1
+
+ x=$(cat ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 7
+}
+
+test_uses_all() {
+ equery uses -a uclibc > ${tmpfile}
+
+ x=$(grep "static" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} ${x} 1
+
+ x=$(cat ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 5
+
+}
+
+# Run tests
+
+test_uses
+test_uses_all
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-which-help.out b/src/equery/tests/test-which-help.out
new file mode 100644
index 0000000..8bf337e
--- /dev/null
+++ b/src/equery/tests/test-which-help.out
@@ -0,0 +1,3 @@
+Print full path to ebuild for a given package
+Syntax:
+ size pkgspec
diff --git a/src/equery/tests/test-which.sh b/src/equery/tests/test-which.sh
new file mode 100755
index 0000000..491868f
--- /dev/null
+++ b/src/equery/tests/test-which.sh
@@ -0,0 +1,22 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_which() {
+ file=$(equery which gcc)
+
+ assert_exists ${FUNCNAME} ${file}
+}
+
+# Run tests
+
+test_which
+
+rm -f ${tmpfile} \ No newline at end of file