Update to genscripts rev 382. This has more fixes for py3k and the modular rewrite...
authorfuzzyray <fuzzyray@gentoo.org>
Fri, 12 Mar 2010 21:44:29 +0000 (21:44 -0000)
committerfuzzyray <fuzzyray@gentoo.org>
Fri, 12 Mar 2010 21:44:29 +0000 (21:44 -0000)
svn path=/trunk/gentoolkit/; revision=755

38 files changed:
TODO
bin/eclean
bin/equery
man/analyse.1
pym/gentoolkit/analyse/__init__.py
pym/gentoolkit/analyse/analyse.py
pym/gentoolkit/analyse/base.py
pym/gentoolkit/analyse/lib.py
pym/gentoolkit/analyse/rebuild.py
pym/gentoolkit/base.py
pym/gentoolkit/cpv.py
pym/gentoolkit/eclean/__init__.py [new file with mode: 0644]
pym/gentoolkit/eclean/clean.py [new file with mode: 0644]
pym/gentoolkit/eclean/cli.py [new file with mode: 0644]
pym/gentoolkit/eclean/exclude.py [new file with mode: 0644]
pym/gentoolkit/eclean/output.py [new file with mode: 0644]
pym/gentoolkit/eclean/pkgindex.py [new file with mode: 0644]
pym/gentoolkit/eclean/search.py [new file with mode: 0644]
pym/gentoolkit/equery/__init__.py
pym/gentoolkit/equery/changes.py
pym/gentoolkit/equery/list_.py
pym/gentoolkit/equery/meta.py
pym/gentoolkit/equery/uses.py
pym/gentoolkit/errors.py
pym/gentoolkit/helpers.py
pym/gentoolkit/keyword.py
pym/gentoolkit/metadata.py
pym/gentoolkit/package.py
pym/gentoolkit/query.py
pym/gentoolkit/test/eclean/Packages [new file with mode: 0644]
pym/gentoolkit/test/eclean/__init__.py [new file with mode: 0644]
pym/gentoolkit/test/eclean/creator.py [new file with mode: 0644]
pym/gentoolkit/test/eclean/test_clean.py [new file with mode: 0644]
pym/gentoolkit/test/eclean/test_search.py [new file with mode: 0644]
pym/gentoolkit/test/test_helpers.py
pym/gentoolkit/test/test_keyword.py
pym/gentoolkit/test/test_query.py [new file with mode: 0644]
setup.py

diff --git a/TODO b/TODO
index f4d4124c43dbdc2164f197fd35082f4584e63caa..997919ece204f30e904c28b3d2efca9f5296670f 100644 (file)
--- a/TODO
+++ b/TODO
@@ -3,15 +3,11 @@
  - add glsa pkgspec
   - query for current GLSAs on installed package(s)
 - rewrite ekeywords and echangelog to use gentoolkit
-- fully deprecate qpkg
-- fully deprecate pkg-size
 - merge change and echangelog
 - merge useflag and euse, have _one_ command line tool
   - update ufed to rely on the CLI tool
   - update ufed to rely on generate-use
   - merge generate-use and ufed?
-- rewrite revdep-rebuild to use gentoolkit
- - drop qpkg dependency; use equery instead
 - write efeatures for turning on/off FEATURES in make.conf
 - look at ekeys, ewatch
 - revision bump tool
@@ -27,43 +23,13 @@ equery (modern):
        Tighten up CPV.split_cpv, it's slow and bad
        Extend PackageFormatter usage to everything that outputs packages to
          allow for purvasive use of -F, --format goodness
-       Add package::repo search syntax to do_lookup
-         _do_repository_lookup?
-       Move do_lookout and all it's silly friends into the new query module
-         and Query class. Essentially, Query, when applied to a pkgspec input
-         should contain most of the common 'helper' methods. So we should be
-         be able to do:
-           Query('portage').find_best(),
-               Query('portage').find_package(),
-               Query('portag*').is_regex() or .uses_globbing(), etc.
-       Refactor each module to be useful for import. Done modules:
-               +depends
-               +belongs
-               +meta
-               +changes
-               +depgraph
 
 Ebuild changes:
        - Add:
                src_test() {
                        "${python}" setup.py test || die "testing returned non zero"
                }
-       - Add:
-               DEPEND on python 2.5 (needed for 'from __future__ import with_statement' and others)
 
 For Next Release:
        - write NEWS file
        - make CPV.__init__ more strict, it allows some silly stuff
-       -  $ equery uses '>=sys-apps/portage-2'
-         * Searching for >=sys-apps/portage-2 ...
-         * Found these USE flags for sys-apps/portage-2.1.6.13:
-       - belongs doesn't properly match atom syntax
-
-
-For following release:
-       - transition package query backend to using Query class.
-         Query class should accept any kind package of input accepted by equery.
-         Most of the functions in helpers should be able to moved out, either in
-         to query (many of those functions pertain to finding packages matching a
-         query) or into other appropriate modules (split_cpv,
-         compare_package_strings into cpv, as they also requires a cpv string)
index 2d7f09cfeb66ae0a41d0f77f8cf790b2c9504bac..e17f9f0ba8685f6c53f8a3550951fe89ff4f6cf6 100755 (executable)
 #!/usr/bin/python
-# Copyright 2003-2005 Gentoo Foundation
-# Distributed under the terms of the GNU General Public License v2
-# $Header: $
+
+"""Copyright 2003-2010 Gentoo Foundation
+Distributed under the terms of the GNU General Public License v2
+"""
 
 from __future__ import print_function
 
 
-###############################################################################
 # Meta:
-__author__ = "Thomas de Grenier de Latour (tgl)"
-__email__ = "degrenier@easyconnect.fr"
+__author__ = "Thomas de Grenier de Latour (tgl), " + \
+       "modular re-write by: Brian Dolbec (dol-sen)"
+__email__ = "degrenier@easyconnect.fr, " + \
+       "brian.dolbec@gmail.com"
 __version__ = "svn"
 __productname__ = "eclean"
 __description__ = "A cleaning tool for Gentoo distfiles and binaries."
 
 
-###############################################################################
-# Python imports:
-
 import sys
-import stat
-import re
-import time
-import getopt
-import signal
-
-import portage
-from portage.output import *
-from portage import os
-
-from gentoolkit.helpers import walk
 
-listdir = portage.listdir
+# This block ensures that ^C interrupts are handled quietly.
+try:
+       import signal
 
-###############################################################################
-# Misc. shortcuts to some portage stuff:
-port_settings = portage.settings
-distdir = port_settings["DISTDIR"]
-pkgdir = port_settings["PKGDIR"]
+       def exithandler(signum,frame):
+               signal.signal(signal.SIGINT, signal.SIG_IGN)
+               signal.signal(signal.SIGTERM, signal.SIG_IGN)
+               print()
+               sys.exit(1)
 
+       signal.signal(signal.SIGINT, exithandler)
+       signal.signal(signal.SIGTERM, exithandler)
+       signal.signal(signal.SIGPIPE, signal.SIG_DFL)
 
-###############################################################################
-# printVersion:
-def printVersion():
-       print("%s (%s) - %s" \
-                       % (__productname__, __version__, __description__))
+except KeyboardInterrupt:
        print()
-       print("Author: %s <%s>" % (__author__,__email__))
-       print("Copyright 2003-2009 Gentoo Foundation")
-       print("Distributed under the terms of the GNU General Public License v2")
-
-
-###############################################################################
-# printUsage: print help message. May also print partial help to stderr if an
-# error from {'options','actions'} is specified.
-def printUsage(error=None,help=None):
-       out = sys.stdout
-       if error: out = sys.stderr
-       if not error in ('actions', 'global-options', \
-                       'packages-options', 'distfiles-options', \
-                       'merged-packages-options', 'merged-distfiles-options', \
-                       'time', 'size'):
-               error = None
-       if not error and not help: help = 'all'
-       if error == 'time':
-               eerror("Wrong time specification")
-               print("Time specification should be an integer followed by a"+ \
-                               " single letter unit.", file=out)
-               print("Available units are: y (years), m (months), w (weeks), "+ \
-                               "d (days) and h (hours).", file=out)
-               print("For instance: \"1y\" is \"one year\", \"2w\" is \"two"+ \
-                               " weeks\", etc. ", file=out)
-               return
-       if error == 'size':
-               eerror("Wrong size specification")
-               print("Size specification should be an integer followed by a"+ \
-                               " single letter unit.", file=out)
-               print("Available units are: G, M, K and B.", file=out)
-               print("For instance: \"10M\" is \"ten megabytes\", \"200K\" "+ \
-                               "is \"two hundreds kilobytes\", etc.", file=out)
-               return
-       if error in ('global-options', 'packages-options', 'distfiles-options', \
-                       'merged-packages-options', 'merged-distfiles-options',):
-               eerror("Wrong option on command line.")
-               print(file=out)
-       elif error == 'actions':
-               eerror("Wrong or missing action name on command line.")
-               print(file=out)
-       print(white("Usage:"), file=out)
-       if error in ('actions','global-options', 'packages-options', \
-       'distfiles-options') or help == 'all':
-               print(" "+turquoise(__productname__), \
-                       yellow("[global-option] ..."), \
-                       green("<action>"), \
-                       yellow("[action-option] ..."), file=out)
-       if error == 'merged-distfiles-options' or help in ('all','distfiles'):
-               print(" "+turquoise(__productname__+'-dist'), \
-                       yellow("[global-option, distfiles-option] ..."), file=out)
-       if error == 'merged-packages-options' or help in ('all','packages'):
-               print(" "+turquoise(__productname__+'-pkg'), \
-                       yellow("[global-option, packages-option] ..."), file=out)
-       if error in ('global-options', 'actions'):
-               print(" "+turquoise(__productname__), \
-                       yellow("[--help, --version]"), file=out)
-       if help == 'all':
-               print(" "+turquoise(__productname__+"(-dist,-pkg)"), \
-                       yellow("[--help, --version]"), file=out)
-       if error == 'merged-packages-options' or help == 'packages':
-               print(" "+turquoise(__productname__+'-pkg'), \
-                       yellow("[--help, --version]"), file=out)
-       if error == 'merged-distfiles-options' or help == 'distfiles':
-               print(" "+turquoise(__productname__+'-dist'), \
-                       yellow("[--help, --version]"), file=out)
-       print(file=out)
-       if error in ('global-options', 'merged-packages-options', \
-       'merged-distfiles-options') or help:
-               print("Available global", yellow("options")+":", file=out)
-               print(yellow(" -C, --nocolor")+ \
-                       "             - turn off colors on output", file=out)
-               print(yellow(" -d, --destructive")+ \
-                       "         - only keep the minimum for a reinstallation", file=out)
-               print(yellow(" -e, --exclude-file=<path>")+ \
-                       " - path to the exclusion file", file=out)
-               print(yellow(" -i, --interactive")+ \
-                       "         - ask confirmation before deletions", file=out)
-               print(yellow(" -n, --package-names")+ \
-                       "       - protect all versions (when --destructive)", file=out)
-               print(yellow(" -p, --pretend")+ \
-                       "             - only display what would be cleaned", file=out)
-               print(yellow(" -q, --quiet")+ \
-                       "               - be as quiet as possible", file=out)
-               print(yellow(" -t, --time-limit=<time>")+ \
-                       "   - don't delete files modified since "+yellow("<time>"), file=out)
-               print("   "+yellow("<time>"), "is a duration: \"1y\" is"+ \
-                               " \"one year\", \"2w\" is \"two weeks\", etc. ", file=out)
-               print("   "+"Units are: y (years), m (months), w (weeks), "+ \
-                               "d (days) and h (hours).", file=out)
-               print(yellow(" -h, --help")+ \
-                       "                - display the help screen", file=out)
-               print(yellow(" -V, --version")+ \
-                       "             - display version info", file=out)
-               print(file=out)
-       if error == 'actions' or help == 'all':
-               print("Available", green("actions")+":", file=out)
-               print(green(" packages")+ \
-                       "      - clean outdated binary packages from:", file=out)
-               print("                  ",teal(pkgdir), file=out)
-               print(green(" distfiles")+ \
-                       "     - clean outdated packages sources files from:", file=out)
-               print("                  ",teal(distdir), file=out)
-               print(file=out)
-       if error in ('packages-options','merged-packages-options') \
-       or help in ('all','packages'):
-               print("Available", yellow("options"),"for the", \
-                               green("packages"),"action:", file=out)
-               print(yellow(" NONE  :)"), file=out)
-               print(file=out)
-       if error in ('distfiles-options', 'merged-distfiles-options') \
-       or help in ('all','distfiles'):
-               print("Available", yellow("options"),"for the", \
-                               green("distfiles"),"action:", file=out)
-               print(yellow(" -f, --fetch-restricted")+ \
-                       "   - protect fetch-restricted files (when --destructive)", file=out)
-               print(yellow(" -s, --size-limit=<size>")+ \
-                       "  - don't delete distfiles bigger than "+yellow("<size>"), file=out)
-               print("   "+yellow("<size>"), "is a size specification: "+ \
-                               "\"10M\" is \"ten megabytes\", \"200K\" is", file=out)
-               print("   "+"\"two hundreds kilobytes\", etc.  Units are: "+ \
-                               "G, M, K and B.", file=out)
-               print(file=out)
-       print("More detailed instruction can be found in", \
-                       turquoise("`man %s`" % __productname__), file=out)
-
-
-###############################################################################
-# einfo: display an info message depending on a color mode
-def einfo(message="", nocolor=False):
-       if not nocolor: prefix = " "+green('*')
-       else: prefix = ">>>"
-       print(prefix,message)
-
-
-###############################################################################
-# eerror: display an error depending on a color mode
-def eerror(message="", nocolor=False):
-       if not nocolor: prefix = " "+red('*')
-       else: prefix = "!!!"
-       print(prefix,message, file=sys.stderr)
-
-
-###############################################################################
-# eprompt: display a user question depending on a color mode.
-def eprompt(message, nocolor=False):
-       if not nocolor: prefix = " "+red('>')+" "
-       else: prefix = "??? "
-       sys.stdout.write(prefix+message)
-       sys.stdout.flush()
-
-
-###############################################################################
-# prettySize: integer -> byte/kilo/mega/giga converter. Optionnally justify the
-# result. Output is a string.
-def prettySize(size,justify=False):
-       units = [" G"," M"," K"," B"]
-       fmt = "{0:.0f}"
-       while len(units) and size >= 1000:
-               fmt = "{0:.1f}"
-               size = size / 1024.
-               units.pop()
-       sizestr = fmt.format(size)+units[-1]
-       if justify:
-               sizestr = " " + blue("[ ") + " "*(7-len(sizestr)) \
-                         + green(sizestr) + blue(" ]")
-       return sizestr
-
-
-###############################################################################
-# yesNoAllPrompt: print a prompt until user answer in yes/no/all. Return a
-# boolean for answer, and also may affect the 'accept_all' option.
-# Note: i gave up with getch-like functions, to much bugs in case of escape
-# sequences. Back to raw_input.
-def yesNoAllPrompt(myoptions,message="Do you want to proceed?"):
-       user_string="xxx"
-       while not user_string.lower() in ["","y","n","a","yes","no","all"]:
-               eprompt(message+" [Y/n/a]: ", myoptions['nocolor'])
-               user_string = sys.stdin.readline()
-       if user_string.lower() in ["a","all"]:
-               myoptions['accept_all'] = True
-       myanswer = user_string.lower() in ["","y","a","yes","all"]
-       return myanswer
-
-
-###############################################################################
-# ParseArgsException: for parseArgs() -> main() communication
-class ParseArgsException(Exception):
-       def __init__(self, value):
-               self.value = value
-       def __str__(self):
-               return repr(self.value)
-
-
-###############################################################################
-# parseSize: convert a file size "Xu" ("X" is an integer, and "u" in [G,M,K,B])
-# into an integer (file size in Bytes). Raises ParseArgsException('size') in
-# case of failure.
-def parseSize(size):
-       myunits = { \
-               'G': (1024**3), \
-               'M': (1024**2), \
-               'K': 1024, \
-               'B': 1 \
-       }
-       try:
-               mymatch = re.match(r"^(?P<value>\d+)(?P<unit>[GMKBgmkb])?$",size)
-               mysize = int(mymatch.group('value'))
-               if mymatch.group('unit'):
-                       mysize *= myunits[mymatch.group('unit').capitalize()]
-       except:
-               raise ParseArgsException('size')
-       return mysize
-
-
-###############################################################################
-# parseTime: convert a duration "Xu" ("X" is an int, and "u" a time unit in
-# [Y,M,W,D,H]) into an integer which is a past EPOCH date.
-# Raises ParseArgsException('time') in case of failure.
-# (yep, big approximations inside... who cares?)
-def parseTime(timespec):
-       myunits = {'H' : (60 * 60)}
-       myunits['D'] = myunits['H'] * 24
-       myunits['W'] = myunits['D'] * 7
-       myunits['M'] = myunits['D'] * 30
-       myunits['Y'] = myunits['D'] * 365
-       try:
-               # parse the time specification
-               mymatch = re.match(r"^(?P<value>\d+)(?P<unit>[YMWDHymwdh])?$",timespec)
-               myvalue = int(mymatch.group('value'))
-               if not mymatch.group('unit'): myunit = 'D'
-               else: myunit = mymatch.group('unit').capitalize()
-       except: raise ParseArgsException('time')
-       # calculate the limit EPOCH date
-       mytime = time.time() - (myvalue * myunits[myunit])
-       return mytime
-
-
-###############################################################################
-# parseCmdLine: parse the command line arguments. Raise exceptions on errors or
-# non-action modes (help/version). Returns an action, and affect the options
-# dict.
-def parseArgs(myoptions={}):
-
-       # local function for interpreting command line options
-       # and setting myoptions accordingly
-       def optionSwitch(myoption,opts,action=None):
-               return_code = True
-               for o, a in opts:
-                       if o in ("-h", "--help"):
-                               if action: raise ParseArgsException('help-'+action)
-                               else: raise ParseArgsException('help')
-                       elif o in ("-V", "--version"):
-                               raise ParseArgsException('version')
-                       elif o in ("-C", "--nocolor"):
-                               myoptions['nocolor'] = True
-                               nocolor()
-                       elif o in ("-d", "--destructive"):
-                               myoptions['destructive'] = True
-                       elif o in ("-i", "--interactive") and not myoptions['pretend']:
-                               myoptions['interactive'] = True
-                       elif o in ("-p", "--pretend"):
-                               myoptions['pretend'] = True
-                               myoptions['interactive'] = False
-                       elif o in ("-q", "--quiet"):
-                               myoptions['quiet'] = True
-                       elif o in ("-t", "--time-limit"):
-                               myoptions['time-limit'] = parseTime(a)
-                       elif o in ("-e", "--exclude-file"):
-                               myoptions['exclude-file'] = a
-                       elif o in ("-n", "--package-names"):
-                               myoptions['package-names'] = True
-                       elif o in ("-f", "--fetch-restricted"):
-                               myoptions['fetch-restricted'] = True
-                       elif o in ("-s", "--size-limit"):
-                               myoptions['size-limit'] = parseSize(a)
-                       else: return_code = False
-               # sanity check of --destructive only options:
-               for myopt in ('fetch-restricted', 'package-names'):
-                       if (not myoptions['destructive']) and myoptions[myopt]:
-                               if not myoptions['quiet']:
-                                       eerror("--%s only makes sense in --destructive mode." \
-                                                       % myopt, myoptions['nocolor'])
-                               myoptions[myopt] = False
-               return return_code
-
-       # here are the different allowed command line options (getopt args)
-       getopt_options = {'short':{}, 'long':{}}
-       getopt_options['short']['global'] = "Cdipqe:t:nhV"
-       getopt_options['long']['global'] = ["nocolor", "destructive", \
-                       "interactive", "pretend", "quiet", "exclude-file=", "time-limit=", \
-                       "package-names", "help", "version"]
-       getopt_options['short']['distfiles'] = "fs:"
-       getopt_options['long']['distfiles'] = ["fetch-restricted", "size-limit="]
-       getopt_options['short']['packages'] = ""
-       getopt_options['long']['packages'] = [""]
-       # set default options, except 'nocolor', which is set in main()
-       myoptions['interactive'] = False
-       myoptions['pretend'] = False
-       myoptions['quiet'] = False
-       myoptions['accept_all'] = False
-       myoptions['destructive'] = False
-       myoptions['time-limit'] = 0
-       myoptions['package-names'] = False
-       myoptions['fetch-restricted'] = False
-       myoptions['size-limit'] = 0
-       # if called by a well-named symlink, set the acction accordingly:
-       myaction = None
-       if os.path.basename(sys.argv[0]) in \
-                       (__productname__+'-pkg', __productname__+'-packages'):
-               myaction = 'packages'
-       elif os.path.basename(sys.argv[0]) in \
-                       (__productname__+'-dist', __productname__+'-distfiles'):
-               myaction = 'distfiles'
-       # prepare for the first getopt
-       if myaction:
-               short_opts = getopt_options['short']['global'] \
-                               + getopt_options['short'][myaction]
-               long_opts = getopt_options['long']['global'] \
-                               + getopt_options['long'][myaction]
-               opts_mode = 'merged-'+myaction
-       else:
-               short_opts = getopt_options['short']['global']
-               long_opts = getopt_options['long']['global']
-               opts_mode = 'global'
-       # apply getopts to command line, show partial help on failure
-       try: opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
-       except: raise ParseArgsException(opts_mode+'-options')
-       # set myoptions accordingly
-       optionSwitch(myoptions,opts,action=myaction)
-       # if action was already set, there should be no more args
-       if myaction and len(args): raise ParseArgsException(opts_mode+'-options')
-       # if action was set, there is nothing left to do
-       if myaction: return myaction
-       # So, we are in "eclean --foo action --bar" mode. Parse remaining args...
-       # Only two actions are allowed: 'packages' and 'distfiles'.
-       if not len(args) or not args[0] in ('packages','distfiles'):
-               raise ParseArgsException('actions')
-       myaction = args.pop(0)
-       # parse the action specific options
-       try: opts, args = getopt.getopt(args, \
-                       getopt_options['short'][myaction], \
-                       getopt_options['long'][myaction])
-       except: raise ParseArgsException(myaction+'-options')
-       # set myoptions again, for action-specific options
-       optionSwitch(myoptions,opts,action=myaction)
-       # any remaning args? Then die!
-       if len(args): raise ParseArgsException(myaction+'-options')
-       # returns the action. Options dictionary is modified by side-effect.
-       return myaction
+       sys.exit(1)
 
-###############################################################################
-# isValidCP: check wether a string is a valid cat/pkg-name
-# This is for 2.0.51 vs. CVS HEAD compatibility, i've not found any function
-# for that which would exists in both. Weird...
-def isValidCP(cp):
-       if not '/' in cp: return False
-       try: portage.cpv_getkey(cp+"-0")
-       except: return False
-       else: return True
-
-
-###############################################################################
-# ParseExcludeFileException: for parseExcludeFile() -> main() communication
-class ParseExcludeFileException(Exception):
-       def __init__(self, value):
-               self.value = value
-       def __str__(self):
-               return repr(self.value)
-
-
-###############################################################################
-# parseExcludeFile: parses an exclusion file, returns an exclusion dictionnary
-# Raises ParseExcludeFileException in case of fatal error.
-def parseExcludeFile(filepath):
-       excl_dict = { \
-                       'categories':{}, \
-                       'packages':{}, \
-                       'anti-packages':{}, \
-                       'garbage':{} }
-       try: file = open(filepath,"r")
-       except IOError:
-               raise ParseExcludeFileException("Could not open exclusion file.")
-       filecontents = file.readlines()
-       file.close()
-       cat_re = re.compile('^(?P<cat>[a-zA-Z0-9]+-[a-zA-Z0-9]+)(/\*)?$')
-       cp_re = re.compile('^(?P<cp>[-a-zA-Z0-9_]+/[-a-zA-Z0-9_]+)$')
-       for line in filecontents:
-               line = line.strip()
-               if not len(line): continue
-               if line[0] == '#': continue
-               try: mycat = cat_re.match(line).group('cat')
-               except: pass
-               else:
-                       if not mycat in portage.settings.categories:
-                               raise ParseExcludeFileException("Invalid category: "+mycat)
-                       excl_dict['categories'][mycat] = None
-                       continue
-               dict_key = 'packages'
-               if line[0] == '!':
-                       dict_key = 'anti-packages'
-                       line = line[1:]
-               try:
-                       mycp = cp_re.match(line).group('cp')
-                       if isValidCP(mycp):
-                               excl_dict[dict_key][mycp] = None
-                               continue
-                       else: raise ParseExcludeFileException("Invalid cat/pkg: "+mycp)
-               except: pass
-               #raise ParseExcludeFileException("Invalid line: "+line)
-               try:
-                       excl_dict['garbage'][line] = re.compile(line)
-               except:
-                       try:
-                               excl_dict['garbage'][line] = re.compile(re.escape(line))
-                       except:
-                               raise ParseExcludeFileException("Invalid file name/regular expression: "+line)
-       return excl_dict
-
-
-###############################################################################
-# exclDictExpand: returns a dictionary of all CP from porttree which match
-# the exclusion dictionary
-def exclDictExpand(excl_dict):
-       mydict = {}
-       if 'categories' in excl_dict:
-               # XXX: i smell an access to something which is really out of API...
-               for mytree in portage.portdb.porttrees:
-                       for mycat in excl_dict['categories']:
-                               for mypkg in listdir(os.path.join(mytree,mycat),ignorecvs=1):
-                                       mydict[mycat+'/'+mypkg] = None
-       if 'packages' in excl_dict:
-               for mycp in excl_dict['packages']:
-                       mydict[mycp] = None
-       if 'anti-packages' in excl_dict:
-               for mycp in excl_dict['anti-packages']:
-                       if mycp in mydict:
-                               del mydict[mycp]
-       return mydict
-
-
-###############################################################################
-# exclDictMatch: checks whether a CP matches the exclusion rules
-def exclDictMatch(excl_dict,pkg):
-       if 'anti-packages' in excl_dict \
-          and pkg in excl_dict['anti-packages']:
-               return False
-       if 'packages' in excl_dict \
-          and pkg in excl_dict['packages']:
-               return True
-       mycat = pkg.split('/')[0]
-       if 'categories' in excl_dict \
-          and mycat in excl_dict['categories']:
-               return True
-       return False
-
-
-###############################################################################
-# findDistfiles: find all obsolete distfiles.
-# XXX: what about cvs ebuilds? i should install some to see where it goes...
-def findDistfiles( \
-               myoptions, \
-               exclude_dict={}, \
-               destructive=False,\
-               fetch_restricted=False, \
-               package_names=False, \
-               time_limit=0, \
-               size_limit=0,):
-       # this regexp extracts files names from SRC_URI. It is not very precise,
-       # but we don't care (may return empty strings, etc.), since it is fast.
-       file_regexp = re.compile('([a-zA-Z0-9_,\.\-\+\~]*)[\s\)]')
-       clean_dict = {}
-       keep = []
-       pkg_dict = {}
-
-       # create a big CPV->SRC_URI dict of packages whose distfiles should be kept
-       if (not destructive) or fetch_restricted:
-               # list all CPV from portree (yeah, that takes time...)
-               for package in portage.portdb.cp_all():
-                       for my_cpv in portage.portdb.cp_list(package):
-                               # get SRC_URI and RESTRICT from aux_get
-                               try: (src_uri,restrict) = \
-                                       portage.portdb.aux_get(my_cpv,["SRC_URI","RESTRICT"])
-                               except KeyError: continue
-                               # keep either all or fetch-restricted only
-                               if (not destructive) or ('fetch' in restrict):
-                                       pkg_dict[my_cpv] = src_uri
-       if destructive:
-               if not package_names:
-                       # list all CPV from vartree
-                       pkg_list = portage.db[portage.root]["vartree"].dbapi.cpv_all()
-               else:
-                       # list all CPV from portree for CP in vartree
-                       pkg_list = []
-                       for package in portage.db[portage.root]["vartree"].dbapi.cp_all():
-                               pkg_list += portage.portdb.cp_list(package)
-               for my_cp in exclDictExpand(exclude_dict):
-                       # add packages from the exclude file
-                       pkg_list += portage.portdb.cp_list(my_cp)
-               for my_cpv in pkg_list:
-                       # skip non-existing CPV (avoids ugly aux_get messages)
-                       if not portage.portdb.cpv_exists(my_cpv): continue
-                       # get SRC_URI from aux_get
-                       try: pkg_dict[my_cpv] = \
-                                       portage.portdb.aux_get(my_cpv,["SRC_URI"])[0]
-                       except KeyError: continue
-               del pkg_list
-
-       # create a dictionary of files which should be deleted
-       if not (os.path.isdir(distdir)):
-               eerror("%s does not appear to be a directory." % distdir, myoptions['nocolor'])
-               eerror("Please set DISTDIR to a sane value.", myoptions['nocolor'])
-               eerror("(Check your /etc/make.conf and environment).", myoptions['nocolor'])
-               exit(1)
-       for file in os.listdir(distdir):
-               filepath = os.path.join(distdir, file)
-               try: file_stat = os.stat(filepath)
-               except: continue
-               if not stat.S_ISREG(file_stat[stat.ST_MODE]): continue
-               if size_limit and (file_stat[stat.ST_SIZE] >= size_limit):
-                       continue
-               if time_limit and (file_stat[stat.ST_MTIME] >= time_limit):
-                       continue
-               if 'garbage' in exclude_dict:
-                       # Try to match file name directly
-                       if file in exclude_dict['garbage']:
-                               file_match = True
-                       # See if file matches via regular expression matching
-                       else:
-                               file_match = False
-                               for file_entry in exclude_dict['garbage']:
-                                       if exclude_dict['garbage'][file_entry].match(file):
-                                               file_match = True
-                                               break
-
-                       if file_match:
-                               continue
-               # this is a candidate for cleaning
-               clean_dict[file]=[filepath]
-       # remove files owned by some protected packages
-       for my_cpv in pkg_dict:
-               for file in file_regexp.findall(pkg_dict[my_cpv]+"\n"):
-                       if file in clean_dict:
-                               del clean_dict[file]
-               # no need to waste IO time if there is nothing left to clean
-               if not len(clean_dict): return clean_dict
-       return clean_dict
-
-
-###############################################################################
-# findPackages: find all obsolete binary packages.
-# XXX: packages are found only by symlinks. Maybe i should also return .tbz2
-#      files from All/ that have no corresponding symlinks.
-def findPackages( \
-               myoptions, \
-               exclude_dict={}, \
-               destructive=False, \
-               time_limit=0, \
-               package_names=False):
-       clean_dict = {}
-       # create a full package dictionary
-
-       if not (os.path.isdir(pkgdir)):
-               eerror("%s does not appear to be a directory." % pkgdir, myoptions['nocolor'])
-               eerror("Please set PKGDIR to a sane value.", myoptions['nocolor'])
-               eerror("(Check your /etc/make.conf and environment).", myoptions['nocolor'])
-               exit(1)
-       for root, dirs, files in walk(pkgdir):
-               if root[-3:] == 'All': continue
-               for file in files:
-                       if not file[-5:] == ".tbz2":
-                               # ignore non-tbz2 files
-                               continue
-                       path = os.path.join(root, file)
-                       category = os.path.split(root)[-1]
-                       cpv = category+"/"+file[:-5]
-                       mystat = os.lstat(path)
-                       if time_limit and (mystat[stat.ST_MTIME] >= time_limit):
-                               # time-limit exclusion
-                               continue
-                       # dict is cpv->[files] (2 files in general, because of symlink)
-                       clean_dict[cpv] = [path]
-                       #if os.path.islink(path):
-                       if stat.S_ISLNK(mystat[stat.ST_MODE]):
-                               clean_dict[cpv].append(os.path.realpath(path))
-       # keep only obsolete ones
-       if destructive:
-               mydbapi = portage.db[portage.root]["vartree"].dbapi
-               if package_names: cp_all = dict.fromkeys(mydbapi.cp_all())
-               else: cp_all = {}
-       else:
-               mydbapi = portage.db[portage.root]["porttree"].dbapi
-               cp_all = {}
-       for mycpv in clean_dict.keys():
-               if exclDictMatch(exclude_dict,portage.cpv_getkey(mycpv)):
-                       # exclusion because of the exclude file
-                       del clean_dict[mycpv]
-                       continue
-               if mydbapi.cpv_exists(mycpv):
-                       # exclusion because pkg still exists (in porttree or vartree)
-                       del clean_dict[mycpv]
-                       continue
-               if portage.cpv_getkey(mycpv) in cp_all:
-                       # exlusion because of --package-names
-                       del clean_dict[mycpv]
-
-       return clean_dict
-
-
-###############################################################################
-# doCleanup: takes a dictionnary {'display name':[list of files]}. Calculate
-# size of each entry for display, prompt user if needed, delete files if needed
-# and return the total size of files that [have been / would be] deleted.
-def doCleanup(clean_dict,action,myoptions):
-       # define vocabulary of this action
-       if action == 'distfiles': file_type = 'file'
-       else: file_type = 'binary package'
-       # sorting helps reading
-       clean_keys = list(clean_dict.keys())
-       clean_keys.sort()
-       clean_size = 0
-       # clean all entries one by one
-       for mykey in clean_keys:
-               key_size = 0
-               for file in clean_dict[mykey]:
-                       # get total size for an entry (may be several files, and
-                       # symlinks count zero)
-                       if os.path.islink(file): continue
-                       try: key_size += os.path.getsize(file)
-                       except: eerror("Could not read size of "+file, \
-                                      myoptions['nocolor'])
-               if not myoptions['quiet']:
-                       # pretty print mode
-                       print(prettySize(key_size,True),teal(mykey))
-               elif myoptions['pretend'] or myoptions['interactive']:
-                       # file list mode
-                       for file in clean_dict[mykey]: print(file)
-               #else: actually delete stuff, but don't print anything
-               if myoptions['pretend']: clean_size += key_size
-               elif not myoptions['interactive'] \
-                    or myoptions['accept_all'] \
-                    or yesNoAllPrompt(myoptions, \
-                                      "Do you want to delete this " \
-                                      + file_type+"?"):
-                       # non-interactive mode or positive answer.
-                       # For each file, try to delete the file and clean it out
-                       # of Packages metadata file
-                       if action == 'packages':
-                               metadata = portage.getbinpkg.PackageIndex()
-                               with open(os.path.join(pkgdir, 'Packages')) as metadata_file:
-                                       metadata.read(metadata_file)
-                       for file in clean_dict[mykey]:
-                               # ...get its size...
-                               filesize = 0
-                               if not os.path.exists(file): continue
-                               if not os.path.islink(file):
-                                       try: filesize = os.path.getsize(file)
-                                       except: eerror("Could not read size of "\
-                                                      +file, myoptions['nocolor'])
-                               # ...and try to delete it.
-                               try:
-                                       os.unlink(file)
-                               except:
-                                       eerror("Could not delete "+file, \
-                                        myoptions['nocolor'])
-                               # only count size if successfully deleted
-                               else:
-                                       clean_size += filesize
-                                       if action == 'packages':
-                                               metadata.packages[:] = [p for p in metadata.packages if 'CPV' in p and p['CPV'] != file]
-
-                       if action == 'packages':
-                               with open(os.path.join(pkgdir, 'Packages'), 'w') as metadata_file:
-                                       metadata.write(metadata_file)
-
-       # return total size of deleted or to delete files
-       return clean_size
-
-
-###############################################################################
-# doAction: execute one action, ie display a few message, call the right find*
-# function, and then call doCleanup with its result.
-def doAction(action,myoptions,exclude_dict={}):
-       # define vocabulary for the output
-       if action == 'packages': files_type = "binary packages"
-       else: files_type = "distfiles"
-       # find files to delete, depending on the action
-       if not myoptions['quiet']:
-               einfo("Building file list for "+action+" cleaning...", \
-                     myoptions['nocolor'])
-       if action == 'packages':
-               clean_dict = findPackages(
-                       myoptions, \
-                       exclude_dict=exclude_dict, \
-                       destructive=myoptions['destructive'], \
-                       package_names=myoptions['package-names'], \
-                       time_limit=myoptions['time-limit'])
-       else:
-               clean_dict = findDistfiles( \
-                       myoptions, \
-                       exclude_dict=exclude_dict, \
-                       destructive=myoptions['destructive'], \
-                       fetch_restricted=myoptions['fetch-restricted'], \
-                       package_names=myoptions['package-names'], \
-                       time_limit=myoptions['time-limit'], \
-                       size_limit=myoptions['size-limit'])
-       # actually clean files if something was found
-       if clean_dict:
-               # verbose pretend message
-               if myoptions['pretend'] and not myoptions['quiet']:
-                       einfo("Here are "+files_type+" that would be deleted:", \
-                             myoptions['nocolor'])
-               # verbose non-pretend message
-               elif not myoptions['quiet']:
-                       einfo("Cleaning "+files_type+"...",myoptions['nocolor'])
-               # do the cleanup, and get size of deleted files
-               clean_size = doCleanup(clean_dict,action,myoptions)
-               # vocabulary for final message
-               if myoptions['pretend']: verb = "would be"
-               else: verb = "has been"
-               # display freed space
-               if not myoptions['quiet']:
-                       einfo("Total space that "+verb+" freed in " \
-                             + action + " directory: " \
-                             + red(prettySize(clean_size)), \
-                             myoptions['nocolor'])
-       # nothing was found, return
-       elif not myoptions['quiet']:
-               einfo("Your "+action+" directory was already clean.", \
-                     myoptions['nocolor'])
-
-
-###############################################################################
-# main: parse command line and execute all actions
-def main():
-       # set default options
-       myoptions = {}
-       myoptions['nocolor'] = port_settings["NOCOLOR"] in ('yes','true') \
-                              and sys.stdout.isatty()
-       if myoptions['nocolor']: nocolor()
-       # parse command line options and actions
-       try: myaction = parseArgs(myoptions)
-       # filter exception to know what message to display
-       except ParseArgsException as e:
-               if e.value == 'help':
-                       printUsage(help='all')
-                       sys.exit(0)
-               elif e.value[:5] == 'help-':
-                       printUsage(help=e.value[5:])
-                       sys.exit(0)
-               elif e.value == 'version':
-                       printVersion()
-                       sys.exit(0)
-               else:
-                       printUsage(e.value)
-                       sys.exit(2)
-       # parse the exclusion file
-       if not 'exclude-file' in myoptions:
-               my_exclude_file = "/etc/%s/%s.exclude" % (__productname__ , myaction)
-               if os.path.isfile(my_exclude_file):
-                       myoptions['exclude-file'] = my_exclude_file
-       if 'exclude-file' in myoptions:
-               try: exclude_dict = parseExcludeFile(myoptions['exclude-file'])
-               except ParseExcludeFileException as e:
-                       eerror(e, myoptions['nocolor'])
-                       eerror("Invalid exclusion file: %s" % myoptions['exclude-file'], \
-                                       myoptions['nocolor'])
-                       eerror("See format of this file in `man %s`" % __productname__, \
-                                       myoptions['nocolor'])
-                       sys.exit(1)
-       else: exclude_dict={}
-       # security check for non-pretend mode
-       if not myoptions['pretend'] and portage.secpass == 0:
-               eerror("Permission denied: you must be root or belong to the portage group.", \
-                      myoptions['nocolor'])
-               sys.exit(1)
-       # execute action
-       doAction(myaction, myoptions, exclude_dict=exclude_dict)
 
+from gentoolkit.eclean.cli import main
 
-###############################################################################
-# actually call main() if launched as a script
-if __name__ == "__main__":
-       try: main()
-       except KeyboardInterrupt:
-               print("Aborted.")
-               sys.exit(130)
-       sys.exit(0)
+try:
+       main()
+except KeyboardInterrupt:
+       print("Aborted.")
+       sys.exit(130)
+sys.exit(0)
 
index a3bb7730a0d143812babf36f426a6d9aba63415a..54c3a0747e5ba794f41bda2bb0056201dee51908 100755 (executable)
@@ -12,6 +12,7 @@ the MD5 sum of each file owned by a given package, and many other things.
 
 from __future__ import print_function
 
+import os
 import sys
 # This block ensures that ^C interrupts are handled quietly.
 try:
@@ -36,11 +37,12 @@ from gentoolkit import equery, errors
 try:
        equery.main()
 except errors.GentoolkitException as err:
-       if '--debug' in sys.argv:
+       if '--debug' in sys.argv or bool(os.getenv('DEBUG', False)):
                raise
        else:
                from gentoolkit import pprinter as pp
                sys.stderr.write(pp.error(str(err)))
-               print()
-               print("Add '--debug' to global options for traceback.")
+               if err.is_serious:
+                       print()
+                       print("Add '--debug' to global options for traceback.")
                sys.exit(1)
index e6d21c73ff6e08ff22e9aa54713ae2815ac95993..76044b8e97dd6c620bd5b222704c10650e080854 100644 (file)
@@ -11,6 +11,14 @@ Is a collection of modules for analysing the state of installed Gentoo packages
 USE flags or keywords used for installation, and their current masking status.
 .br
 It can also optionally (re)generate new /etc/portage/package.* files.
+.br 
+
+.br
+.B CAUTION:
+This is beta software and is not yet feature complete. Some features, options or its name
+may change in the future.   Any files that it generates are saved to your home directory
+and will not harm your system without effort on your part.
+.br
 
 .SH "GLOBAL OPTIONS"
 .HP
index 7a5fbec435e3c72b01555a6c130841c3e3a80ce3..82625b61e674ef71c12458d3b1ae2b09ba824f64 100644 (file)
@@ -19,7 +19,7 @@ __version__ = "svn"
 __productname__ = "analyse"
 __authors__ = (
        'Brian Dolbec, <brian.dolbec@gmail.com>'
-       
+
 )
 
 # make an exportable copy of the info for help output
@@ -29,7 +29,7 @@ MODULE_INFO = {
        "__version__": __version__,
        "__productname__": __productname__,
        "__authors__": __authors__
-       
+
 }
 
 import errno
@@ -54,7 +54,7 @@ NAME_MAP = {
 }
 
 FORMATTED_OPTIONS = (
-               ("    (a)nalyse", 
+               ("    (a)nalyse",
                "analyses the installed PKG database USE flag or keyword useage"),
                ("    (r)ebuild",
                "analyses the Installed PKG database and generates files suitable"),
index 679751042c8d4f39e13002e307a9c0df5e0b6a22..21623242cb55135811656756f41126796ac58069 100644 (file)
@@ -20,6 +20,7 @@ from gentoolkit.analyse.lib import (get_installed_use,  get_iuse, abs_flag,
        abs_list, get_all_cpv_use, get_flags, FlagAnalyzer, KeywordAnalyser)
 from gentoolkit.analyse.output import nl, AnalysisPrinter
 from gentoolkit.package import Package
+from gentoolkit.helpers import get_installed_cpvs
 
 import portage
 
@@ -35,15 +36,31 @@ def gather_flags_info(
                _get_used=get_installed_use
                ):
        """Analyse the installed pkgs USE flags for frequency of use
-       
+
+       @type cpvs: list
        @param cpvs: optional list of [cat/pkg-ver,...] to analyse or
                        defaults to entire installed pkg db
+       @type: system_flags: list
+       @param system_flags: the current default USE flags as defined
+                       by portage.settings["USE"].split()
+       @type include_unset: bool
+       @param include_unset: controls the inclusion of unset USE flags in the report.
+       @type target: string
+       @param target: the environment variable being analysed
+                       one of ["USE", "PKGUSE"]
+       @type _get_flags: function
+       @param _get_flags: ovride-able for testing,
+                       defaults to gentoolkit.analyse.lib.get_flags
+       @param _get_used: ovride-able for testing,
+                       defaults to gentoolkit.analyse.lib.get_installed_use
        @rtype dict. {flag:{"+":[cat/pkg-ver,...], "-":[cat/pkg-ver,...], "unset":[]}
        """
        if cpvs is None:
                cpvs = VARDB.cpv_all()
        # pass them in to override for tests
        flags = FlagAnalyzer(system_flags,
+               filter_defaults=False,
+               target=target,
                _get_flags=_get_flags,
                _get_used=get_installed_use
        )
@@ -87,7 +104,7 @@ def gather_keywords_info(
                analyser = None
                ):
        """Analyse the installed pkgs 'keywords' for frequency of use
-       
+
        @param cpvs: optional list of [cat/pkg-ver,...] to analyse or
                        defaults to entire installed pkg db
        @param system_keywords: list of the system keywords
@@ -139,7 +156,7 @@ class Analyse(ModuleBase):
        """Installed db analysis tool to query the installed databse
        and produce/output stats for USE flags or keywords/mask.
        The 'rebuild' action output is in the form suitable for file type output
-       to create a new package.use, package.keywords, package.unmask 
+       to create a new package.use, package.keywords, package.unmask
        type files in the event of needing to rebuild the
        /etc/portage/* user configs
        """
@@ -153,7 +170,7 @@ class Analyse(ModuleBase):
                        "verbose": False,
                        "quiet": False,
                        'prefix': False,
-                       'portage': False
+                       'portage': True
                }
                self.module_opts = {
                        "-f": ("flags", "boolean", True),
@@ -166,8 +183,8 @@ class Analyse(ModuleBase):
                        "--verbose": ("verbose", "boolean", True),
                        "-p": ("prefix", "boolean", True),
                        "--prefix": ("prefix", "boolean", True),
-                       "-G": ("portage", "boolean", True),
-                       "--portage": ("portage", "boolean", True),
+                       "-G": ("portage", "boolean", False),
+                       "--portage": ("portage", "boolean", False),
                }
                self.formatted_options = [
                        ("    -h, --help",  "Outputs this useage message"),
@@ -177,17 +194,17 @@ class Analyse(ModuleBase):
                        ("    -u, --unset",
                        "Additionally include any unset USE flags and the packages"),
                        ("", "that could use them"),
-                       ("    -v, --verbose", 
+                       ("    -v, --verbose",
                        "Used in the analyse action to output more detailed information"),
-                       ("    -p, --prefix", 
+                       ("    -p, --prefix",
                        "Used for testing purposes only, runs report using " +
                        "a prefix keyword and 'prefix' USE flag"),
-                       ("    -G, --portage", 
-                       "Use portage directly instead of gentoolkit's Package " +
-                       "object for some operations. Usually a little faster."),
+                       #("    -G, --portage",
+                       #"Use portage directly instead of gentoolkit's Package " +
+                       #"object for some operations. Usually a little faster."),
                ]
                self.formatted_args = [
-                       ("    use", 
+                       ("    use",
                        "causes the action to analyse the installed packages USE flags"),
                        ("    pkguse",
                        "causes the action to analyse the installed packages PKGUSE flags"),
@@ -197,15 +214,21 @@ class Analyse(ModuleBase):
                        "causes the action to analyse the installed packages keywords"),
                ]
                self.short_opts = "huvpG"
-               self.long_opts = ("help", "unset", "verbose", "prefix", "portage")
+               self.long_opts = ("help", "unset", "verbose", "prefix") #, "portage")
                self.need_queries = True
                self.arg_spec = "Target"
                self.arg_options = ['use', 'pkguse','keywords']
                self.arg_option = False
+               self.warning = (
+                       "     CAUTION",
+                       "This is beta software and some features/options are incomplete,",
+                       "some features may change in future releases includig its name.",
+                       "Feedback will be appreciated, http://bugs.gentoo.org")
+
 
        def run(self, input_args, quiet=False):
                """runs the module
-               
+
                @param input_args: input arguments to be parsed
                """
                query = self.main_setup(input_args)
@@ -216,10 +239,10 @@ class Analyse(ModuleBase):
                        self.analyse_keywords()
 
        def analyse_flags(self, target):
-               """This will scan the installed packages db and analyse the 
+               """This will scan the installed packages db and analyse the
                USE flags used for installation and produce a report on how
                they were used.
-               
+
                @type target: string
                @param target: the target to be analysed, one of ["use", "pkguse"]
                """
@@ -227,17 +250,18 @@ class Analyse(ModuleBase):
                self.printer = AnalysisPrinter("use", self.options["verbose"], system_use)
                if self.options["verbose"]:
                        cpvs = VARDB.cpv_all()
+                       #cpvs = get_installed_cpvs()
                        #print "Total number of installed ebuilds =", len(cpvs)
                        flag_users = gather_flags_info(cpvs, system_use,
                                self.options["unset"], target=target.upper(),
                                use_portage=self.options['portage'])
                else:
-                       flag_users = gather_flags_info(system_flags=system_use, 
+                       cpvs = get_installed_cpvs()
+                       flag_users = gather_flags_info(cpvs, system_flags=system_use,
                                include_unset=self.options["unset"], target=target.upper(),
                                use_portage=self.options['portage'])
                #print flag_users
-               flag_keys = list(flag_users.keys())
-               flag_keys.sort()
+               flag_keys = sorted(flag_users)
                if self.options["verbose"]:
                        print("    Flag                              System  #pkgs   cat/pkg-ver")
                        blankline = nl
@@ -261,12 +285,12 @@ class Analyse(ModuleBase):
                        print("===================================================")
                        print("Total number of flags in report =", pp.output.red(str(len(flag_keys))))
                        if self.options["verbose"]:
-                               print("Total number of installed ebuilds =", pp.output.red(str(len(cpvs))))
+                               print("Total number of installed ebuilds =", pp.output.red(str(len([x for x in cpvs]))))
                        print()
 
 
        def analyse_keywords(self, keywords=None):
-               """This will scan the installed packages db and analyse the 
+               """This will scan the installed packages db and analyse the
                keywords used for installation and produce a report on them.
                """
                print()
@@ -295,7 +319,7 @@ class Analyse(ModuleBase):
                        cpvs = VARDB.cpv_all()
                        #print "Total number of installed ebuilds =", len(cpvs)
                        keyword_users = gather_keywords_info(
-                               cpvs=cpvs, 
+                               cpvs=cpvs,
                                system_keywords=system_keywords,
                                use_portage=self.options['portage'],
                                keywords=keywords, analyser = self.analyser
@@ -305,13 +329,12 @@ class Analyse(ModuleBase):
                        keyword_users = gather_keywords_info(
                                system_keywords=system_keywords,
                                use_portage=self.options['portage'],
-                               keywords=keywords, 
+                               keywords=keywords,
                                analyser = self.analyser
                                )
                        blankline = lambda: None
                #print keyword_users
-               keyword_keys = list(keyword_users.keys())
-               keyword_keys.sort()
+               keyword_keys = sorted(keyword_users)
                if self.options["verbose"]:
                        print(" Keyword               System  #pkgs   cat/pkg-ver")
                elif not self.options['quiet']:
@@ -344,9 +367,9 @@ class Analyse(ModuleBase):
 
 
 def main(input_args):
-       """Common starting method by the analyse master 
+       """Common starting method by the analyse master
        unless all modules are converted to this class method.
-       
+
        @param input_args: input args as supplied by equery master module.
        """
        query_module = Analyse()
index bb0a8bc80d381b84de7885542e3c55079fc1d31c..d45ccc6c001061d68a77240fcb0b97faa6ecd9d9 100644 (file)
@@ -28,7 +28,7 @@ from gentoolkit.base import mod_usage
 
 class ModuleBase(object):
        """Analyse base module class to parse module options print module help, etc.."""
-       
+
        def __init__(self):
                self.module_name = None
                self.options = {}
@@ -36,7 +36,7 @@ class ModuleBase(object):
                self.short_opts = None
                self.long_opts = None
                self.module_opts = {}
-               self.depwarning = None
+               self.warning = None
                self.need_queries = True
 
 
@@ -51,9 +51,9 @@ class ModuleBase(object):
                        print()
                        print(__doc__.strip())
                        print()
-               if self.depwarning:
+               if self.warning:
                        print()
-                       for line in self.depwarning:
+                       for line in self.warning:
                                sys.stderr.write(pp.warn(line))
                        print()
                print(mod_usage(mod_name=self.module_name, arg=self.arg_spec, optional=self.arg_option))
index 17e2118c0d31c8a3ae168ef52b28ebb9fbdfd287..a32d13b867ad38625714967570c7e3aa99c8fa1d 100644 (file)
@@ -20,7 +20,7 @@ import portage
 
 def get_installed_use(cpv, use="USE"):
        """Gets the installed USE flags from the VARDB
-       
+
        @type: cpv: string
        @param cpv: cat/pkg-ver
        @type use: string
@@ -33,7 +33,7 @@ def get_installed_use(cpv, use="USE"):
 
 def get_iuse(cpv):
        """Gets the current IUSE flags from the tree
-       
+
        @type: cpv: string
        @param cpv: cat/pkg-ver
        @rtype list
@@ -47,7 +47,7 @@ def get_iuse(cpv):
 
 def abs_flag(flag):
        """Absolute value function for a USE flag
-       
+
        @type flag: string
        @param flag: the use flag to absolute.
        @rtype: string
@@ -61,7 +61,7 @@ def abs_flag(flag):
 
 def abs_list(the_list):
        """Absolute value function for a USE flag list
-       
+
        @type the_list: list
        @param the_list: the use flags to absolute.
        @rtype: list
@@ -76,7 +76,7 @@ def abs_list(the_list):
 def filter_flags(use, use_expand_hidden, usemasked, useforced):
        """Filter function to remove hidden or otherwise not normally
        visible USE flags from a list.
-       
+
        @type use: list
        @param use: the USE flag list to be filtered.
        @type use_expand_hidden: list
@@ -110,7 +110,7 @@ def filter_flags(use, use_expand_hidden, usemasked, useforced):
 
 def get_all_cpv_use(cpv):
        """Uses portage to determine final USE flags and settings for an emerge
-       
+
        @type cpv: string
        @param cpv: eg cat/pkg-ver
        @rtype: lists
@@ -137,7 +137,7 @@ def get_all_cpv_use(cpv):
 def get_flags(cpv, final_setting=False):
        """Retrieves all information needed to filter out hidded, masked, etc.
        USE flags for a given package.
-       
+
        @type cpv: string
        @param cpv: eg. cat/pkg-ver
        @type final_setting: boolean
@@ -158,7 +158,7 @@ class FlagAnalyzer(object):
        """Specialty functions for analysing an installed package's
        USE flags.  Can be used for single or mulitple use without
        needing to be reset unless the system USE flags are changed.
-       
+
        @type system: list or set
        @param system: the default system USE flags.
        @type _get_flags: function
@@ -168,17 +168,21 @@ class FlagAnalyzer(object):
                """
        def __init__(self,
                system,
+               filter_defaults=False,
+               target="USE",
                _get_flags=get_flags,
                _get_used=get_installed_use
        ):
                self.get_flags = _get_flags
                self.get_used = _get_used
+               self.filter_defaults = filter_defaults
+               self.target = target
                self.reset(system)
 
        def reset(self, system):
                """Resets the internal system USE flags and use_expand variables
                to the new setting. The use_expand variable is handled internally.
-               
+
                @type system: list or set
                @param system: the default system USE flags.
                """
@@ -189,19 +193,19 @@ class FlagAnalyzer(object):
                """Gets all relavent USE flag info for a cpv and breaks them down
                into 3 sets, plus (package.use enabled), minus ( package.use disabled),
                unset.
-               
+
                @param cpv: string. 'cat/pkg-ver'
                @rtype tuple of sets
                @return (plus, minus, unset) sets of USE flags
                """
-               installed = set(self.get_used(cpv, "USE"))
+               installed = set(self.get_used(cpv, self.target))
                iuse =  set(abs_list(self.get_flags(cpv)))
                return self._analyse(installed, iuse)
 
        def _analyse(self, installed, iuse):
                """Analyses the supplied info and returns the flag settings
                that differ from the defaults
-               
+
                @type installed: set
                @param installed: the installed with use flags
                @type iuse: set
@@ -209,7 +213,10 @@ class FlagAnalyzer(object):
                """
                defaults = self.system.intersection(iuse)
                usedflags = iuse.intersection(set(installed))
-               plus = usedflags.difference(defaults)
+               if self.filter_defaults:
+                       plus = usedflags.difference(defaults)
+               else:
+                       plus = usedflags
                minus = defaults.difference(usedflags)
                unset = iuse.difference(defaults, plus, minus)
                cleaned = self.remove_expanding(unset)
@@ -219,7 +226,7 @@ class FlagAnalyzer(object):
                """Gets all relevent USE flag info for a pkg and breaks them down
                into 3 sets, plus (package.use enabled), minus ( package.use disabled),
                unset.
-               
+
                @param pkg: gentoolkit.package.Package object
                @rtype tuple of sets
                @return (plus, minus, unset) sets of USE flags
@@ -229,7 +236,9 @@ class FlagAnalyzer(object):
                return self._analyse(installed, iuse)
 
        def pkg_used(self, pkg):
-               return pkg.use().split()
+               if self.target == "USE":
+                       return pkg.use().split()
+               return pkg.environment(self.target).split()
 
        def pkg_flags(self, pkg):
                final_use, use_expand_hidden, usemasked, useforced = \
@@ -246,7 +255,7 @@ class FlagAnalyzer(object):
        def remove_expanding(self, flags):
                """Remove unwanted USE_EXPAND flags
                from unset IUSE sets
-               
+
                @param flags: short list or set of USE flags
                @rtype set
                @return USE flags
@@ -264,12 +273,12 @@ class FlagAnalyzer(object):
 class KeywordAnalyser(object):
        """Specialty functions for analysing the installed package db for
        keyword useage and the packages that used them.
-       
+
        Note: should be initialized with the internal set_order() before use.
        See internal set_order() for more details.
        This class of functions can be used for single cpv checks or
        used repeatedly for an entire package db.
-       
+
        @type  arch: string
        @param arch: the system ARCH setting
        @type  accept_keywords: list
@@ -302,9 +311,9 @@ class KeywordAnalyser(object):
                self.mismatched = []
 
        def determine_keyword(self, keywords, used, cpv):
-               """Determine the keyword from the installed USE flags and 
+               """Determine the keyword from the installed USE flags and
                the KEYWORDS that was used to install a package.
-               
+
                @param keywords: list of keywords available to install a pkg
                @param used: list of USE flalgs recorded for the installed pkg
                @rtype: string
@@ -396,7 +405,7 @@ class KeywordAnalyser(object):
 
        def get_inst_keyword_cpv(self, cpv):
                """Determines the installed with keyword for cpv
-               
+
                @type cpv: string
                @param cpv: an installed CAT/PKG-VER
                @rtype: string
@@ -409,7 +418,7 @@ class KeywordAnalyser(object):
 
        def get_inst_keyword_pkg(self, pkg):
                """Determines the installed with keyword for cpv
-               
+
                @param pkg: gentoolkit.package.Package object
                @rtype: string
                @returns a keyword determined to have been used to install cpv
@@ -447,11 +456,11 @@ class KeywordAnalyser(object):
        def set_order(self, used):
                """Used to set the parsing order to determine a keyword
                used for installation.
-               
+
                This is needed due to the way prefix arch's and keywords
                work with portage.  It looks for the 'prefix' flag. A positive result
                sets it to the prefix order and keyword.
-               
+
                @type used: list
                @param used: a list of pkg USE flags or the system USE flags"""
                if 'prefix' in used:
index 31c00a114040b3f37f017b00b8a3cbcc5bcbc262..c7cf1e5276c36cd1706f949110a2625e766a9244 100644 (file)
@@ -9,16 +9,12 @@
 """Provides a rebuild file of USE flags or keywords used and by
 what packages according to the Installed package database"""
 
-from __future__ import print_function
-
 
-# Move to Imports section after Python 2.6 is stable
+from __future__ import print_function
 
 
 import sys
 
-from portage import os
-
 import gentoolkit
 from gentoolkit.dbapi import PORTDB, VARDB
 from gentoolkit.analyse.base import ModuleBase
@@ -28,6 +24,7 @@ from gentoolkit.analyse.lib import (get_installed_use, get_flags,
 from gentoolkit.analyse.output import RebuildPrinter
 
 import portage
+from portage import os
 
 
 def cpv_all_diff_use(
@@ -37,12 +34,31 @@ def cpv_all_diff_use(
                _get_flags=get_flags,
                _get_used=get_installed_use
                ):
+       """Data gathering and analysis function determines
+       the difference between the current default USE flag settings
+       and the currently installed pkgs recorded USE flag settings
+
+       @type cpvs: list
+       @param cpvs: optional list of [cat/pkg-ver,...] to analyse or
+                       defaults to entire installed pkg db
+       @type: system_flags: list
+       @param system_flags: the current default USE flags as defined
+                       by portage.settings["USE"].split()
+       @type _get_flags: function
+       @param _get_flags: ovride-able for testing,
+                       defaults to gentoolkit.analyse.lib.get_flags
+       @param _get_used: ovride-able for testing,
+                       defaults to gentoolkit.analyse.lib.get_installed_use
+       @rtype dict. {cpv:['flag1', '-flag2',...]}
+       """
        if cpvs is None:
                cpvs = VARDB.cpv_all()
        cpvs.sort()
        data = {}
        # pass them in to override for tests
        flags = FlagAnalyzer(system_flags,
+               filter_defaults=True,
+               target="USE",
                _get_flags=_get_flags,
                _get_used=get_installed_use
        )
@@ -59,7 +75,7 @@ class Rebuild(ModuleBase):
        """Installed db analysis tool to query the installed databse
        and produce/output stats for USE flags or keywords/mask.
        The 'rebuild' action output is in the form suitable for file type output
-       to create a new package.use, package.keywords, package.unmask 
+       to create a new package.use, package.keywords, package.unmask
        type files in the event of needing to rebuild the
        /etc/portage/* user configs
        """
@@ -92,7 +108,7 @@ class Rebuild(ModuleBase):
                        ("  ", "leading '=' and include the version")
                ]
                self.formatted_args = [
-                       ("    use", 
+                       ("    use",
                        "causes the action to analyse the installed packages USE flags"),
                        ("    keywords",
                        "causes the action to analyse the installed packages keywords"),
@@ -106,12 +122,18 @@ class Rebuild(ModuleBase):
                self.arg_spec = "TargetSpec"
                self.arg_options = ['use', 'keywords', 'unmask']
                self.arg_option = False
+               self.warning = (
+                       "     CAUTION",
+                       "This is beta software and some features/options are incomplete,",
+                       "some features may change in future releases includig its name.",
+                       "The file generated is saved in your home directory",
+                       "Feedback will be appreciated, http://bugs.gentoo.org")
 
 
 
        def run(self, input_args, quiet=False):
                """runs the module
-               
+
                @param input_args: input arguments to be parsed
                """
                self.options['quiet'] = quiet
@@ -121,8 +143,8 @@ class Rebuild(ModuleBase):
                        self.rebuild_use()
                elif query in ["keywords"]:
                        self.rebuild_keywords()
-               elif query in ["mask"]:
-                       self.rebuild_mask()
+               elif query in ["unmask"]:
+                       self.rebuild_unmask()
 
 
        def rebuild_use(self):
@@ -140,11 +162,10 @@ class Rebuild(ModuleBase):
                                pp.emph(" packages that need entries")))
                        #print pp.emph("     package.use to maintain their current setting")
                if pkgs:
-                       pkg_keys = list(pkgs.keys())
-                       pkg_keys.sort()
+                       pkg_keys = sorted(pkgs)
                        #print len(pkgs)
                        if self.options["pretend"] and not self.options["quiet"]:
-                               print() 
+                               print()
                                print(pp.globaloption(
                                        "  -- These are the installed packages & flags " +
                                        "that were detected"))
@@ -183,29 +204,28 @@ class Rebuild(ModuleBase):
                print("Module action not yet available")
                print()
 
-       def rebuild_mask(self):
+       def rebuild_unmask(self):
                print("Module action not yet available")
                print()
 
 
        def save_file(self, filepath, data):
                """Writes the data to the file determined by filepath
-               
+
                @param filepath: string. eg. '/path/to/filename'
                @param data: list of lines to write to filepath
                """
                if  not self.options["quiet"]:
                        print('   - Saving file: %s' %filepath)
-               #print "Just kidding :)  I haven't codded it yet"
                with open(filepath, "w") as output:
                        output.write('\n'.join(data))
                print("   - Done")
 
 
 def main(input_args):
-       """Common starting method by the analyse master 
+       """Common starting method by the analyse master
        unless all modules are converted to this class method.
-       
+
        @param input_args: input args as supplied by equery master module.
        """
        query_module = Rebuild()
index 9819390b41bb57370640db12e82a2c8f53b6c24b..29d3279851b89d72b02759fbbe8db28e572b5513 100644 (file)
@@ -1,12 +1,11 @@
-# Copyright(c) 2009, Gentoo Foundation
+#!/usr/bin/python
 #
-# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
-# Copyright(c) 2010, Gentoo Foundation
+# Copyright(c) 2009 - 2010, Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 #
 # $Header: $
 
-"""Analyse Base Module class to hold common module operation functions
+"""Gentoolkit Base Module class to hold common module operation functions
 """
 
 from __future__ import print_function
index 9f2c30300d1cc492cfd85c23c374f2ea68bad665..663afea31d4c6543028aa986a2f6af8fd6ae1b14 100644 (file)
@@ -1,6 +1,8 @@
 #!/usr/bin/python
 #
-# Copyright(c) 2009, Gentoo Foundation
+# Copyright(c) 2005 Jason Stubbs <jstubbs@gentoo.org>                         
+# Copyright(c) 2005-2006 Brian Harring <ferringb@gmail.com>
+# Copyright(c) 2009-2010 Gentoo Foundation
 #
 # Licensed under the GNU General Public License, v2
 #
@@ -18,10 +20,21 @@ __all__ = (
 # Imports
 # =======
 
+import re
+
 from portage.versions import catpkgsplit, vercmp, pkgcmp
 
 from gentoolkit import errors
 
+# =======
+# Globals
+# =======
+
+isvalid_version_re = re.compile("^(?:cvs\\.)?(?:\\d+)(?:\\.\\d+)*[a-z]?"
+    "(?:_(p(?:re)?|beta|alpha|rc)\\d*)*$")
+isvalid_cat_re = re.compile("^(?:[a-zA-Z0-9][-a-zA-Z0-9+._]*(?:/(?!$))?)+$")
+_pkg_re = re.compile("^[a-zA-Z0-9+_]+$")
+
 # =======
 # Classes
 # =======
@@ -43,25 +56,63 @@ class CPV(object):
                True
        """
 
-       def __init__(self, cpv):
+       def __init__(self, cpv, validate=False):
                self.cpv = cpv
-
-               values = split_cpv(cpv)
-               self.category = values[0]
-               self.name = values[1]
-               self.version = values[2]
-               self.revision = values[3]
-               del values
-
-               if not self.name:
+               self._category = None
+               self._name = None
+               self._version = None
+               self._revision = None
+               self._cp = None
+               self._fullversion = None
+
+               self.validate = validate
+               if validate and not self.name:
                        raise errors.GentoolkitInvalidCPV(cpv)
 
-               sep = '/' if self.category else ''
-               self.cp = sep.join((self.category, self.name))
-
-               sep = '-' if self.revision else ''
-               self.fullversion = sep.join((self.version, self.revision))
-               del sep
+       @property
+       def category(self):
+               if self._category is None:
+                       self._set_cpv_chunks()
+               return self._category
+
+       @property
+       def name(self):
+               if self._name is None:
+                       self._set_cpv_chunks()
+               return self._name
+
+       @property
+       def version(self):
+               if self._version is None:
+                       self._set_cpv_chunks()
+               return self._version
+
+       @property
+       def revision(self):
+               if self._revision is None:
+                       self._set_cpv_chunks()
+               return self._revision
+
+       @property
+       def cp(self):
+               if self._cp is None:
+                       sep = '/' if self.category else ''
+                       self._cp = sep.join((self.category, self.name))
+               return self._cp
+
+       @property
+       def fullversion(self):
+               if self._fullversion is None:
+                       sep = '-' if self.revision else ''
+                       self._fullversion = sep.join((self.version, self.revision))
+               return self._fullversion
+
+       def _set_cpv_chunks(self):
+               chunks = split_cpv(self.cpv, validate=self.validate)
+               self._category = chunks[0]
+               self._name = chunks[1]
+               self._version = chunks[2]
+               self._revision = chunks[3]
 
        def __eq__(self, other):
                if not isinstance(other, self.__class__):
@@ -142,34 +193,63 @@ def compare_strs(pkg1, pkg2):
                return pkgcmp(pkg1[1:], pkg2[1:])
 
 
-def split_cpv(cpv):
+def split_cpv(cpv, validate=True):
        """Split a cpv into category, name, version and revision.
 
-       Inlined from helpers because of circular imports.
+       Modified from pkgcore.ebuild.cpv
 
-       @todo: this function is slow and accepts some crazy things for cpv
        @type cpv: str
-       @param cpv: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom or regex
+       @param cpv: pkg, cat/pkg, pkg-ver, cat/pkg-ver
        @rtype: tuple
        @return: (category, pkg_name, version, revision)
                Each tuple element is a string or empty string ("").
        """
 
-       result = catpkgsplit(cpv)
+       category = name = version = revision = ''
+
+       try:
+               category, pkgver = cpv.rsplit("/", 1)
+       except ValueError:
+               pkgver = cpv
+       if validate and category and not isvalid_cat_re.match(category):
+               raise errors.GentoolkitInvalidCPV(cpv)
+       pkg_chunks = pkgver.split("-")
+       lpkg_chunks = len(pkg_chunks)
+       if lpkg_chunks == 1:
+               return (category, pkg_chunks[0], version, revision)
+       if isvalid_rev(pkg_chunks[-1]):
+               if lpkg_chunks < 3:
+                       # needs at least ('pkg', 'ver', 'rev')
+                       raise errors.GentoolkitInvalidCPV(cpv)
+               rev = pkg_chunks.pop(-1)
+               if rev:
+                       revision = rev
+
+       if validate and not isvalid_version_re.match(pkg_chunks[-1]):
+               raise errors.GentoolkitInvalidCPV(cpv)
+       version = pkg_chunks.pop(-1)
+
+       if not isvalid_pkg_name(pkg_chunks):
+               raise errors.GentoolkitInvalidCPV(cpv)
+       name = '-'.join(pkg_chunks)
+
+       return (category, name, version, revision)
+
+
+def isvalid_pkg_name(chunks):
+    if not chunks[0]:
+        # this means a leading -
+        return False
+    mf = _pkg_re.match
+    if not all(not s or mf(s) for s in chunks):
+        return False
+    if chunks[-1].isdigit() or not chunks[-1]:
+        # not allowed.
+        return False
+    return True
 
-       if result:
-               result = list(result)
-               if result[0] == 'null':
-                       result[0] = ''
-               if result[3] == 'r0':
-                       result[3] = ''
-       else:
-               result = cpv.split("/")
-               if len(result) == 1:
-                       result = ['', cpv, '', '']
-               else:
-                       result = result + ['', '']
 
-       return tuple(result)
+def isvalid_rev(s):
+    return s and s[0] == 'r' and s[1:].isdigit()
 
 # vim: set ts=4 sw=4 tw=79:
diff --git a/pym/gentoolkit/eclean/__init__.py b/pym/gentoolkit/eclean/__init__.py
new file mode 100644 (file)
index 0000000..edf9385
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/python
+#
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
diff --git a/pym/gentoolkit/eclean/clean.py b/pym/gentoolkit/eclean/clean.py
new file mode 100644 (file)
index 0000000..9f6d597
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import sys
+
+from portage import os
+import gentoolkit.pprinter as pp
+from gentoolkit.eclean.pkgindex import PkgIndex
+
+
+class CleanUp(object):
+       """Performs all cleaning actions to distfiles or package directories.
+
+       @param controller: a progress output/user interaction controller function
+                                          which returns a Boolean to control file deletion
+                                          or bypassing/ignoring
+       """
+
+       def __init__(self, controller):
+               self.controller = controller
+
+       def clean_dist(self, clean_dict):
+               """Calculate size of each entry for display, prompt user if needed,
+               delete files if approved and return the total size of files that
+               have been deleted.
+
+               @param clean_dict: dictionary of {'display name':[list of files]}
+
+               @rtype: int
+               @return: total size that was cleaned
+               """
+               file_type = 'file'
+               clean_keys = self._sort_keys(clean_dict)
+               clean_size = 0
+               # clean all entries one by one
+               for key in clean_keys:
+                       clean_size += self._clean_files(clean_dict[key], key, file_type)
+               # return total size of deleted or to delete files
+               return clean_size
+
+       def clean_pkgs(self, clean_dict, pkgdir):
+               """Calculate size of each entry for display, prompt user if needed,
+               delete files if approved and return the total size of files that
+               have been deleted.
+
+               @param clean_dict:  dictionary of  {'display name':[list of files]}
+               @param metadata: package index of type portage.getbinpkg.PackageIndex()
+               @param pkgdir: path to the package directory to be cleaned
+
+               @rtype: int
+               @return: total size that was cleaned
+               """
+               file_type = 'binary package'
+               clean_keys = self._sort_keys(clean_dict)
+               clean_size = 0
+               # clean all entries one by one
+               for key in clean_keys:
+                       clean_size += self._clean_files(clean_dict[key], key, file_type)
+
+               #  run 'emaint --fix' here
+               if clean_size:
+                       index_control = PkgIndex(self.controller)
+                       # emaint is not yet importable so call it
+                       # print a blank line here for separation
+                       print()
+                       clean_size += index_control.call_emaint()
+               # return total size of deleted or to delete files
+               return clean_size
+
+
+       def pretend_clean(self, clean_dict):
+               """Shortcut function that calculates total space savings
+               for the files in clean_dict.
+
+               @param clean_dict: dictionary of {'display name':[list of files]}
+               @rtype: integer
+               @return: total size that would be cleaned
+               """
+               file_type = 'file'
+               clean_keys = self._sort_keys(clean_dict)
+               clean_size = 0
+               # tally all entries one by one
+               for key in clean_keys:
+                       key_size = self._get_size(clean_dict[key])
+                       self.controller(key_size, key, clean_dict[key], file_type)
+                       clean_size += key_size
+               return clean_size
+
+       def _get_size(self, key):
+               """Determine the total size for an entry (may be several files)."""
+               key_size = 0
+               for file_ in key:
+                       #print file_
+                       # get total size for an entry (may be several files, and
+                       # links don't count
+                       # ...get its statinfo
+                       try:
+                               statinfo = os.stat(file_)
+                               if statinfo.st_nlink == 1:
+                                       key_size += statinfo.st_size
+                       except EnvironmentError as er:
+                               print( pp.error(
+                                       "Could not get stat info for:" + file_), file=sys.stderr)
+                               print( pp.error("Error: %s" %str(er)), file=sys.stderr)
+               return key_size
+
+       def _sort_keys(self, clean_dict):
+               """Returns a list of sorted dictionary keys."""
+               # sorting helps reading
+               clean_keys = sorted(clean_dict)
+               return clean_keys
+
+       def _clean_files(self, files, key, file_type):
+               """File removal function."""
+               clean_size = 0
+               for file_ in files:
+                       #print file_, type(file_)
+                       # ...get its statinfo
+                       try:
+                               statinfo = os.stat(file_)
+                       except EnvironmentError as er:
+                               print( pp.error(
+                                       "Could not get stat info for:" + file_), file=sys.stderr)
+                               print( pp.error(
+                                       "Error: %s" %str(er)), file=sys.stderr)
+                       if self.controller(statinfo.st_size, key, file_, file_type):
+                               # ... try to delete it.
+                               try:
+                                       os.unlink(file_)
+                                       # only count size if successfully deleted and not a link
+                                       if statinfo.st_nlink == 1:
+                                               clean_size += statinfo.st_size
+                               except EnvironmentError as er:
+                                       print( pp.error("Could not delete "+file_), file=sys.stderr)
+                                       print( pp.error("Error: %s" %str(er)), file=sys.stderr)
+               return clean_size
+
+
+
+
+
+
+
diff --git a/pym/gentoolkit/eclean/cli.py b/pym/gentoolkit/eclean/cli.py
new file mode 100644 (file)
index 0000000..6a507ef
--- /dev/null
@@ -0,0 +1,500 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+__author__ = "Thomas de Grenier de Latour (tgl), " + \
+       "modular re-write by: Brian Dolbec (dol-sen)"
+__email__ = "degrenier@easyconnect.fr, " + \
+       "brian.dolbec@gmail.com"
+__version__ = "svn"
+__productname__ = "eclean"
+__description__ = "A cleaning tool for Gentoo distfiles and binaries."
+
+
+import sys
+import re
+import time
+import getopt
+
+import portage
+from portage import os
+from portage.output import white, yellow, turquoise, green, teal, red
+
+import gentoolkit.pprinter as pp
+from gentoolkit.eclean.search import (DistfilesSearch,
+       findPackages, port_settings, pkgdir)
+from gentoolkit.eclean.exclude import (parseExcludeFile,
+       ParseExcludeFileException)
+from gentoolkit.eclean.clean import CleanUp
+from gentoolkit.eclean.output import OutputControl
+#from gentoolkit.eclean.dbapi import Dbapi
+
+def printVersion():
+       """Output the version info."""
+       print( "%s (%s) - %s" \
+                       % (__productname__, __version__, __description__))
+       print()
+       print("Author: %s <%s>" % (__author__,__email__))
+       print("Copyright 2003-2009 Gentoo Foundation")
+       print("Distributed under the terms of the GNU General Public License v2")
+
+
+def printUsage(_error=None, help=None):
+       """Print help message. May also print partial help to stderr if an
+       error from {'options','actions'} is specified."""
+
+       out = sys.stdout
+       if _error:
+               out = sys.stderr
+       if not _error in ('actions', 'global-options', \
+                       'packages-options', 'distfiles-options', \
+                       'merged-packages-options', 'merged-distfiles-options', \
+                       'time', 'size'):
+               _error = None
+       if not _error and not help: help = 'all'
+       if _error == 'time':
+               print( pp.error("Wrong time specification"), file=out)
+               print( "Time specification should be an integer followed by a"+
+                               " single letter unit.", file=out)
+               print( "Available units are: y (years), m (months), w (weeks), "+
+                               "d (days) and h (hours).", file=out)
+               print( "For instance: \"1y\" is \"one year\", \"2w\" is \"two"+
+                               " weeks\", etc. ", file=out)
+               return
+       if _error == 'size':
+               print( pp.error("Wrong size specification"), file=out)
+               print( "Size specification should be an integer followed by a"+
+                               " single letter unit.", file=out)
+               print( "Available units are: G, M, K and B.", file=out)
+               print("For instance: \"10M\" is \"ten megabytes\", \"200K\" "+
+                               "is \"two hundreds kilobytes\", etc.", file=out)
+               return
+       if _error in ('global-options', 'packages-options', 'distfiles-options', \
+                       'merged-packages-options', 'merged-distfiles-options',):
+               print( pp.error("Wrong option on command line."), file=out)
+               print( file=out)
+       elif _error == 'actions':
+               print( pp.error("Wrong or missing action name on command line."), file=out)
+               print( file=out)
+       print( white("Usage:"), file=out)
+       if _error in ('actions','global-options', 'packages-options', \
+                       'distfiles-options') or help == 'all':
+               print( " "+turquoise(__productname__),
+                       yellow("[global-option] ..."),
+                       green("<action>"),
+                       yellow("[action-option] ..."), file=out)
+       if _error == 'merged-distfiles-options' or help in ('all','distfiles'):
+               print( " "+turquoise(__productname__+'-dist'),
+                       yellow("[global-option, distfiles-option] ..."), file=out)
+       if _error == 'merged-packages-options' or help in ('all','packages'):
+               print( " "+turquoise(__productname__+'-pkg'),
+                       yellow("[global-option, packages-option] ..."), file=out)
+       if _error in ('global-options', 'actions'):
+               print( " "+turquoise(__productname__),
+                       yellow("[--help, --version]"), file=out)
+       if help == 'all':
+               print( " "+turquoise(__productname__+"(-dist,-pkg)"),
+                       yellow("[--help, --version]"), file=out)
+       if _error == 'merged-packages-options' or help == 'packages':
+               print( " "+turquoise(__productname__+'-pkg'),
+                       yellow("[--help, --version]"), file=out)
+       if _error == 'merged-distfiles-options' or help == 'distfiles':
+               print( " "+turquoise(__productname__+'-dist'),
+                       yellow("[--help, --version]"), file=out)
+       print(file=out)
+       if _error in ('global-options', 'merged-packages-options', \
+       'merged-distfiles-options') or help:
+               print( "Available global", yellow("options")+":", file=out)
+               print( yellow(" -C, --nocolor")+
+                       "            - turn off colors on output", file=out)
+               print( yellow(" -d, --destructive")+
+                       "        - only keep the minimum for a reinstallation", file=out)
+               print( yellow(" -e, --exclude-file=<path>")+
+                       " - path to the exclusion file", file=out)
+               print( yellow(" -i, --interactive")+
+                       "        - ask confirmation before deletions", file=out)
+               print( yellow(" -n, --package-names")+
+                       "      - protect all versions (when --destructive)", file=out)
+               print( yellow(" -p, --pretend")+
+                       "            - only display what would be cleaned", file=out)
+               print( yellow(" -q, --quiet")+
+                       "              - be as quiet as possible", file=out)
+               print( yellow(" -t, --time-limit=<time>")+
+                       "   - don't delete files modified since "+yellow("<time>"), file=out)
+               print( "   "+yellow("<time>"), "is a duration: \"1y\" is"+
+                               " \"one year\", \"2w\" is \"two weeks\", etc. ", file=out)
+               print( "   "+"Units are: y (years), m (months), w (weeks), "+
+                               "d (days) and h (hours).", file=out)
+               print( yellow(" -h, --help")+ \
+                       "               - display the help screen", file=out)
+               print( yellow(" -V, --version")+
+                       "            - display version info", file=out)
+               print( file=out)
+       if _error == 'actions' or help == 'all':
+               print( "Available", green("actions")+":", file=out)
+               print( green(" packages")+
+                       "     - clean outdated binary packages from PKGDIR", file=out)
+               print( green(" distfiles")+
+                       "    - clean outdated packages sources files from DISTDIR", file=out)
+               print( file=out)
+       if _error in ('packages-options','merged-packages-options') \
+       or help in ('all','packages'):
+               print( "Available", yellow("options"),"for the",
+                               green("packages"),"action:", file=out)
+               print( yellow(" NONE  :)"), file=out)
+               print( file=out)
+       if _error in ('distfiles-options', 'merged-distfiles-options') \
+       or help in ('all','distfiles'):
+               print("Available", yellow("options"),"for the",
+                               green("distfiles"),"action:", file=out)
+               print( yellow(" -f, --fetch-restricted")+
+                       "   - protect fetch-restricted files (when --destructive)", file=out)
+               print( yellow(" -s, --size-limit=<size>")+
+                       "  - don't delete distfiles bigger than "+yellow("<size>"), file=out)
+               print( "   "+yellow("<size>"), "is a size specification: "+
+                               "\"10M\" is \"ten megabytes\", \"200K\" is", file=out)
+               print( "   "+"\"two hundreds kilobytes\", etc.  Units are: "+
+                               "G, M, K and B.", file=out)
+               print( file=out)
+       print( "More detailed instruction can be found in",
+                       turquoise("`man %s`" % __productname__), file=out)
+
+
+class ParseArgsException(Exception):
+       """For parseArgs() -> main() communications."""
+       def __init__(self, value):
+               self.value = value # sdfgsdfsdfsd
+       def __str__(self):
+               return repr(self.value)
+
+
+def parseSize(size):
+       """Convert a file size "Xu" ("X" is an integer, and "u" in
+       [G,M,K,B]) into an integer (file size in Bytes).
+
+       @raise ParseArgsException: in case of failure
+       """
+       units = {
+               'G': (1024**3),
+               'M': (1024**2),
+               'K': 1024,
+               'B': 1
+       }
+       try:
+               match = re.match(r"^(?P<value>\d+)(?P<unit>[GMKBgmkb])?$",size)
+               size = int(match.group('value'))
+               if match.group('unit'):
+                       size *= units[match.group('unit').capitalize()]
+       except:
+               raise ParseArgsException('size')
+       return size
+
+
+def parseTime(timespec):
+       """Convert a duration "Xu" ("X" is an int, and "u" a time unit in
+       [Y,M,W,D,H]) into an integer which is a past EPOCH date.
+       Raises ParseArgsException('time') in case of failure.
+       (yep, big approximations inside... who cares?).
+       """
+       units = {'H' : (60 * 60)}
+       units['D'] = units['H'] * 24
+       units['W'] = units['D'] * 7
+       units['M'] = units['D'] * 30
+       units['Y'] = units['D'] * 365
+       try:
+               # parse the time specification
+               match = re.match(r"^(?P<value>\d+)(?P<unit>[YMWDHymwdh])?$",timespec)
+               value = int(match.group('value'))
+               if not match.group('unit'): unit = 'D'
+               else: unit = match.group('unit').capitalize()
+       except:
+               raise ParseArgsException('time')
+       return time.time() - (value * units[unit])
+
+
+def parseArgs(options={}):
+       """Parse the command line arguments. Raise exceptions on
+       errors or non-action modes (help/version). Returns an action, and affect
+       the options dict.
+       """
+
+       def optionSwitch(option,opts,action=None):
+               """local function for interpreting command line options
+               and setting options accordingly"""
+               return_code = True
+               for o, a in opts:
+                       if o in ("-h", "--help"):
+                               if action:
+                                       raise ParseArgsException('help-'+action)
+                               else:
+                                       raise ParseArgsException('help')
+                       elif o in ("-V", "--version"):
+                               raise ParseArgsException('version')
+                       elif o in ("-C", "--nocolor"):
+                               options['nocolor'] = True
+                               pp.output.nocolor()
+                       elif o in ("-d", "--destructive"):
+                               options['destructive'] = True
+                       elif o in ("-D", "--deprecated"):
+                               options['deprecated'] = True
+                       elif o in ("-i", "--interactive") and not options['pretend']:
+                               options['interactive'] = True
+                       elif o in ("-p", "--pretend"):
+                               options['pretend'] = True
+                               options['interactive'] = False
+                       elif o in ("-q", "--quiet"):
+                               options['quiet'] = True
+                               options['verbose'] = False
+                       elif o in ("-t", "--time-limit"):
+                               options['time-limit'] = parseTime(a)
+                       elif o in ("-e", "--exclude-file"):
+                               options['exclude-file'] = a
+                       elif o in ("-n", "--package-names"):
+                               options['package-names'] = True
+                       elif o in ("-f", "--fetch-restricted"):
+                               options['fetch-restricted'] = True
+                       elif o in ("-s", "--size-limit"):
+                               options['size-limit'] = parseSize(a)
+                       elif o in ("-v", "--verbose") and not options['quiet']:
+                                       options['verbose'] = True
+                       else:
+                               return_code = False
+               # sanity check of --destructive only options:
+               for opt in ('fetch-restricted', 'package-names'):
+                       if (not options['destructive']) and options[opt]:
+                               if not options['quiet']:
+                                       print( pp.error(
+                                               "--%s only makes sense in --destructive mode." % opt), file=sys.stderr)
+                               options[opt] = False
+               return return_code
+
+       # here are the different allowed command line options (getopt args)
+       getopt_options = {'short':{}, 'long':{}}
+       getopt_options['short']['global'] = "CdDipqe:t:nhVv"
+       getopt_options['long']['global'] = ["nocolor", "destructive",
+               "deprecated", "interactive", "pretend", "quiet", "exclude-file=",
+               "time-limit=", "package-names", "help", "version",  "verbose"]
+       getopt_options['short']['distfiles'] = "fs:"
+       getopt_options['long']['distfiles'] = ["fetch-restricted", "size-limit="]
+       getopt_options['short']['packages'] = ""
+       getopt_options['long']['packages'] = [""]
+       # set default options, except 'nocolor', which is set in main()
+       options['interactive'] = False
+       options['pretend'] = False
+       options['quiet'] = False
+       options['accept_all'] = False
+       options['destructive'] = False
+       options['deprecated'] = False
+       options['time-limit'] = 0
+       options['package-names'] = False
+       options['fetch-restricted'] = False
+       options['size-limit'] = 0
+       options['verbose'] = False
+       # if called by a well-named symlink, set the acction accordingly:
+       action = None
+       # temp print line to ensure it is the svn/branch code running, etc..
+       #print(  "###### svn/branch/gentoolkit_eclean ####### ==> ", os.path.basename(sys.argv[0]))
+       if os.path.basename(sys.argv[0]) in \
+                       (__productname__+'-pkg', __productname__+'-packages'):
+               action = 'packages'
+       elif os.path.basename(sys.argv[0]) in \
+                       (__productname__+'-dist', __productname__+'-distfiles'):
+               action = 'distfiles'
+       # prepare for the first getopt
+       if action:
+               short_opts = getopt_options['short']['global'] \
+                       + getopt_options['short'][action]
+               long_opts = getopt_options['long']['global'] \
+                       + getopt_options['long'][action]
+               opts_mode = 'merged-'+action
+       else:
+               short_opts = getopt_options['short']['global']
+               long_opts = getopt_options['long']['global']
+               opts_mode = 'global'
+       # apply getopts to command line, show partial help on failure
+       try:
+               opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
+       except:
+               raise ParseArgsException(opts_mode+'-options')
+       # set options accordingly
+       optionSwitch(options,opts,action=action)
+       # if action was already set, there should be no more args
+       if action and len(args):
+               raise ParseArgsException(opts_mode+'-options')
+       # if action was set, there is nothing left to do
+       if action:
+               return action
+       # So, we are in "eclean --foo action --bar" mode. Parse remaining args...
+       # Only two actions are allowed: 'packages' and 'distfiles'.
+       if not len(args) or not args[0] in ('packages','distfiles'):
+               raise ParseArgsException('actions')
+       action = args.pop(0)
+       # parse the action specific options
+       try:
+               opts, args = getopt.getopt(args, \
+                       getopt_options['short'][action], \
+                       getopt_options['long'][action])
+       except:
+               raise ParseArgsException(action+'-options')
+       # set options again, for action-specific options
+       optionSwitch(options,opts,action=action)
+       # any remaning args? Then die!
+       if len(args):
+               raise ParseArgsException(action+'-options')
+       # returns the action. Options dictionary is modified by side-effect.
+       return action
+
+
+def doAction(action,options,exclude={}, output=None):
+       """doAction: execute one action, ie display a few message, call the right
+       find* function, and then call doCleanup with its result."""
+       # define vocabulary for the output
+       if action == 'packages':
+               files_type = "binary packages"
+       else:
+               files_type = "distfiles"
+       saved = {}
+       deprecated = {}
+       # find files to delete, depending on the action
+       if not options['quiet']:
+               output.einfo("Building file list for "+action+" cleaning...")
+       if action == 'packages':
+               clean_me = findPackages(
+                       options,
+                       exclude=exclude,
+                       destructive=options['destructive'],
+                       package_names=options['package-names'],
+                       time_limit=options['time-limit'],
+                       pkgdir=pkgdir,
+                       #port_dbapi=Dbapi(portage.db[portage.root]["porttree"].dbapi),
+                       #var_dbapi=Dbapi(portage.db[portage.root]["vartree"].dbapi),
+               )
+       else:
+               # accept defaults
+               engine = DistfilesSearch(output=options['verbose-output'],
+                       #portdb=Dbapi(portage.db[portage.root]["porttree"].dbapi),
+                       #var_dbapi=Dbapi(portage.db[portage.root]["vartree"].dbapi),
+               )
+               clean_me, saved, deprecated = engine.findDistfiles(
+                       exclude=exclude,
+                       destructive=options['destructive'],
+                       fetch_restricted=options['fetch-restricted'],
+                       package_names=options['package-names'],
+                       time_limit=options['time-limit'],
+                       size_limit=options['size-limit'],
+                       deprecate = options['deprecated']
+               )
+       # actually clean files if something was found
+       if clean_me:
+               # verbose pretend message
+               if options['pretend'] and not options['quiet']:
+                       output.einfo("Here are the "+files_type+" that would be deleted:")
+               # verbose non-pretend message
+               elif not options['quiet']:
+                       output.einfo("Cleaning " + files_type  +"...")
+               # do the cleanup, and get size of deleted files
+               cleaner = CleanUp( output.progress_controller)
+               if  options['pretend']:
+                       clean_size = cleaner.pretend_clean(clean_me)
+               elif action in ['distfiles']:
+                       clean_size = cleaner.clean_dist(clean_me)
+               elif action in ['packages']:
+                       clean_size = cleaner.clean_pkgs(clean_me,
+                               pkgdir)
+               # vocabulary for final message
+               if options['pretend']:
+                       verb = "would be"
+               else:
+                       verb = "were"
+               # display freed space
+               if not options['quiet']:
+                       output.total('normal', clean_size, len(clean_me), verb, action)
+       # nothing was found, return
+       elif not options['quiet']:
+               output.einfo("Your "+action+" directory was already clean.")
+       if saved and not options['quiet']:
+               print()
+               print( (pp.emph("   The folowing ") + yellow("Deprecated") +
+                       pp.emph(" files were saved from cleaning due to exclusion file entries")))
+               output.set_colors('deprecated')
+               clean_size = cleaner.pretend_clean(saved)
+               output.total('deprecated', clean_size, len(saved), verb, action)
+       if deprecated and not options['quiet']:
+               print()
+               print( (pp.emph("   The folowing ") + yellow("Deprecated") +
+                       pp.emph(" installed packages were found")))
+               output.set_colors('deprecated')
+               output.list_pkgs(deprecated)
+
+
+def main():
+       """Parse command line and execute all actions."""
+       # set default options
+       options = {}
+       options['nocolor'] = (port_settings["NOCOLOR"] in ('yes','true')
+               or not sys.stdout.isatty())
+       if options['nocolor']:
+               pp.output.nocolor()
+       # parse command line options and actions
+       try:
+               action = parseArgs(options)
+       # filter exception to know what message to display
+       except ParseArgsException as e:
+               if e.value == 'help':
+                       printUsage(help='all')
+                       sys.exit(0)
+               elif e.value[:5] == 'help-':
+                       printUsage(help=e.value[5:])
+                       sys.exit(0)
+               elif e.value == 'version':
+                       printVersion()
+                       sys.exit(0)
+               else:
+                       printUsage(e.value)
+                       sys.exit(2)
+       output = OutputControl(options)
+       options['verbose-output'] = lambda x: None
+       if not options['quiet']:
+               if options['verbose']:
+                       options['verbose-output'] = output.einfo
+       # parse the exclusion file
+       if 'exclude-file' in options:
+               try:
+                       exclude = parseExcludeFile(options['exclude-file'],
+                                       options['verbose-output'])
+               except ParseExcludeFileException as e:
+                       print( pp.error(str(e)), file=sys.stderr)
+                       print( pp.error(
+                               "Invalid exclusion file: %s" % options['exclude-file']), file=sys.stderr)
+                       print( pp.error(
+                               "See format of this file in `man %s`" % __productname__), file=sys.stderr)
+                       sys.exit(1)
+       else:
+               exclude_file = "/etc/%s/%s.exclude" % (__productname__ , action)
+               if os.path.isfile(exclude_file):
+                       options['exclude-file'] = exclude_file
+               exclude={}
+       # security check for non-pretend mode
+       if not options['pretend'] and portage.secpass == 0:
+               print( pp.error(
+                       "Permission denied: you must be root or belong to " +
+                       "the portage group."), file=sys.stderr)
+               sys.exit(1)
+       # execute action
+       doAction(action, options, exclude=exclude,
+               output=output)
+
+
+if __name__ == "__main__":
+       """actually call main() if launched as a script"""
+       try:
+               main()
+       except KeyboardInterrupt:
+               print( "Aborted.")
+               sys.exit(130)
+       sys.exit(0)
diff --git a/pym/gentoolkit/eclean/exclude.py b/pym/gentoolkit/eclean/exclude.py
new file mode 100644 (file)
index 0000000..74a982a
--- /dev/null
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import sys
+import re
+import portage
+
+from portage import os
+from gentoolkit.pprinter import warn
+
+# Misc. shortcuts to some portage stuff:
+listdir = portage.listdir
+
+FILENAME_RE = [re.compile(r'(?P<pkgname>[-a-zA-z0-9\+]+)(?P<ver>-\d+\S+)'),
+       re.compile(r'(?P<pkgname>[-a-zA-z]+)(?P<ver>_\d+\S+)'),
+       re.compile(r'(?P<pkgname>[-a-zA-z_]+)(?P<ver>\d\d+\S+)'),
+       re.compile(r'(?P<pkgname>[-a-zA-z0-9_]+)(?P<ver>-default\S+)'),
+       re.compile(r'(?P<pkgname>[-a-zA-z0-9]+)(?P<ver>_\d\S+)'),
+       re.compile(r'(?P<pkgname>[-a-zA-z0-9\+\.]+)(?P<ver>-\d+\S+)'),
+       re.compile(r'(?P<pkgname>[-a-zA-z0-9\+\.]+)(?P<ver>.\d+\S+)')]
+
+debug_modules = []
+
+def dprint(module, message):
+       if module in debug_modules:
+               print(message)
+
+def isValidCP(cp):
+       """Check whether a string is a valid cat/pkg-name.
+
+       This is for 2.0.51 vs. CVS HEAD compatibility, I've not found any function
+       for that which would exists in both. Weird...
+
+       @param cp: catageory/package string
+       @rtype: bool
+       """
+
+       if not '/' in cp:
+               return False
+       try:
+               portage.cpv_getkey(cp+"-0")
+       except:
+               return False
+       else:
+               return True
+
+
+class ParseExcludeFileException(Exception):
+       """For parseExcludeFile() -> main() communication.
+
+       @param value: Error message string
+       """
+       def __init__(self, value):
+               self.value = value
+       def __str__(self):
+               return repr(self.value)
+
+
+def parseExcludeFile(filepath, output):
+       """Parses an exclusion file.
+
+       @param filepath: file containing the list of cat/pkg's to exclude
+       @param output: --verbose enabled output method or "lambda x: None"
+
+       @rtype: dict
+       @return: an exclusion dict
+       @raise ParseExcludeFileException: in case of fatal error
+       """
+
+       exclude = {
+                       'categories': {},
+                       'packages': {},
+                       'anti-packages': {},
+                       'filenames': {}
+               }
+       output("Parsing Exclude file: " + filepath)
+       try:
+               file_ = open(filepath,"r")
+       except IOError:
+               raise ParseExcludeFileException("Could not open exclusion file: " +
+                       filepath)
+       filecontents = file_.readlines()
+       file_.close()
+       cat_re = re.compile('^(?P<cat>[a-zA-Z0-9]+-[a-zA-Z0-9]+)(/\*)?$')
+       cp_re = re.compile('^(?P<cp>[-a-zA-Z0-9_]+/[-a-zA-Z0-9_]+)$')
+       # used to output the line number for exception error reporting
+       linenum = 0
+       for line in filecontents:
+               # need to increment it here due to continue statements.
+               linenum += 1
+               line = line.strip()
+               if not len(line): # skip blank a line
+                       continue
+               if line[0] == '#': # skip a comment line
+                       continue
+               #print( "parseExcludeFile: line=", line)
+               try: # category matching
+                       cat = cat_re.match(line).group('cat')
+                       #print( "parseExcludeFile: found cat=", cat)
+               except:
+                       pass
+               else:
+                       if not cat in portage.settings.categories:
+                               raise ParseExcludeFileException("Invalid category: "+cat +
+                                       " @line # " + str(linenum))
+                       exclude['categories'][cat] = None
+                       continue
+               dict_key = 'packages'
+               if line[0] == '!': # reverses category setting
+                       dict_key = 'anti-packages'
+                       line = line[1:]
+               try: # cat/pkg matching
+                       cp = cp_re.match(line).group('cp')
+                       #print( "parseExcludeFile: found cp=", cp)
+                       if isValidCP(cp):
+                               exclude[dict_key][cp] = None
+                               continue
+                       else:
+                               raise ParseExcludeFileException("Invalid cat/pkg: "+cp +
+                                       " @line # " + str(linenum))
+               except:
+                       pass
+               #raise ParseExcludeFileException("Invalid line: "+line)
+               try: # filename matching.
+                       exclude['filenames'][line] = re.compile(line)
+                       #print( "parseExcludeFile: found filenames", line)
+               except:
+                       try:
+                               exclude['filenames'][line] = re.compile(re.escape(line))
+                               #print( "parseExcludeFile: found escaped filenames", line)
+                       except:
+                               raise ParseExcludeFileException("Invalid file name/regular " +
+                                       "expression: @line # " + str(linenum) + " line=" +line)
+       output("Exclude file parsed. Found " +
+               "%d categories, %d packages, %d anti-packages %d filenames"
+               %(len(exclude['categories']), len(exclude['packages']),
+               len(exclude['anti-packages']), len(exclude['filenames'])))
+       #print()
+       #print( "parseExcludeFile: final exclude_dict = ", exclude)
+       #print()
+       return exclude
+
+def cp_all(categories):
+               """temp function until the new portdb.cp_all([cat,...])
+               behaviour is fully available.
+
+               @param categories: list of categories to get all packages for
+                               eg. ['app-portage', 'sys-apps',...]
+               @rtype: list of cat/pkg's  ['foo/bar', 'foo/baz']
+               """
+               try:
+                       cps = portage.portdb.cp_all(categories)
+                       message = "Deprication Warning: eclean.exclude.cp_all()\n" + \
+                               "New portage functionality is available " +\
+                               "Please migrate code permanently"
+                       print( warn(message), file=sys.stderr)
+               except:  # new behaviour not available
+                       #~ message =  "Exception: eclean.exclude.cp_all() " +\
+                               #~ "new portdb.cp_all() behavior not found. using fallback code"
+                       #~ print( warn(message), file=sys.stderr)
+                       cps = []
+                       # XXX: i smell an access to something which is really out of API...
+                       _pkg_dir_name_re = re.compile(r'^\w[-+\w]*$')
+                       for tree in portage.portdb.porttrees:
+                               for cat in categories:
+                                       for pkg in listdir(os.path.join(tree,cat),
+                                                               EmptyOnError=1, ignorecvs=1, dirsonly=1):
+                                               if not _pkg_dir_name_re.match(pkg) or pkg == "CVS":
+                                                       continue
+                                               cps.append(cat+'/'+pkg)
+               #print( "cp_all: new cps list=", cps)
+               return cps
+
+def exclDictExpand(exclude):
+       """Returns a dictionary of all CP/CPV from porttree which match
+       the exclusion dictionary.
+       """
+       d = {}
+       if 'categories' in exclude:
+               # replace the following cp_all call with
+               # portage.portdb.cp_all([cat1, cat2])
+               # when it is available in all portage versions.
+               cps = cp_all(exclude['categories'])
+               for cp in cps:
+                       d[cp] = None
+       if 'packages' in exclude:
+               for cp in exclude['packages']:
+                       d[cp] = None
+       if 'anti-packages' in exclude:
+               for cp in exclude['anti-packages']:
+                       if cp in d:
+                               del d[cp]
+       return d
+
+def exclDictMatchCP(exclude,pkg):
+       """Checks whether a CP matches the exclusion rules."""
+       if 'anti-packages' in exclude and pkg in exclude['anti-packages']:
+               return False
+       if 'packages' in exclude and pkg in exclude['packages']:
+               return True
+       cat = pkg.split('/')[0]
+       if 'categories' in exclude and cat in exclude['categories']:
+               return True
+       return False
+
+def exclDictExpandPkgname(exclude):
+       """Returns a set of all pkgnames  from porttree which match
+       the exclusion dictionary.
+       """
+       p = set()
+       if 'categories' in exclude:
+               # replace the following cp_all call with
+               # portage.portdb.cp_all([cat1, cat2])
+               # when it is available in all portage versions.
+               cps = cp_all(exclude['categories'])
+               for cp in cps:
+                       pkgname = cp.split('/')[1]
+                       p.add(pkgname)
+       if 'packages' in exclude:
+                       pkgname = cp.split('/')[1]
+                       p.add(pkgname)
+       if 'anti-packages' in exclude:
+               for cp in exclude['anti-packages']:
+                       if cp in p:
+                               p.remove(cp)
+       return p
+
+
+def exclMatchFilename(exclude_names, filename):
+       """Attempts to split the package name out of a filename
+       and then checks if it matches any exclusion rules.
+
+       This is intended to be run on the cleaning list after all
+       normal checks and removal of protected files.  This will reduce
+       the number of files to perform this last minute check on
+
+       @param exclude_names: a set of pkgnames to exlcude
+       @param filename:
+
+       @rtype: bool
+       """
+       found = False
+       index = 0
+       while not found and index < len(FILENAME_RE):
+               found = FILENAME_RE[index].match(filename)
+               index += 1
+       if not found:
+               dprint( "exclude", "exclMatchFilename: filename: " +\
+                       "%s, Could not determine package name" %filename)
+               return False
+       pkgname = found.group('pkgname')
+       dprint("exclude", "exclMatchFilename: found pkgname = " +
+               "%s, %s, %d, %s" %(pkgname, str(pkgname in exclude_names),
+               index-1, filename))
+       return (pkgname in exclude_names)
+
diff --git a/pym/gentoolkit/eclean/output.py b/pym/gentoolkit/eclean/output.py
new file mode 100644 (file)
index 0000000..670e67c
--- /dev/null
@@ -0,0 +1,179 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import sys
+import portage
+from portage.output import *
+from gentoolkit.pprinter import cpv, number, emph
+
+
+class OutputControl(object):
+       """Outputs data according to predetermined options and handles any user
+       interaction.
+
+       @param options: dictionary of boolean options as determined in cli.py
+                       used here: interactive, pretend, quiet, accept_all, nocolor.
+       """
+
+       def __init__(self, options):
+               if not options:
+                       # set some defaults
+                       self.options['interactive'] = False
+                       self.options['pretend'] = True
+                       self.options['quiet'] = False
+                       self.options['accept_all'] = False
+                       self.options['nocolor'] = False
+               else:
+                       self.options = options
+               self.set_colors("normal")
+
+       def set_colors(self, mode):
+               """Sets the colors for the progress_controller
+               and prettysize output
+
+               @param mode: string, 1 of ["normal", "deprecated"]
+               """
+               if mode == "normal":
+                       self.pkg_color = cpv        # green
+                       self.numbers = number  # turquoise
+                       self.brace = blue
+               elif mode == "deprecated":
+                       self.pkg_color = yellow
+                       self.numbers =  teal # darkgreen
+                       self.brace = blue
+
+       def einfo(self, message=""):
+               """Display an info message depending on a color mode.
+
+               @param message: text string to display
+
+               @outputs to stdout.
+               """
+               if not  self.options['nocolor']:
+                       prefix = " "+green('*')
+               else:
+                       prefix = ">>>"
+               print(prefix,message)
+
+       def eprompt(self, message):
+               """Display a user question depending on a color mode.
+
+               @param message: text string to display
+
+               @output to stdout
+               """
+               if not self.options['nocolor']:
+                       prefix = " "+red('>')+" "
+               else:
+                       prefix = "??? "
+               sys.stdout.write(prefix+message)
+               sys.stdout.flush()
+
+       def prettySize(self, size, justify=False, color=None):
+               """int -> byte/kilo/mega/giga converter. Optionally
+               justify the result. Output is a string.
+
+               @param size: integer
+               @param justify: optional boolean, defaults to False
+               @param color: optional color, defaults to green
+                               as defined in portage.output
+
+               @returns a formatted and (escape sequenced)
+                               colorized text string
+               """
+               if color == None:
+                       color = self.numbers
+               units = [" G"," M"," K"," B"]
+               approx = 0
+               while len(units) and size >= 1000:
+                       approx = 1
+                       size = size / 1024.
+                       units.pop()
+               sizestr = '%d'% size + units[-1]
+               if justify:
+                       sizestr = " " + self.brace("[ ")  + \
+                               color(sizestr.rjust(7)) + self.brace(" ]")
+               return sizestr
+
+       def yesNoAllPrompt(self, message="Do you want to proceed?"):
+               """Print a prompt until user answer in yes/no/all. Return a
+               boolean for answer, and also may affect the 'accept_all' option.
+
+               @param message: optional different input string from the default
+                               message of: "Do you want to proceed?"
+               @outputs to stdout
+               @modifies class var options['accept_all']
+               @rtype: bool
+               """
+               user_string="xxx"
+               while not user_string.lower() in ["","y","n","a","yes","no","all"]:
+                       self.eprompt(message+" [Y/n/a]: ")
+                       user_string =  sys.stdin.readline().rstrip('\n')
+                       user_string = user_string.strip()
+               if user_string.lower() in ["a","all"]:
+                       self.options['accept_all'] = True
+               answer = user_string.lower() in ["","y","a","yes","all"]
+               return answer
+
+       def progress_controller(self, size, key, clean_list, file_type):
+               """Callback function for doCleanup. It outputs data according to the
+               options configured.
+               Alternatively it handles user interaction for decisions that are
+               required.
+
+               @param size: Integer of the file(s) size
+               @param key: the filename/pkgname currently being processed
+               @param clean_list: list of files being processed.
+               """
+               if not self.options['quiet']:
+                       # pretty print mode
+                       print(self.prettySize(size,True), self.pkg_color(key))
+               elif self.options['pretend'] or self.options['interactive']:
+                       # file list mode
+                       for file_ in clean_list:
+                               print(file_)
+               if self.options['pretend']:
+                       return False
+               elif not self.options['interactive'] \
+                       or self.options['accept_all'] \
+                       or self.yesNoAllPrompt("Do you want to delete this " + file_type + "?"):
+                       return True
+               return False
+
+       def total(self, mode, size, num_files, verb, action):
+               """outputs the formatted totals to stdout
+
+               @param mode: sets color and message. 1 of ['normal', 'deprecated']
+               @param size: total space savings
+               @param num_files: total number of files
+               @param verb: string eg. 1 of ["would be", "has been"]
+               @param action: string eg 1 of ['distfiles', 'packages']
+               """
+               self.set_colors(mode)
+               if mode =="normal":
+                       message="Total space from "+red(str(num_files))+" files "+\
+                               verb+" freed in the " + action + " directory"
+                       print( " ===========")
+                       print( self.prettySize(size, True, red), message)
+               elif mode == "deprecated":
+                       message = "Total space from "+red(str(num_files))+" package files\n"+\
+                               "   Re-run the last command with the -D " +\
+                               "option to clean them as well"
+                       print( " ===========")
+                       print( self.prettySize(size, True, red), message)
+
+       def list_pkgs(self, pkgs):
+               """outputs the packages to stdout
+
+               @param pkgs: dict. of {cat/pkg-ver: src_uri,}
+               """
+               indent = ' ' * 12
+               for key in pkgs:
+                       print( indent,self.pkg_color(key))
+               print()
diff --git a/pym/gentoolkit/eclean/pkgindex.py b/pym/gentoolkit/eclean/pkgindex.py
new file mode 100644 (file)
index 0000000..f9d9f3c
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from __future__ import print_function
+
+
+import subprocess
+import sys
+
+import gentoolkit.pprinter as pp
+
+import portage
+from portage import os
+
+
+class PkgIndex(object):
+       """Handle the cleaning of the binpkg Package
+       Index file
+
+       @type output: class
+       @param output: optional output class for printing
+       """
+
+       def __init__(self, controller=None):
+               self.controller = controller
+
+
+       def _get_emaint_binhost(self):
+               """Obtain a reference to the binhost module class
+
+               @sets: self.binhost to BinhostHandler class
+               @rtype: boolean
+               """
+               try:
+                       self.emaint_control = Modules()
+                       self.binhost = self.emaint_control._get_class('binhost')
+               except InvalidModuleName as er:
+                       print( pp.error("Error importing emaint binhost module"), file=sys.stderr)
+                       print( pp.error("Original error: " + er), file=sys.stderr)
+               except:
+                       return False
+               return True
+
+
+       def _load_modules(self):
+               """Import the emaint modules and report the success/fail of them
+               """
+               try:
+                       from emaint.module import Modules
+                       from emaint.main import TaskHandler
+               except ImportError as e:
+                       return False
+               return True
+
+
+       def clean_pkgs_index(self,):
+               """This will clean the binpkgs packages index file"""
+               go = self._load_modules()
+               if go:
+                       if self.get_emaint_binhost():
+                               self.taskmaster = TaskHandler(show_progress_bar=True)
+                               tasks = [self.binhost]
+                               self.taskmaster.run_tasks(tasks)
+
+
+       def call_emaint(self):
+               """Run the stand alone emaint script from
+               a subprocess call.
+
+               @rtype: integer
+               @return: the difference in file size
+               """
+               file_ = os.path.join(portage.settings['PKGDIR'], 'Packages')
+               statinfo = os.stat(file_)
+               size1 = statinfo.st_size
+               command = "emaint --fix binhost"
+               try:
+                       retcode = subprocess.call(command, shell=True)
+                       if retcode < 0:
+                               print( pp.error("Child was terminated by signal" + str(-retcode)), file=sys.stderr)
+               except OSError as e:
+                       print( pp.error("Execution failed:" + e), file=sys.stderr)
+               print()
+               statinfo = os.stat(file_)
+               clean_size = size1 - statinfo.st_size
+               self.controller(clean_size, "Packages Index", file_, "Index")
+               return clean_size
diff --git a/pym/gentoolkit/eclean/search.py b/pym/gentoolkit/eclean/search.py
new file mode 100644 (file)
index 0000000..4c5b0ac
--- /dev/null
@@ -0,0 +1,520 @@
+#!/usr/bin/python
+
+# Copyright 2003-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+from __future__ import print_function
+
+
+import re
+import stat
+import sys
+
+import portage
+from portage import os
+
+import gentoolkit
+import gentoolkit.pprinter as pp
+from gentoolkit.eclean.exclude import (exclDictMatchCP, exclDictExpand,
+       exclDictExpandPkgname, exclMatchFilename)
+#from gentoolkit.package import Package
+from gentoolkit.helpers import walk
+
+
+# Misc. shortcuts to some portage stuff:
+port_settings = portage.settings
+pkgdir = port_settings["PKGDIR"]
+
+err = sys.stderr
+deprecated_message=""""Deprecation Warning: Installed package: %s
+        Is no longer in the tree or an installed overlay"""
+DEPRECATED = pp.warn(deprecated_message)
+
+debug_modules = []
+
+
+def dprint(module, message):
+       if module in debug_modules:
+               print(message)
+
+
+def get_distdir():
+       """Returns DISTDIR if sane, else barfs."""
+
+       d = portage.settings["DISTDIR"]
+       if not os.path.isdir(d):
+               e = pp.error("%s does not appear to be a directory.\n" % d)
+               e += pp.error("Please set DISTDIR to a sane value.\n")
+               e += pp.error("(Check your /etc/make.conf and environment).")
+               print( e, file=sys.stderr)
+               exit(1)
+       return d
+
+distdir = get_distdir()
+
+
+class DistfilesSearch(object):
+       """
+
+               @param output: verbose output method or (lambda x: None) to turn off
+               @param vardb: defaults to portage.db[portage.root]["vartree"].dbapi
+                                       is overridden for testing.
+               @param portdb: defaults to portage.portdb and is overriden for testing.
+"""
+
+       def __init__(self,
+                       output,
+                       portdb=portage.portdb,
+                       vardb=portage.db[portage.root]["vartree"].dbapi,
+                       ):
+               self.vardb =vardb
+               self.portdb = portdb
+               self.output = output
+
+       def findDistfiles(self,
+                       exclude={},
+                       destructive=False,
+                       fetch_restricted=False,
+                       package_names=False,
+                       time_limit=0,
+                       size_limit=0,
+                       _distdir=distdir,
+                       deprecate=False
+                       ):
+               """Find all obsolete distfiles.
+
+               XXX: what about cvs ebuilds?
+               I should install some to see where it goes...
+
+               @param exclude: an exclusion dict as defined in
+                               exclude.parseExcludeFile class.
+               @param destructive: boolean, defaults to False
+               @param fetch_restricted: boolean, defaults to False
+               @param package_names: boolean, defaults to False.
+               @param time_limit: integer time value as returned by parseTime()
+               @param size_limit: integer value of max. file size to keep or 0 to ignore.
+               @param _distdir: path to the distfiles dir being checked, defaults to portage.
+               @param deprecate: bool to control checking the clean dict. files for exclusion
+
+               @rtype: dict
+               @return dict. of package files to clean i.e. {'cat/pkg-ver.tbz2': [filename],}
+               """
+               clean_me = {}
+               pkgs = {}
+               saved = {}
+               deprecated = {}
+               installed_included = False
+               # create a big CPV->SRC_URI dict of packages
+               # whose distfiles should be kept
+               if (not destructive) or fetch_restricted:
+                       self.output("...non-destructive type search")
+                       # TODO fix fetch_restricted to save the installed packges filenames while processing
+                       pkgs, _deprecated = self._non_destructive(destructive, fetch_restricted, exclude=exclude)
+                       deprecated.update(_deprecated)
+                       installed_included = True
+               if destructive:
+                       self.output("...destructive type search: %d packages already found" %len(pkgs))
+                       pkgs, _deprecated = self._destructive(package_names,
+                                       exclude, pkgs, installed_included)
+                       deprecated.update(_deprecated)
+               # gather the files to be cleaned
+               self.output("...checking limits for %d ebuild sources"
+                               %len(pkgs))
+               clean_me = self._check_limits(_distdir,
+                               size_limit, time_limit, exclude)
+               # remove any protected files from the list
+               self.output("...removing protected sources from %s candidates to clean"
+                               %len(clean_me))
+               clean_me = self._remove_protected(pkgs, clean_me)
+               if not deprecate and len(exclude) and len(clean_me):
+                       self.output("...checking final for exclusion from " +\
+                               "%s remaining candidates to clean" %len(clean_me))
+                       clean_me, saved = self._check_excludes(exclude, clean_me)
+               return clean_me, saved, deprecated
+
+
+####################### begin _check_limits code block
+
+       def _check_limits(self,
+                       _distdir,
+                       size_limit,
+                       time_limit,
+                       exclude,
+                       clean_me={}
+                       ):
+               """Checks files if they exceed size and/or time_limits, etc.
+               """
+               checks = [self._isreg_limit_]
+               if size_limit:
+                       checks.append(self._size_limit_)
+                       self.size_limit = size_limit
+               else:
+                       self.output("   - skipping size limit check")
+               if time_limit:
+                       checks.append(self._time_limit_)
+                       self.time_limit = time_limit
+               else:
+                       self.output("   - skipping time limit check")
+               if 'filenames' in exclude:
+                       checks.append(self._filenames_limit_)
+                       self.exclude = exclude
+               else:
+                       self.output("   - skipping exclude filenames check")
+               max_index = len(checks)
+               for file in os.listdir(_distdir):
+                       filepath = os.path.join(_distdir, file)
+                       try:
+                               file_stat = os.stat(filepath)
+                       except:
+                               continue
+                       _index = 0
+                       next = True
+                       skip_file = False
+                       while _index<max_index and next:
+                               next, skip_file = checks[_index](file_stat, file)
+                               _index +=1
+                       if skip_file:
+                               continue
+                       # this is a candidate for cleaning
+                       #print( "Adding file to clean_list:", file)
+                       clean_me[file]=[filepath]
+               return clean_me
+
+       def _isreg_limit_(self, file_stat, file):
+               """check if file is a regular file."""
+               is_reg_file = stat.S_ISREG(file_stat[stat.ST_MODE])
+               return  is_reg_file, not is_reg_file
+
+       def _size_limit_(self, file_stat, file):
+               """checks if the file size exceeds the size_limit"""
+               if (file_stat[stat.ST_SIZE] >= self.size_limit):
+                       #print( "size match ", file, file_stat[stat.ST_SIZE])
+                       return False, True
+               return True, False
+
+       def _time_limit_(self, file_stat, file):
+               """checks if the file exceeds the time_limit"""
+               if (file_stat[stat.ST_MTIME] >= self.time_limit):
+                       #print( "time match ", file, file_stat[stat.ST_MTIME])
+                       return False, True
+               return True,False
+
+       def _filenames_limit_(self, file_stat, file):
+               """checks if the file matches an exclusion file listing"""
+               # Try to match file name directly
+               if file in self.exclude['filenames']:
+                       return False, True
+               # See if file matches via regular expression matching
+               else:
+                       file_match = False
+                       for file_entry in self.exclude['filenames']:
+                               if self.exclude['filenames'][file_entry].match(file):
+                                       file_match = True
+                                       break
+               if file_match:
+                       return False, True
+               return True, False
+
+####################### end _check_limits code block
+
+       def _remove_protected(self,
+                       pkgs,
+                       clean_me
+                       ):
+               """Remove files owned by some protected packages.
+
+               @returns packages to clean
+               @rtype: dictionary
+               """
+               # this regexp extracts files names from SRC_URI. It is not very precise,
+               # but we don't care (may return empty strings, etc.), since it is fast.
+               file_regexp = re.compile(r'([a-zA-Z0-9_,\.\-\+\~]*)[\s\)]')
+               for cpv in pkgs:
+                       for file in file_regexp.findall(pkgs[cpv]+"\n"):
+                               if file in clean_me:
+                                       del clean_me[file]
+                       # no need to waste IO time if there is nothing left to clean
+                       if not len(clean_me):
+                               return clean_me
+               return clean_me
+
+       def _non_destructive(self,
+                       destructive,
+                       fetch_restricted,
+                       pkgs_ = {},
+                       exclude={}
+                       ):
+               """performs the non-destructive checks
+
+               @param destructive: boolean
+               @param pkgs_: starting dictionary to add to
+                               defaults to {}.
+
+               @returns packages and thier SRC_URI's: {cpv: src_uri,}
+               @rtype: dictionary
+               """
+               pkgs = pkgs_.copy()
+               deprecated = {}
+               # the following code block was split to optimize for speed
+               # list all CPV from portree (yeah, that takes time...)
+               self.output("   - getting complete ebuild list")
+               cpvs = set(self.portdb.cpv_all())
+               # now add any installed cpv's that are not in the tree or overlays
+               installed_cpvs = self.vardb.cpv_all()
+               cpvs.update(installed_cpvs)
+               if fetch_restricted and destructive:
+                       self.output("   - getting source file names " +
+                               "for %d installed ebuilds" %len(installed_cpvs))
+                       pkgs, _deprecated = self._unrestricted(pkgs, installed_cpvs)
+                       deprecated.update(_deprecated)
+                       # remove the installed cpvs then check the remaining for fetch restiction
+                       cpvs.difference_update(installed_cpvs)
+                       self.output("   - getting fetch-restricted source file names " +
+                               "for %d remaining ebuilds" %len(cpvs))
+                       pkgs, _deprecated = self._fetch_restricted(destructive, pkgs, cpvs)
+                       deprecated.update(_deprecated)
+               else:
+                       self.output("   - getting source file names " +
+                               "for %d ebuilds" %len(cpvs))
+                       pkgs, _deprecated = self._unrestricted(pkgs, cpvs)
+                       deprecated.update(_deprecated)
+               return pkgs, deprecated
+
+       def _fetch_restricted(self, destructive, pkgs_, cpvs):
+               """perform fetch restricted non-destructive source
+               filename lookups
+
+               @param destructive: boolean
+               @param pkgs_: starting dictionary to add to
+               @param cpvs: set of (cat/pkg-ver, ...) identifiers
+
+               @return a new pkg dictionary
+               @rtype: dictionary
+               """
+               pkgs = pkgs_.copy()
+               deprecated = {}
+               for cpv in cpvs:
+                       # get SRC_URI and RESTRICT from aux_get
+                       try: # main portdb
+                               (src_uri,restrict) = \
+                                       self.portdb.aux_get(cpv,["SRC_URI","RESTRICT"])
+                               # keep fetch-restricted check
+                               # inside try so it is bypassed on KeyError
+                               if 'fetch' in restrict:
+                                       pkgs[cpv] = src_uri
+                       except KeyError:
+                               try: # installed vardb
+                                       (src_uri,restrict) = \
+                                               self.vardb.aux_get(cpv,["SRC_URI","RESTRICT"])
+                                       deprecated[cpv] = src_uri
+                                       self.output(DEPRECATED %cpv)
+                                       # keep fetch-restricted check
+                                       # inside try so it is bypassed on KeyError
+                                       if 'fetch' in restrict:
+                                               pkgs[cpv] = src_uri
+                               except KeyError:
+                                       self.output("   - Key Error looking up: " + cpv)
+               return pkgs, deprecated
+
+       def _unrestricted(self, pkgs_, cpvs):
+               """Perform unrestricted source filenames lookups
+
+               @param pkgs_: starting packages dictionary
+               @param cpvs: set of (cat/pkg-ver, ...) identifiers
+
+               @return a new pkg dictionary
+               @rtype: dictionary
+               """
+               pkgs = pkgs_.copy()
+               deprecated = {}
+               for cpv in cpvs:
+                       # get SRC_URI from aux_get
+                       try:
+                               pkgs[cpv] = self.portdb.aux_get(cpv,["SRC_URI"])[0]
+                       except KeyError:
+                               try: # installed vardb
+                                       pkgs[cpv] = self.vardb.aux_get(cpv,["SRC_URI"])[0]
+                                       deprecated[cpv] = pkgs[cpv]
+                                       self.output(DEPRECATED %cpv)
+                               except KeyError:
+                                       self.output("   - Key Error looking up: " + cpv)
+               return pkgs, deprecated
+
+       def _destructive(self,
+                       package_names,
+                       exclude,
+                       pkgs_={},
+                       installed_included=False
+                       ):
+               """Builds on pkgs according to input options
+
+               @param package_names: boolean
+               @param exclude: an exclusion dict as defined in
+                               exclude.parseExcludeFile class.
+               @param pkgs: starting dictionary to add to
+                               defaults to {}.
+               @param installed_included: bool. pkgs already
+                               has the installed cpv's added.
+
+               @returns pkgs: {cpv: src_uri,}
+               """
+               pkgs = pkgs_.copy()
+               deprecated = {}
+               pkgset = set()
+               if not installed_included:
+                       if not package_names:
+                               # list all installed CPV's from vartree
+                               #print( "_destructive: getting vardb.cpv_all")
+                               pkgset.update(self.vardb.cpv_all())
+                               self.output("   - processing %s installed ebuilds" % len(pkgset))
+                       elif package_names:
+                               # list all CPV's from portree for CP's in vartree
+                               #print( "_destructive: getting vardb.cp_all")
+                               cps = self.vardb.cp_all()
+                               self.output("   - processing %s installed packages" % len(cps))
+                               for package in cps:
+                                       pkgset.update(self.portdb.cp_list(package))
+               self.output("   - processing excluded")
+               excludes = self._get_excludes(exclude)
+               excludes_length = len(excludes)
+               pkgset.update(excludes)
+               pkgs_done = set(list(pkgs))
+               pkgset.difference_update(pkgs_done)
+               self.output(
+                       "   - (%d of %d total) additional excluded packages to get source filenames for"
+                       %(len(pkgset), excludes_length))
+               #self.output("   - processing %d ebuilds for filenames" %len(pkgset))
+               pkgs, _deprecated = self._unrestricted(pkgs, pkgset)
+               deprecated.update(_deprecated)
+               #self.output("   - done...")
+               return pkgs, deprecated
+
+       def _get_excludes(self, exclude):
+               """Expands the exclude dictionary into a set of
+               CPV's
+
+               @param exclude: dictionary of exclusion categories,
+                       packages to exclude from the cleaning
+
+               @rtype: set
+               @return set of package cpv's
+               """
+               pkgset = set()
+               for cp in exclDictExpand(exclude):
+                       # add packages from the exclude file
+                       pkgset.update(self.portdb.cp_list(cp))
+               return pkgset
+
+       def _check_excludes(self, exclude, clean_me):
+               """Performs a last minute check on remaining filenames
+               to see if they should be protected.  Since if the pkg-version
+               was deprecated it would not have been matched to a
+               source filename and removed.
+
+               @param exclude: an exclusion dictionary
+               @param clean_me: the list of filenames for cleaning
+
+               @rtype: dict of packages to clean
+               """
+               saved = {}
+               pn_excludes = exclDictExpandPkgname(exclude)
+               dprint("excludes", "_check_excludes: made it here ;)")
+               if not pn_excludes:
+                       return clean_me, saved
+               dprint("excludes", pn_excludes)
+               for key in list(clean_me):
+                       if exclMatchFilename(pn_excludes, key):
+                               saved[key] = clean_me[key]
+                               del clean_me[key]
+                               self.output("   ...Saved excluded package filename: " + key)
+               return clean_me, saved
+
+
+def findPackages(
+               options,
+               exclude={},
+               destructive=False,
+               time_limit=0,
+               package_names=False,
+               pkgdir=None,
+               port_dbapi=portage.db[portage.root]["porttree"].dbapi,
+               var_dbapi=portage.db[portage.root]["vartree"].dbapi
+       ):
+       """Find all obsolete binary packages.
+
+       XXX: packages are found only by symlinks.
+       Maybe i should also return .tbz2 files from All/ that have
+       no corresponding symlinks.
+
+       @param options: dict of options determined at runtime
+       @param exclude: an exclusion dict as defined in
+                       exclude.parseExcludeFile class.
+       @param destructive: boolean, defaults to False
+       @param time_limit: integer time value as returned by parseTime()
+       @param package_names: boolean, defaults to False.
+                       used only if destructive=True
+       @param pkgdir: path to the binary package dir being checked
+       @param port_dbapi: defaults to portage.db[portage.root]["porttree"].dbapi
+                                       can be overridden for tests.
+       @param var_dbapi: defaults to portage.db[portage.root]["vartree"].dbapi
+                                       can be overridden for tests.
+
+       @rtype: dict
+       @return clean_me i.e. {'cat/pkg-ver.tbz2': [filepath],}
+       """
+       clean_me = {}
+       # create a full package dictionary
+
+       # now do an access test, os.walk does not error for "no read permission"
+       try:
+               test = os.listdir(pkgdir)
+               del test
+       except EnvironmentError as er:
+               print( pp.error("Error accessing PKGDIR." ), file=sys.stderr)
+               print( pp.error("(Check your /etc/make.conf and environment)."), file=sys.stderr)
+               print( pp.error("Error: %s" %str(er)), file=sys.stderr)
+               exit(1)
+       for root, dirs, files in walk(pkgdir):
+               if root[-3:] == 'All':
+                       continue
+               for file in files:
+                       if not file[-5:] == ".tbz2":
+                               # ignore non-tbz2 files
+                               continue
+                       path = os.path.join(root, file)
+                       category = os.path.split(root)[-1]
+                       cpv = category+"/"+file[:-5]
+                       st = os.lstat(path)
+                       if time_limit and (st[stat.ST_MTIME] >= time_limit):
+                               # time-limit exclusion
+                               continue
+                       # dict is cpv->[files] (2 files in general, because of symlink)
+                       clean_me[cpv] = [path]
+                       #if os.path.islink(path):
+                       if stat.S_ISLNK(st[stat.ST_MODE]):
+                               clean_me[cpv].append(os.path.realpath(path))
+       # keep only obsolete ones
+       if destructive:
+               dbapi = var_dbapi
+               if package_names:
+                       cp_all = dict.fromkeys(dbapi.cp_all())
+               else:
+                       cp_all = {}
+       else:
+               dbapi = port_dbapi
+               cp_all = {}
+       for cpv in list(clean_me):
+               if exclDictMatchCP(exclude,portage.cpv_getkey(cpv)):
+                       # exclusion because of the exclude file
+                       del clean_me[cpv]
+                       continue
+               if dbapi.cpv_exists(cpv):
+                       # exclusion because pkg still exists (in porttree or vartree)
+                       del clean_me[cpv]
+                       continue
+               if portage.cpv_getkey(cpv) in cp_all:
+                       # exlusion because of --package-names
+                       del clean_me[cpv]
+
+       return clean_me
index 2bee8d9526e8dc49aaa02f24ef284ecf0055424b..d511082f6c5c968b3218e3fb7db5e5549d1b5011 100644 (file)
@@ -218,7 +218,10 @@ def initialize_configuration():
                os.getenv("NOCOLOR") in ("yes", "true")) or CONFIG['color'] == 0):
                pp.output.nocolor()
 
-       CONFIG['verbose'] = not CONFIG['piping']
+       if CONFIG['piping']:
+               CONFIG['verbose'] = False
+
+       CONFIG['debug'] = bool(os.getenv('DEBUG', False))
 
 
 def main_usage():
@@ -316,10 +319,11 @@ def main():
        # 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']:
+       # verbose is shorthand for the very common 'not quiet or piping'
+       if CONFIG['quiet'] or CONFIG['piping']:
                CONFIG['verbose'] = False
+       else:
+               CONFIG['verbose'] = True
 
        try:
                module_name, module_args = split_arguments(args)
index d8607048a12198ba57246bdf6dfd983ad99b8760..0a5f9d09a3715b14eaf30c86e2bf80fba19aae6a 100644 (file)
@@ -101,8 +101,7 @@ 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
+       for i, entry in enumerate(entries, start=1):
                if i < len_entries:
                        print(entry)
                else:
index 32c7dc3a56f22a367683767f899aa104130ad1a7..84b1a7ec55a83ce57fc90cfc572149a5c8a7a277 100644 (file)
@@ -185,24 +185,12 @@ def main(input_args):
                #
 
                for pkg in matches:
-                       if CONFIG['verbose']:
-                               pkgstr = PackageFormatter(
-                                       pkg,
-                                       do_format=True,
-                                       custom_format=QUERY_OPTS["package_format"]
-                               )
-                       else:
-                               pkgstr = PackageFormatter(
-                                       pkg,
-                                       do_format=False,
-                                       custom_format=QUERY_OPTS["package_format"]
-                               )
-
-                       if (QUERY_OPTS["in_installed"] and
-                               not QUERY_OPTS["in_porttree"] and
-                               not QUERY_OPTS["in_overlay"]):
-                               if not 'I' in pkgstr.location:
-                                       continue
+                       pkgstr = PackageFormatter(
+                               pkg,
+                               do_format=CONFIG['verbose'],
+                               custom_format=QUERY_OPTS["package_format"]
+                       )
+
                        if (QUERY_OPTS["in_porttree"] and
                                not QUERY_OPTS["in_overlay"]):
                                if not 'P' in pkgstr.location:
@@ -215,7 +203,7 @@ def main(input_args):
 
                        if QUERY_OPTS["include_mask_reason"]:
                                ms_int, ms_orig = pkgstr.format_mask_status()
-                               if not ms_int > 2:
+                               if ms_int < 3:
                                        # ms_int is a number representation of mask level.
                                        # Only 2 and above are "hard masked" and have reasons.
                                        continue
index e1ca79405bc6365e6dbac65d0a11cf7f8a49b386..210a2b31413a05a00bdf727637fba761e0afbbfe 100644 (file)
@@ -16,12 +16,15 @@ __docformat__ = 'epytext'
 
 import re
 import sys
+import codecs
 from getopt import gnu_getopt, GetoptError
+from functools import partial
 
 from portage import os
 
 import gentoolkit.pprinter as pp
 from gentoolkit import errors
+from gentoolkit import keyword
 from gentoolkit.equery import format_options, mod_usage, CONFIG
 from gentoolkit.helpers import print_sequence, print_file
 from gentoolkit.textwrap_ import TextWrapper
@@ -216,7 +219,7 @@ def format_keywords(keywords):
 
        result = []
 
-       for kw in sorted(keywords):
+       for kw in sorted(keywords, cmp=keyword.compare_strs):
                if kw.startswith('-'):
                        # arch masked
                        kw = pp.keyword(kw, stable=False, hard_masked=True)
@@ -248,9 +251,15 @@ def format_keywords_line(pkg, fmtd_keywords, slot, verstr_len):
 def call_format_functions(best_match, matches):
        """Call information gathering functions and display the results."""
 
+       if hasattr(sys.stdout, "buffer"):
+               utf8_stdout = codecs.getwriter("utf-8")(sys.stdout.buffer)
+       else:
+               utf8_stdout = codecs.getwriter("utf-8")(sys.stdout)
+       uprint = partial(print, file=utf8_stdout)
+
        if CONFIG['verbose']:
                repo = best_match.repo_name()
-               print(" * %s [%s]" % (pp.cpv(best_match.cp), pp.section(repo)))
+               uprint(" * %s [%s]" % (pp.cpv(best_match.cp), pp.section(repo)))
 
        got_opts = False
        if any(QUERY_OPTS.values()):
@@ -258,12 +267,17 @@ def call_format_functions(best_match, matches):
                got_opts = True
 
        if QUERY_OPTS["herd"] or not got_opts:
-               herds = format_herds(best_match.metadata.herds(include_email=True))
+               herds = best_match.metadata.herds(include_email=True)
+               if any(not h[0] for h in herds):
+                       print(pp.warn("The packages metadata.xml has an empty <herd> tag"),
+                               file = sys.stderr)
+                       herds = [x for x in herds if x[0]]
+               herds = format_herds(herds)
                if QUERY_OPTS["herd"]:
                        print_sequence(format_list(herds))
                else:
                        for herd in herds:
-                               print(format_line(herd, "Herd:        ", " " * 13))
+                               uprint(format_line(herd, "Herd:        ", " " * 13))
 
        if QUERY_OPTS["maintainer"] or not got_opts:
                maints = format_maintainers(best_match.metadata.maintainers())
@@ -271,10 +285,10 @@ def call_format_functions(best_match, matches):
                        print_sequence(format_list(maints))
                else:
                        if not maints:
-                               print(format_line([], "Maintainer:  ", " " * 13))
+                               uprint(format_line([], "Maintainer:  ", " " * 13))
                        else:
                                for maint in maints:
-                                       print(format_line(maint, "Maintainer:  ", " " * 13))
+                                       uprint(format_line(maint, "Maintainer:  ", " " * 13))
 
        if QUERY_OPTS["upstream"] or not got_opts:
                upstream = format_upstream(best_match.metadata.upstream())
@@ -282,11 +296,11 @@ def call_format_functions(best_match, matches):
                        upstream = format_list(upstream)
                else:
                        upstream = format_list(upstream, "Upstream:    ", " " * 13)
-               print_sequence(upstream)
+               print_sequence(upstream, file = utf8_stdout)
 
        if not got_opts:
                pkg_loc = best_match.package_path()
-               print(format_line(pkg_loc, "Location:    ", " " * 13))
+               uprint(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', ...], ...}
@@ -300,18 +314,18 @@ def call_format_functions(best_match, matches):
                                match, fmtd_keywords, slot, verstr_len
                        )
                        if QUERY_OPTS["keywords"]:
-                               print(keywords_line)
+                               uprint(keywords_line)
                        else:
                                indent = " " * (16 + verstr_len)
-                               print(format_line(keywords_line, "Keywords:    ", indent))
+                               uprint(format_line(keywords_line, "Keywords:    ", indent))
 
        if QUERY_OPTS["description"]:
                desc = best_match.metadata.descriptions()
-               print_sequence(format_list(desc))
+               print_sequence(format_list(desc), file = utf8_stdout)
 
        if QUERY_OPTS["useflags"]:
                useflags = format_useflags(best_match.metadata.use())
-               print_sequence(format_list(useflags))
+               print_sequence(format_list(useflags), file = utf8_stdout)
 
        if QUERY_OPTS["xml"]:
                print_file(os.path.join(best_match.package_path(), 'metadata.xml'))
@@ -375,7 +389,7 @@ def format_line(line, first="", subsequent="", force_quiet=False):
 
                result = "".join(line)
 
-       return result.encode("utf-8")
+       return result
 
 
 def format_list(lst, first="", subsequent="", force_quiet=False):
@@ -466,9 +480,15 @@ def main(input_args):
        for query in (Query(x) for x in queries):
                best_match = query.find_best()
                matches = query.find(include_masked=True)
-               if not matches:
+               if best_match is None or not matches:
                        raise errors.GentoolkitNoMatches(query)
 
+               if best_match.metadata is None:
+                       print(pp.warn("Package {0} is missing "
+                               "metadata.xml".format(best_match.cpv)),
+                               file = sys.stderr)
+                       continue
+
                if not first_run:
                        print()
 
index 19aab6e741baf806d5b4833372dea0a44ed8d699..181f219194b0428bad65c08a7d686458233d3e1a 100644 (file)
@@ -8,9 +8,6 @@
 
 from __future__ import print_function
 
-# Move to imports section when Python 2.6 is stable
-
-
 __docformat__ = 'epytext'
 
 # =======
@@ -35,7 +32,7 @@ from gentoolkit.query import Query
 # Globals
 # =======
 
-QUERY_OPTS = {"allVersions" : False}
+QUERY_OPTS = {"all_versions" : False}
 
 # =========
 # Functions
@@ -170,7 +167,10 @@ def get_global_useflags():
 def get_output_descriptions(pkg, global_usedesc):
        """Prepare descriptions and usage information for each USE flag."""
 
-       local_usedesc = pkg.metadata.use()
+       if pkg.metadata is None:
+               local_usedesc = []
+       else:
+               local_usedesc = pkg.metadata.use()
        iuse = pkg.environment("IUSE")
 
        if iuse:
@@ -229,7 +229,7 @@ def parse_module_options(module_opts):
                        print_help()
                        sys.exit(0)
                elif opt in ('-a', '--all'):
-                       QUERY_OPTS['allVersions'] = True
+                       QUERY_OPTS['all_versions'] = True
 
 
 def print_legend():
@@ -271,12 +271,12 @@ def main(input_args):
                if not first_run:
                        print()
 
-               if QUERY_OPTS["allVersions"]:
+               if QUERY_OPTS["all_versions"]:
                        matches = query.find(include_masked=True)
                else:
                        matches = [query.find_best()]
 
-               if not matches:
+               if not any(matches):
                        raise errors.GentoolkitNoMatches(query)
 
                matches.sort()
index c5a88dea2028de8f8b9dfe6a80b34359f28c9598..152f660e971a96e5df17c23b6146c4b035f88fd2 100644 (file)
@@ -25,13 +25,14 @@ __all__ = (
 
 class GentoolkitException(Exception):
        """Base class for gentoolkit exceptions."""
-       def __init__(self):
-               pass
+       def __init__(self, is_serious=True):
+               self.is_serious = is_serious
 
 
 class GentoolkitFatalError(GentoolkitException):
        """A fatal error occurred. Usually used to catch Portage exceptions."""
-       def __init__(self, err):
+       def __init__(self, err, is_serious=True):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.err = err
 
        def __str__(self):
@@ -40,7 +41,8 @@ class GentoolkitFatalError(GentoolkitException):
 
 class GentoolkitAmbiguousPackage(GentoolkitException):
        """Got an ambiguous package name."""
-       def __init__(self, choices):
+       def __init__(self, choices, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.choices = choices
 
        def __str__(self):
@@ -50,7 +52,8 @@ class GentoolkitAmbiguousPackage(GentoolkitException):
 
 class GentoolkitInvalidAtom(GentoolkitException):
        """Got a malformed package atom."""
-       def __init__(self, atom):
+       def __init__(self, atom, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.atom = atom
 
        def __str__(self):
@@ -59,7 +62,8 @@ class GentoolkitInvalidAtom(GentoolkitException):
 
 class GentoolkitSetNotFound(GentoolkitException):
        """Got unknown set."""
-       def __init__(self, setname):
+       def __init__(self, setname, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.setname = setname
 
        def __str__(self):
@@ -68,7 +72,8 @@ class GentoolkitSetNotFound(GentoolkitException):
 
 class GentoolkitInvalidCategory(GentoolkitException):
        """The category was not listed in portage.settings.categories."""
-       def __init__(self, category):
+       def __init__(self, category, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.category = category
 
        def __str__(self):
@@ -77,7 +82,8 @@ class GentoolkitInvalidCategory(GentoolkitException):
 
 class GentoolkitInvalidPackage(GentoolkitException):
        """Got an unknown or invalid package."""
-       def __init__(self, package):
+       def __init__(self, package, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.package = package
 
        def __str__(self):
@@ -86,7 +92,8 @@ class GentoolkitInvalidPackage(GentoolkitException):
 
 class GentoolkitInvalidCPV(GentoolkitException):
        """Got an invalid category/package-ver string."""
-       def __init__(self, cpv):
+       def __init__(self, cpv, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.cpv = cpv
 
        def __str__(self):
@@ -95,7 +102,8 @@ class GentoolkitInvalidCPV(GentoolkitException):
 
 class GentoolkitInvalidRegex(GentoolkitException):
        """The regex could not be compiled."""
-       def __init__(self, regex):
+       def __init__(self, regex, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.regex = regex
 
        def __str__(self):
@@ -104,7 +112,8 @@ class GentoolkitInvalidRegex(GentoolkitException):
 
 class GentoolkitInvalidVersion(GentoolkitException):
        """Got a malformed version."""
-       def __init__(self, version):
+       def __init__(self, version, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.version = version
 
        def __str__(self):
@@ -113,7 +122,8 @@ class GentoolkitInvalidVersion(GentoolkitException):
 
 class GentoolkitNoMatches(GentoolkitException):
        """No packages were found matching the search query."""
-       def __init__(self, query, in_installed=False):
+       def __init__(self, query, in_installed=False, is_serious=False):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.query = query
                self.in_installed = in_installed
 
@@ -124,7 +134,8 @@ class GentoolkitNoMatches(GentoolkitException):
 
 class GentoolkitUnknownKeyword(GentoolkitException):
        """No packages were found matching the search query."""
-       def __init__(self, query, keywords, use):
+       def __init__(self, query, keywords, use, is_serious=True):
+               GentoolkitException.__init__(self, is_serious=is_serious)
                self.query = query
                self.keywords = keywords
                self.use = use
index 0aad2a7a822b65bef44a05dddfab5e7f9541d35a..6888619391b46c6de782b2531a8323e2a33f2e87 100644 (file)
@@ -27,7 +27,9 @@ __docformat__ = 'epytext'
 # Imports
 # =======
 
+import sys
 import re
+import codecs
 from functools import partial
 from itertools import chain
 
@@ -195,7 +197,8 @@ class ChangeLog(object):
 
                result = []
                partial_entries = []
-               with open(self.changelog_path) as log:
+               with codecs.open(self.changelog_path, encoding="utf-8",
+                       errors="replace") as log:
                        for line in log:
                                if line.startswith('#'):
                                        continue
@@ -434,16 +437,20 @@ def get_installed_cpvs(predicate=None):
 def print_file(path):
        """Display the contents of a file."""
 
-       with open(path) as open_file:
+       out = sys.stdout
+       if hasattr(out, "buffer"):
+               out = out.buffer
+
+       with open(path, "rb") as open_file:
                lines = open_file.read()
-               print(lines.strip())
+               print(lines.strip(), file = out)
 
 
-def print_sequence(seq):
+def print_sequence(seq, file = sys.stdout):
        """Print every item of a sequence."""
 
        for item in seq:
-               print(item)
+               print(item, file = file)
 
 
 def uniqify(seq, preserve_order=True):
index d40ab429574ed9b134b60eb86c4d447cba363d2f..d555e7cf4590a5c3794a78f170cd6509331546a5 100644 (file)
@@ -51,7 +51,13 @@ def compare_strs(kw1, kw2):
        @see: >>> help(cmp)
        """
 
-       pass
+       kw1_arch, sep, kw1_os = kw1.partition('-')
+       kw2_arch, sep, kw2_os = kw2.partition('-')
+       if kw1_arch != kw2_arch:
+               if kw1_os != kw2_os:
+                       return cmp(kw1_os, kw2_os)
+               return cmp(kw1_arch, kw2_arch)
+       return cmp(kw1_os, kw2_os)
 
 
 def reduce_keywords(keywords):
index 7765bcb1cd151575beb3b60eec82be5b9b9a189b..730d9936eb5db56a6228d855da3a84edfd8ee3b6 100644 (file)
@@ -232,11 +232,14 @@ class MetaData(object):
 
                result = []
                for elem in self._xml_tree.findall('herd'):
+                       text = elem.text
+                       if text is None:
+                               text = ''
                        if include_email:
-                               herd_mail = self._get_herd_email(elem.text)
-                               result.append((elem.text, herd_mail))
+                               herd_mail = self._get_herd_email(text)
+                               result.append((text, herd_mail))
                        else:
-                               result.append(elem.text)
+                               result.append(text)
 
                return result
 
index 89d06b19a1f33740a3bd271b6bda6aabe0c427bb..9c11275042a61526c8e0ab6c01dadc884c3f8ac9 100644 (file)
@@ -62,14 +62,15 @@ from gentoolkit.keyword import determine_keyword
 class Package(CPV):
        """Exposes the state of a given CPV."""
 
-       def __init__(self, cpv):
+       def __init__(self, cpv, validate=False):
                if isinstance(cpv, CPV):
                        self.__dict__.update(cpv.__dict__)
                else:
-                       CPV.__init__(self, cpv)
-               del cpv
+                       CPV.__init__(self, cpv, validate=validate)
 
-               if not all(hasattr(self, x) for x in ('category', 'version')):
+               if validate and not all(
+                       hasattr(self, x) for x in ('category', 'version')
+               ):
                        # CPV allows some things that Package must not
                        raise errors.GentoolkitInvalidPackage(self.cpv)
 
@@ -102,7 +103,13 @@ class Package(CPV):
                        metadata_path = os.path.join(
                                self.package_path(), 'metadata.xml'
                        )
-                       self._metadata = MetaData(metadata_path)
+                       try:
+                               self._metadata = MetaData(metadata_path)
+                       except IOError as e:
+                               import errno
+                               if e.errno != errno.ENOENT:
+                                       raise
+                               return None
 
                return self._metadata
 
@@ -397,11 +404,11 @@ class PackageFormatter(object):
        """
 
        _tmpl_verbose = "[$location] [$mask] $cpv:$slot"
-       _tmpl_quiet = "$cpv:$slot"
+       _tmpl_quiet = "$cpv"
 
-       def __init__(self, pkg, do_format=True, custom_format=None, fill_sizes = None):
+       def __init__(self, pkg, do_format=True, custom_format=None):
                self._pkg = None
-               self.do_format = do_format
+               self._do_format = do_format
                self._str = None
                self._location = None
                if not custom_format:
@@ -412,15 +419,6 @@ class PackageFormatter(object):
                self.tmpl = Template(custom_format)
                self.format_vars = LazyItemsDict()
                self.pkg = pkg
-               if fill_sizes:
-                       self.fill_sizes = fill_sizes
-               else:
-                       self.fill_sizes = {
-                               'cpv': 50,
-                               'keyword': 10,
-                               'mask': 10,
-                               }
-
 
        def __repr__(self):
                return "<%s %s @%#8x>" % (self.__class__.__name__, self.pkg, id(self))
@@ -455,7 +453,6 @@ class PackageFormatter(object):
                fmt_vars.addLazySingleton("mask", self.format_mask)
                fmt_vars.addLazySingleton("mask2", self.format_mask_status2)
                fmt_vars.addLazySingleton("cpv", self.format_cpv)
-               fmt_vars.addLazySingleton("cpv_fill", self.format_cpv, fill=True)
                fmt_vars.addLazySingleton("cp", self.format_cpv, "cp")
                fmt_vars.addLazySingleton("category", self.format_cpv, "category")
                fmt_vars.addLazySingleton("name", self.format_cpv, "name")
@@ -548,23 +545,19 @@ class PackageFormatter(object):
                        hard_masked=set(('M', '?', '-')).intersection(maskmode)
                )
 
-       def format_cpv(self, attr = None, fill=False):
+       def format_cpv(self, attr=None):
                if attr is None:
                        value = self.pkg.cpv
                else:
                        value = getattr(self.pkg, attr)
-               if self.do_format:
-                       if fill:
-                               trail = '.'*(self.fill_sizes['cpv']-len(value))
-                               return pp.cpv(value) + trail
-                       else:
-                               return pp.cpv(value)
+               if self._do_format:
+                       return pp.cpv(value)
                else:
                        return value
 
        def format_slot(self):
                value = self.pkg.environment("SLOT")
-               if self.do_format:
+               if self._do_format:
                        return pp.slot(value)
                else:
                        return value
index 110337ee85775aa2181d7388b9c84b01360c3f0f..764f82b822010a25ff3d43ba97492522fe6c82af 100644 (file)
@@ -19,6 +19,7 @@ __all__ = (
 import fnmatch
 import re
 from functools import partial
+from string import ascii_letters
 
 import portage
 
@@ -30,7 +31,6 @@ from gentoolkit.cpv import CPV
 from gentoolkit.dbapi import PORTDB, VARDB
 from gentoolkit.package import Package
 from gentoolkit.sets import get_set_atoms, SETPREFIX
-#from gentoolkit.helpers import *
 
 # =======
 # Classes
@@ -46,6 +46,11 @@ class Query(object):
                @param is_regex: query is a regular expression
                """
 
+               # We need at least one of these chars for a valid query
+               needed_chars = ascii_letters + '*'
+               if not set(query).intersection(needed_chars):
+                       raise errors.GentoolkitInvalidPackage(query)
+
                # Separate repository
                repository = None
                if query.count(':') == 2:
@@ -95,6 +100,7 @@ class Query(object):
                in_overlay=True,
                include_masked=True,
                show_progress=True,
+               no_matches_fatal=True,
                **kwargs
        ):
                """A high-level wrapper around gentoolkit package-finder functions.
@@ -107,6 +113,8 @@ class Query(object):
                @param in_overlay: search for query in overlays
                @type show_progress: bool
                @param show_progress: output search progress
+               @type no_matches_fatal: bool
+               @param no_matches_fatal: raise errors.GentoolkitNoMatches
                @rtype: list
                @return: Package objects matching query
                """
@@ -123,8 +131,9 @@ class Query(object):
                                complex_package_finder = helpers.get_installed_cpvs
                elif in_porttree or in_overlay:
                        simple_package_finder = partial(
-                               helpers.find_packages,
-                               include_masked=include_masked
+                               self.find,
+                               include_masked=include_masked,
+                               in_installed=False
                        )
                        complex_package_finder = helpers.get_uninstalled_cpvs
                else:
@@ -149,6 +158,9 @@ class Query(object):
                if self.repo_filter is not None:
                        matches = self._filter_by_repository(matches)
 
+               if no_matches_fatal and not matches:
+                       ii = in_installed and not (in_porttree or in_overlay)
+                       raise errors.GentoolkitNoMatches(self.query, in_installed=ii)
                return matches
 
        def find(self, in_installed=True, include_masked=True):
@@ -208,7 +220,7 @@ class Query(object):
                        raise errors.GentoolkitInvalidAtom(err)
                # xmatch can return an empty string, so checking for None is not enough
                if not best:
-                       if not include_keyworded or include_masked:
+                       if not (include_keyworded or include_masked):
                                return None
                        try:
                                matches = PORTDB.xmatch("match-all", self.query)
@@ -351,4 +363,3 @@ class Query(object):
                elif self.is_regex or self.uses_globbing():
                        return "complex"
                return "simple"
-
diff --git a/pym/gentoolkit/test/eclean/Packages b/pym/gentoolkit/test/eclean/Packages
new file mode 100644 (file)
index 0000000..1275ccb
--- /dev/null
@@ -0,0 +1,1017 @@
+ACCEPT_KEYWORDS: amd64 ~amd64
+ACCEPT_LICENSE: *
+ACCEPT_PROPERTIES: *
+CBUILD: x86_64-pc-linux-gnu
+CHOST: x86_64-pc-linux-gnu
+CONFIG_PROTECT: /etc /usr/share/X11/xkb /var/lib/hsqldb
+CONFIG_PROTECT_MASK: /etc/ca-certificates.conf /etc/env.d /etc/env.d/java/ /etc/fonts/fonts.conf /etc/gconf /etc/gentoo-release /etc/revdep-rebuild /etc/sandbox.d /etc/terminfo
+FEATURES: assume-digests buildpkg ccache distlocks fixpackages news parallel-fetch parralell-fetch preserve-libs protect-owned sandbox sfperms strict unmerge-logs unmerge-orphans userfetch
+GENTOO_MIRRORS: http://gentoo.osuosl.org/ 
+PACKAGES: 73
+PROFILE: default/linux/amd64/10.0/desktop
+SYNC: rsync://rsync.namerica.gentoo.org/gentoo-portage
+TIMESTAMP: 1264301192
+USE: X a52 aac aalib acl acpi adns alsa alsa_cards_ali5451 alsa_cards_als4000 alsa_cards_atiixp alsa_cards_atiixp-modem alsa_cards_bt87x alsa_cards_ca0106 alsa_cards_cmipci alsa_cards_emu10k1x alsa_cards_ens1370 alsa_cards_ens1371 alsa_cards_es1938 alsa_cards_es1968 alsa_cards_fm801 alsa_cards_hda-intel alsa_cards_intel8x0 alsa_cards_intel8x0m alsa_cards_maestro3 alsa_cards_trident alsa_cards_usb-audio alsa_cards_via82xx alsa_cards_via82xx-modem alsa_cards_ymfpci alsa_pcm_plugins_adpcm alsa_pcm_plugins_alaw alsa_pcm_plugins_asym alsa_pcm_plugins_copy alsa_pcm_plugins_dmix alsa_pcm_plugins_dshare alsa_pcm_plugins_dsnoop alsa_pcm_plugins_empty alsa_pcm_plugins_extplug alsa_pcm_plugins_file alsa_pcm_plugins_hooks alsa_pcm_plugins_iec958 alsa_pcm_plugins_ioplug alsa_pcm_plugins_ladspa alsa_pcm_plugins_lfloat alsa_pcm_plugins_linear alsa_pcm_plugins_meter alsa_pcm_plugins_mmap_emul alsa_pcm_plugins_mulaw alsa_pcm_plugins_multi alsa_pcm_plugins_null alsa_pcm_plugins_plug alsa_pcm_plugins_rate alsa_pcm_plugins_route alsa_pcm_plugins_share alsa_pcm_plugins_shm alsa_pcm_plugins_softvol amd64 apache2_modules_actions apache2_modules_alias apache2_modules_auth_basic apache2_modules_authn_alias apache2_modules_authn_anon apache2_modules_authn_dbm apache2_modules_authn_default apache2_modules_authn_file apache2_modules_authz_dbm apache2_modules_authz_default apache2_modules_authz_groupfile apache2_modules_authz_host apache2_modules_authz_owner apache2_modules_authz_user apache2_modules_autoindex apache2_modules_cache apache2_modules_dav apache2_modules_dav_fs apache2_modules_dav_lock apache2_modules_deflate apache2_modules_dir apache2_modules_disk_cache apache2_modules_env apache2_modules_expires apache2_modules_ext_filter apache2_modules_file_cache apache2_modules_filter apache2_modules_headers apache2_modules_include apache2_modules_info apache2_modules_log_config apache2_modules_logio apache2_modules_mem_cache apache2_modules_mime apache2_modules_mime_magic apache2_modules_negotiation apache2_modules_rewrite apache2_modules_setenvif apache2_modules_speling apache2_modules_status apache2_modules_unique_id apache2_modules_userdir apache2_modules_usertrack apache2_modules_vhost_alias avahi berkdb bidi bluetooth bonobo branding bzip2 cairo cddb cdr cleartype cli consolekit cracklib crypt css cups curl cxx dbus dlloader dri dts dv dvd dvdr dvdread eds elibc_glibc emboss encode evo fam fame fbcon ffmpeg flac fortran ftp gconf gdbm gecko gif gimpprint gnome gnutls gpm gs gstreamer gtk gtk2 gtkhtml h323 hal howl iconv imagemagic imap imlib2 innodb input_devices_evdev ipv6 java javascript jikes joystick jpeg kernel_linux lcd_devices_bayrad lcd_devices_cfontz lcd_devices_cfontz633 lcd_devices_glk lcd_devices_hd44780 lcd_devices_lb216 lcd_devices_lcdm001 lcd_devices_mtxorb lcd_devices_ncurses lcd_devices_text lcms ldap libnotify lm_sensors mad md5sum messages mikmod mime mmx mng modules moxnomail mozdevelop mp3 mp4 mpeg mpi mplayer mudflap multilib mysql nautilus ncurses nfs nls no-old-linux nptl nptlonly nptonly numeric nvidia ogg opengl openmp pam pcre pda pdf perl plugin png policykit posix ppds pppd python qt3support quicktime readline reflection rtc ruby_targets_ruby18 samba sdl session shm sndfile sox speex spell spl sqlite sqlite3 sse sse2 sse3 ssl startup-notification svg swat sysfs syslog tcpd threadsonly thumbnail thunar thunar-vfs tiff tk transcode truetype unicode usb userland_GNU utf8 v4l v4l2 vcd vdpau video_cards_nvidia vorbis x264 xine xml xorg xosd xpm xulrunner xv xvid xvmc zlib zvbi
+VERSION: 0
+
+CPV: app-arch/bzip2-1.0.5-r1
+DESC: A high-quality data compressor used extensively by Gentoo Linux
+IUSE: static
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc ~sparc-fbsd x86 ~x86-fbsd
+LICENSE: BZIP2
+MD5: ab8f256048b0167c0628f850f7187981
+MTIME: 1264262577
+SHA1: 576df7f5f21f87a3b41b99e282e5eec09a5f4325
+SIZE: 584218
+REPO: gentoo
+
+CPV: app-arch/cpio-2.10
+DESC: A file archival tool which can also read and write tar files
+IUSE: nls
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: fd8328657cab407fd99306713187c7aa
+MTIME: 1264264179
+SHA1: f35d72268d1c7333c885454f774ce3acd14cc493
+SIZE: 196505
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/cpio-2.10-r1
+DESC: A file archival tool which can also read and write tar files
+EAPI: 2
+IUSE: nls
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 5e69ec6d2493761c32744eca27e19009
+MTIME: 1264301003
+SHA1: 99715c7646655dc6627ec4433cefaabc9fba0076
+SIZE: 208357
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/cpio-2.9-r2
+DESC: A file archival tool which can also read and write tar files
+IUSE: nls
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc ~sparc-fbsd x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 55e4f7bc94b03cafce4b1afb6295c1ab
+MTIME: 1264264294
+SHA1: 3becefc751c6316966d2b7a6404c0785434e5368
+SIZE: 174202
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/cpio-2.9-r3
+DESC: A file archival tool which can also read and write tar files
+IUSE: nls
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: a947348c332c75683d66663928bbe45b
+MTIME: 1264264353
+SHA1: 6a48fb69f046e16b3c59f3b21129909d31026818
+SIZE: 174361
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/file-roller-2.24.3
+DEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.13 >=gnome-base/libgnome-2.6 >=gnome-base/libgnomeui-2.6 >=gnome-base/libglade-2.4 >=gnome-base/gconf-2.6 >=gnome-base/nautilus-2.22.2 gnome-base/gnome-common sys-devel/gettext >=dev-util/intltool-0.35 >=dev-util/pkgconfig-0.19 >=app-text/gnome-doc-utils-0.3.2 >=sys-apps/sed-4
+DESC: archive manager for GNOME
+IUSE: nautilus
+KEYWORDS: alpha amd64 hppa ia64 ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 730b91a73bce36f7145de7dd1dc6c701
+MTIME: 1264264717
+RDEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.13 >=gnome-base/libgnome-2.6 >=gnome-base/libgnomeui-2.6 >=gnome-base/libglade-2.4 >=gnome-base/gconf-2.6 >=gnome-base/nautilus-2.22.2
+SHA1: df43031484955ee09402b53651d89cf3765315c3
+SIZE: 1151945
+USE: nautilus
+REPO: gentoo
+
+CPV: app-arch/file-roller-2.26.3
+DEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.13 gnome-base/gconf gnome-base/nautilus gnome-base/gnome-common sys-devel/gettext dev-util/intltool dev-util/pkgconfig app-text/gnome-doc-utils >=sys-apps/sed-4
+DESC: archive manager for GNOME
+EAPI: 2
+IUSE: nautilus
+KEYWORDS: alpha amd64 ~hppa ia64 ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 154f28c22b3f1c5836ba06b8f671f718
+MTIME: 1264264651
+RDEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.13 gnome-base/gconf gnome-base/nautilus
+SHA1: 035a1130cbd602e5872abe45e96d5f901a3de1f3
+SIZE: 1101683
+USE: nautilus
+REPO: gentoo
+
+CPV: app-arch/file-roller-2.28.1
+DEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.16 gnome-base/gconf gnome-base/nautilus gnome-base/gnome-common sys-devel/gettext dev-util/intltool dev-util/pkgconfig app-text/gnome-doc-utils >=sys-apps/sed-4
+DESC: archive manager for GNOME
+EAPI: 2
+IUSE: nautilus
+KEYWORDS: ~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 7f1154b8d357400206873187603b6e10
+MTIME: 1264264576
+RDEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.16 gnome-base/gconf gnome-base/nautilus
+SHA1: 43142dd2566cd1018e0f416da474b7bb441a96cb
+SIZE: 1313333
+USE: nautilus
+REPO: gentoo
+
+CPV: app-arch/file-roller-2.28.2
+DEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.16 gnome-base/gconf gnome-base/nautilus gnome-base/gnome-common sys-devel/gettext dev-util/intltool dev-util/pkgconfig app-text/gnome-doc-utils >=sys-apps/sed-4
+DESC: archive manager for GNOME
+EAPI: 2
+IUSE: nautilus
+KEYWORDS: ~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd ~x86-freebsd ~amd64-linux ~x86-linux
+LICENSE: GPL-2
+MD5: ae10bc3d791c59af92f39d4a7fd87871
+MTIME: 1264301192
+RDEPEND: >=dev-libs/glib-2.16.0 >=x11-libs/gtk+-2.16 gnome-base/gconf gnome-base/nautilus
+SHA1: 3959f6022e38afd113beabff5102e500c353cf45
+SIZE: 1318363
+USE: nautilus
+REPO: gentoo
+
+CPV: app-arch/gzip-1.4
+DEPEND: sys-devel/gettext
+DESC: Standard GNU compressor
+IUSE: nls pic static
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-2
+MD5: 32ac853a6792046bda7f88767e5260db
+MTIME: 1264300929
+PROVIDE: virtual/gzip
+SHA1: eaf1de2ae3f1bc0e9c4e91c3da03e91de92e4e6b
+SIZE: 142419
+USE: nls
+REPO: gentoo
+
+CPV: app-arch/p7zip-4.57
+DESC: Port of 7-Zip archiver for Unix
+IUSE: static doc
+KEYWORDS: ~alpha amd64 hppa ~ia64 ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: LGPL-2.1
+MD5: 9ce142c7ffa21b85d5a877bb69e28677
+MTIME: 1264264235
+SHA1: 488fca992e2da7c51cb8f8325d996507f0f61b0a
+SIZE: 1533328
+REPO: gentoo
+
+CPV: app-arch/p7zip-4.58
+DESC: Port of 7-Zip archiver for Unix
+IUSE: static doc
+KEYWORDS: ~alpha amd64 hppa ~ia64 ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: LGPL-2.1
+MD5: 79249e03c6ff6c961627d65c68120e37
+MTIME: 1264264131
+SHA1: af33c7a2153e9c6acb914b8a0ed0e5c74bedfc9e
+SIZE: 1564378
+REPO: gentoo
+
+CPV: app-arch/p7zip-4.65
+DEPEND: x11-libs/wxGTK:2.8[X,-odbc]
+DESC: Port of 7-Zip archiver for Unix
+EAPI: 2
+IUSE: doc kde rar static wxwidgets
+KEYWORDS: ~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd ~x86-freebsd ~x86-interix ~amd64-linux ~x86-linux ~ppc-macos ~x86-macos
+LICENSE: LGPL-2.1
+MD5: 95d47a6dd8feff8c0b84f997927a25b0
+MTIME: 1264301127
+RDEPEND: x11-libs/wxGTK:2.8[X,-odbc]
+SHA1: 82dcf81c00866f120bc35a3b8fb2237c8067df57
+SIZE: 2278496
+USE: wxwidgets
+REPO: gentoo
+
+CPV: app-arch/rar-3.9.0
+DESC: RAR compressor/uncompressor
+KEYWORDS: -* ~amd64 ~x86
+LICENSE: RAR
+MD5: c1de3dade9c34089da431aa69497dcb1
+MTIME: 1264262625
+RDEPEND: >=sys-libs/glibc-2.4
+SHA1: 83ba223e351dfaeac9a107f062ec55298b27615f
+SIZE: 308490
+REPO: gentoo
+
+CPV: app-arch/tar-1.20
+DEPEND: >=sys-devel/gettext-0.10.35
+DESC: Use this to make tarballs :)
+IUSE: nls static userland_GNU
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 887e5c0a8bf38f63546c8f0c80b57e49
+MTIME: 1264264511
+SHA1: 8f8d4305745b14d5ed2cd42fabc512775db3d7c4
+SIZE: 785279
+USE: nls userland_GNU
+REPO: gentoo
+
+CPV: app-arch/tar-1.21-r1
+DEPEND: >=sys-devel/gettext-0.10.35
+DESC: Use this to make tarballs :)
+IUSE: nls static userland_GNU
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 9ebea4a9c24d8c26270d41c6adc6771e
+MTIME: 1264264431
+SHA1: be19702b47d2aa00d51c8ed6889bcc0593edc556
+SIZE: 810009
+USE: nls userland_GNU
+REPO: gentoo
+
+CPV: app-arch/tar-1.22
+DEPEND: >=sys-devel/gettext-0.10.35
+DESC: Use this to make tarballs :)
+IUSE: nls static userland_GNU
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 3c302c5355d5f12f3809ccad18db7e0d
+MTIME: 1264300860
+SHA1: 0fac4f3d391d30fbedb36447c286fa54fc249cda
+SIZE: 813235
+USE: nls userland_GNU
+REPO: gentoo
+
+CPV: app-editors/nano-2.0.9
+DEPEND: >=sys-libs/ncurses-5.2 sys-devel/gettext
+DESC: GNU GPL'd Pico clone with more functionality
+IUSE: debug justify minimal ncurses nls slang spell unicode
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc ~sparc-fbsd x86 ~x86-fbsd
+LICENSE: GPL-3
+MD5: 274550c29c9b263296dbfdd5f544b94b
+MTIME: 1264265176
+RDEPEND: >=sys-libs/ncurses-5.2 sys-devel/gettext
+SHA1: 0f7ad9888b1df320dde6499548b868b7a89c2361
+SIZE: 454616
+USE: ncurses nls spell unicode
+REPO: gentoo
+
+CPV: app-editors/nano-2.1.10
+DEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+DESC: GNU GPL'd Pico clone with more functionality
+EAPI: 2
+IUSE: debug justify minimal ncurses nls slang spell unicode
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 5e2fdcc73fd3731aaf1bbd79a2c69635
+MTIME: 1264265216
+RDEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+SHA1: 8b2a8e6bd12573fdaf89b03199caca9ea819e6e9
+SIZE: 448975
+USE: ncurses nls spell unicode
+REPO: gentoo
+
+CPV: app-editors/nano-2.2.0
+DEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+DESC: GNU GPL'd Pico clone with more functionality
+EAPI: 2
+IUSE: debug justify minimal ncurses nls slang spell unicode
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 6d27422ba5aa436bdf05ee0dcad06bf9
+MTIME: 1264265255
+RDEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+SHA1: 4c563f227bb0962f8c77e83f3472f3c466247451
+SIZE: 465543
+USE: ncurses nls spell unicode
+REPO: gentoo
+
+CPV: app-editors/nano-2.2.2
+DEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+DESC: GNU GPL'd Pico clone with more functionality
+EAPI: 2
+IUSE: debug justify minimal ncurses nls slang spell unicode
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: e2c33dd669b4380180cae217f772c472
+MTIME: 1264300332
+RDEPEND: >=sys-libs/ncurses-5.2[unicode?] sys-devel/gettext
+SHA1: dd374677cf847a53c36d4d34a9e4b7ca1b92e44c
+SIZE: 472482
+USE: ncurses nls spell unicode
+REPO: gentoo
+
+CPV: app-emulation/emul-linux-x86-compat-20091231-r1
+DEPEND: >=sys-apps/findutils-4.2.26
+DESC: 32 bit lib-compat, and also libgcc_s and libstdc++ from gcc 3.3 and 3.4 for non-multilib systems
+IUSE: multilib
+KEYWORDS: -* ~amd64 ~amd64-linux
+LICENSE: GPL-2
+MD5: b44ea7e3867b8bc5195044d6c5167edb
+MTIME: 1264300253
+RDEPEND: sys-libs/libstdc++-v3
+SHA1: 752218fa71c89932f50e9a487ce7034d2c89c019
+SIZE: 673489
+USE: multilib
+REPO: gentoo
+
+CPV: app-misc/gtypist-2.8.3
+DEPEND: >=sys-libs/ncurses-5.2
+DESC: Universal typing tutor
+IUSE: nls emacs xemacs
+KEYWORDS: ~amd64 ~ppc ~x86
+LICENSE: GPL-2
+MD5: 9cdee43a070f7f1a04048574f5d1df4d
+MTIME: 1264299719
+RDEPEND: >=sys-libs/ncurses-5.2
+SHA1: 0da2fef7e609a2de5c3ba62193421f0b55fbf1ef
+SIZE: 517754
+USE: nls
+REPO: gentoo
+
+CPV: app-portage/layman-1.2.3
+DEPEND: || ( dev-lang/python[xml] ( dev-lang/python dev-python/pyxml ) ) >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: alpha amd64 arm hppa ia64 ~mips ppc ppc64 sparc x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 525788d2c144d63f93b70c3cb396ffd1
+MTIME: 1264265280
+RDEPEND: || ( dev-lang/python[xml] ( dev-lang/python dev-python/pyxml ) ) >=dev-lang/python-2.5 virtual/python
+SHA1: e69c712bf3d2decc6f2d591e5065fa9c32d268c4
+SIZE: 63035
+REPO: gentoo
+
+CPV: app-portage/layman-1.2.4-r3
+DEPEND: || ( dev-lang/python[xml] ( dev-lang/python dev-python/pyxml ) ) >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: b16542f9539cb5c6648d165fa888ae25
+MTIME: 1264265308
+RDEPEND: || ( dev-lang/python[xml] ( dev-lang/python dev-python/pyxml ) ) >=dev-lang/python-2.5 virtual/python
+SHA1: 311bb22ceccd382fdf79d62708d586547e3d793c
+SIZE: 64942
+REPO: gentoo
+
+CPV: app-portage/layman-1.2.6
+DEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: b3f903c492df0c2f8c82225f784b0d64
+MTIME: 1264265336
+RDEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 virtual/python
+SHA1: c86bef390dbcb765f4d96d2affacedc072333074
+SIZE: 64924
+REPO: gentoo
+
+CPV: app-portage/layman-1.3.0
+DEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 9d79d89f556f51e6d6221d72a59f7927
+MTIME: 1264299669
+RDEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 virtual/python
+SHA1: 4034c89723eb6f20a52ca432507f449d6963d4c3
+SIZE: 66077
+REPO: gentoo
+
+CPV: app-portage/layman-1.3.0_rc1
+DEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 9cabc319559778c6a59d11f325dba3d9
+MTIME: 1264265170
+RDEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 virtual/python
+SHA1: 1e53103e6d204479add69fe73c2e35bb9aceb487
+SIZE: 68734
+REPO: gentoo
+
+CPV: app-portage/layman-1.3.0_rc1-r3
+DEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 >=app-admin/eselect-python-20090804 virtual/python
+DESC: A python script for retrieving gentoo overlays.
+EAPI: 2
+IUSE: git subversion test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: fc330556091381a323703a5c1392395c
+MTIME: 1264265364
+RDEPEND: dev-lang/python[xml] >=dev-lang/python-2.5 virtual/python
+SHA1: 07c7c177b1772663852b678d6afd2ba75a7c7200
+SIZE: 66374
+REPO: gentoo
+
+CPV: app-portage/portage-utils-0.3.1
+DESC: small and fast portage helper tools written in C
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-2
+MD5: 79c4da94810df0f3034fe30c26c05003
+MTIME: 1264299639
+SHA1: 06738d79cf54f92ee61e534fa730f3ca3ff8951f
+SIZE: 105322
+REPO: gentoo
+
+CPV: app-shells/bash-4.0_p37
+DEPEND: >=sys-libs/ncurses-5.2-r2 virtual/libintl
+DESC: The standard GNU Bourne again shell
+EAPI: 1
+IUSE: afs bashlogger examples mem-scramble +net nls plugins vanilla
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 1f6a93554c485d8025d98c9a4a8fa006
+MTIME: 1264300780
+RDEPEND: >=sys-libs/ncurses-5.2-r2 virtual/libintl !<sys-apps/portage-2.1.5 !<sys-apps/paludis-0.26.0_alpha5
+SHA1: b751ddb741a1581bf1e17867ca94df764fdf4ecc
+SIZE: 1013849
+USE: net nls
+REPO: gentoo
+
+CPV: dev-db/sqlite-3.6.22-r1
+DEPEND: dev-libs/icu sys-libs/readline dev-lang/tcl =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: A SQL Database Engine in a C Library
+EAPI: 2
+IUSE: debug doc extensions +fts3 icu +readline soundex tcl +threadsafe test
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~sparc-fbsd ~x86-fbsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: as-is
+MD5: 312fb5844ea57184e849aa51e180016e
+MTIME: 1264299603
+RDEPEND: dev-libs/icu sys-libs/readline dev-lang/tcl
+SHA1: 0abb9db0dc6a29207e2d3e0579a5715c77f1d3d2
+SIZE: 712591
+SLOT: 3
+USE: fts3 icu readline tcl threadsafe
+REPO: gentoo
+
+CPV: dev-java/icedtea6-bin-1.6.2-r2
+DEPEND: =dev-java/java-config-2* >=sys-apps/portage-2.1
+DESC: A Gentoo-made binary build of the icedtea6 JDK
+EAPI: 1
+IUSE: X alsa doc examples nsplugin source
+KEYWORDS: ~amd64 ~x86
+LICENSE: GPL-2-with-linking-exception
+MD5: 596d044b5894588a97eae356d2d05f02
+MTIME: 1264299533
+RDEPEND: >=sys-devel/gcc-4.3 >=sys-libs/glibc-2.9 >=media-libs/giflib-4.1.6-r1 =media-libs/jpeg-8* >=media-libs/libpng-1.2.38 >=sys-libs/zlib-1.2.3-r1 >=media-libs/alsa-lib-1.0.20 >=media-libs/freetype-2.3.9:2 >=media-libs/fontconfig-2.6.0-r2:1.0 >=x11-libs/libXext-1.0.5 >=x11-libs/libXi-1.2.1 >=x11-libs/libXtst-1.0.3 >=x11-libs/libX11-1.2.2 x11-libs/libXt =dev-java/java-config-2*
+SHA1: 75bd18425582a774d6f5544b73399fac3fe2b417
+SIZE: 35258682
+USE: X alsa
+REPO: gentoo
+
+CPV: dev-java/sun-jdk-1.6.0.18
+DEPEND: =dev-java/java-config-2* >=sys-apps/portage-2.1
+DESC: Sun's Java SE Development Kit
+IUSE: X alsa derby doc examples jce nsplugin odbc
+KEYWORDS: ~amd64 ~x86
+LICENSE: dlj-1.1
+MD5: f6898e4601bbefce5011e4506870250f
+MTIME: 1264299356
+RDEPEND: sys-libs/glibc media-libs/alsa-lib x11-libs/libXext x11-libs/libXi x11-libs/libXp x11-libs/libXtst x11-libs/libXt x11-libs/libX11 =dev-java/java-config-2*
+SHA1: 9263959ac184d2643518ed3ba36a40e4f7be92f9
+SIZE: 65027860
+SLOT: 1.6
+USE: X alsa nsplugin
+REPO: gentoo
+
+CPV: dev-libs/DirectFB-1.4.3
+DEPEND: media-libs/libsdl media-libs/giflib media-libs/libpng media-libs/jpeg sys-fs/sysfsutils sys-libs/zlib >=media-libs/freetype-2.0.1 x11-libs/libXext x11-libs/libX11 x11-proto/xextproto x11-proto/xproto
+DESC: Thin library on top of the Linux framebuffer devices
+IUSE: debug fbcon fusion gif jpeg mmx png sdl sse sysfs truetype v4l v4l2 X zlib video_cards_intel video_cards_mach64 video_cards_mga video_cards_neomagic video_cards_nsc video_cards_nvidia video_cards_r128 video_cards_radeon video_cards_s3 video_cards_savage video_cards_sis video_cards_tdfx video_cards_via video_cards_vmware input_devices_dynapro input_devices_elo2300 input_devices_evdev input_devices_joystick input_devices_keyboard input_devices_lirc input_devices_mouse input_devices_mutouch input_devices_tslib
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 -mips ~ppc ~ppc64 ~sh -sparc ~x86
+LICENSE: LGPL-2.1
+MD5: 0d62fd95d45383be9de1d3d63a8b687e
+MTIME: 1264299004
+RDEPEND: media-libs/libsdl media-libs/giflib media-libs/libpng media-libs/jpeg sys-fs/sysfsutils sys-libs/zlib >=media-libs/freetype-2.0.1 x11-libs/libXext x11-libs/libX11
+SHA1: f9ffc4909669626a3eb99c5f850a2f50790d4b69
+SIZE: 1594011
+USE: X fbcon gif input_devices_evdev jpeg mmx png sdl sse sysfs truetype v4l v4l2 video_cards_nvidia zlib
+REPO: gentoo
+
+CPV: dev-libs/dbus-glib-0.82-r1
+DEPEND: >=sys-apps/dbus-1.1 >=dev-libs/glib-2.10 >=dev-libs/expat-1.95.8 dev-util/pkgconfig sys-devel/gettext
+DESC: D-Bus bindings for glib
+EAPI: 2
+IUSE: bash-completion debug doc test bash-completion
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: || ( GPL-2 AFL-2.1 )
+MD5: 414b1ca40951ec482c454896953a6b49
+MTIME: 1264298849
+RDEPEND: >=sys-apps/dbus-1.1 >=dev-libs/glib-2.10 >=dev-libs/expat-1.95.8
+SHA1: 9ac0d74ce34ddb83f6a25c6b1b64449b0769ccdc
+SIZE: 241344
+REPO: gentoo
+
+CPV: dev-libs/fribidi-0.19.2
+DESC: A free implementation of the unicode bidirectional algorithm
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86 ~x86-fbsd ~amd64-linux ~x86-linux ~ppc-macos ~x64-solaris ~x86-solaris
+LICENSE: LGPL-2.1
+MD5: 897e5a3a7d4d003dab4f3e74b32ed8e4
+MTIME: 1264298806
+SHA1: fcfc11f2cc834580f6d95e64dcdb7f4755e2a9fb
+SIZE: 107278
+REPO: gentoo
+
+CPV: dev-libs/gmime-2.4.13
+DEPEND: >=dev-libs/glib-2.12 sys-libs/zlib dev-util/pkgconfig >=sys-apps/sed-4
+DESC: Utilities for creating and parsing messages using MIME
+IUSE: doc mono debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: LGPL-2.1
+MD5: 09a3282f2061022a0ab844c13a192cf7
+MTIME: 1264298765
+RDEPEND: >=dev-libs/glib-2.12 sys-libs/zlib
+SHA1: c79a707cad05d18b183829890ebe23ea21efd4d8
+SIZE: 431050
+SLOT: 2.4
+REPO: gentoo
+
+CPV: dev-libs/libisofs-0.6.26
+DEPEND: virtual/acl sys-libs/zlib dev-util/pkgconfig
+DESC: libisofs is an open-source library for reading, mastering and writing optical discs.
+EAPI: 2
+IUSE: acl xattr zlib
+KEYWORDS: ~alpha ~amd64 ~hppa ~ppc ~ppc64 ~x86
+LICENSE: GPL-2
+MD5: 37ad8de72ab38bbd89c7641a20ac2b11
+MTIME: 1264298707
+RDEPEND: virtual/acl sys-libs/zlib
+SHA1: eed462d17ae2a2006dbc80437414054d51c69473
+SIZE: 199818
+USE: acl zlib
+REPO: gentoo
+
+CPV: dev-libs/libtasn1-2.4
+DEPEND: >=dev-lang/perl-5.6 sys-devel/bison
+DESC: provides ASN.1 structures parsing capabilities for use with GNUTLS
+IUSE: doc
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3 LGPL-2.1
+MD5: 94c40f06b2028421eb318b320e7e1c79
+MTIME: 1264298652
+SHA1: 9f819c2613324c31d736affa6e4ee1dd5bc2a3aa
+SIZE: 149335
+REPO: gentoo
+
+CPV: dev-util/git-1.6.6.1
+DEPEND: sys-libs/zlib dev-lang/perl dev-lang/tk net-misc/curl dev-libs/expat app-arch/cpio dev-lang/perl[-build]
+DESC: GIT - the stupid content tracker, the revision control system heavily used by the Linux kernel team
+EAPI: 2
+IUSE: +blksha1 +curl cgi doc emacs gtk iconv +perl ppcsha1 tk +threads +webdav xinetd cvs subversion bash-completion
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-2
+MD5: 0d010cb874ae0fe8bdfedc383725bdc6
+MTIME: 1264298591
+RDEPEND: sys-libs/zlib dev-lang/perl dev-lang/tk net-misc/curl dev-libs/expat dev-perl/Error dev-perl/Net-SMTP-SSL dev-perl/Authen-SASL >=dev-python/pygtk-2.8 || ( dev-python/pygtksourceview:2 dev-python/gtksourceview-python ) dev-lang/perl[-build]
+SHA1: 528c6eefe610e85d3b81e12367f53fca5c13ca7b
+SIZE: 6604580
+USE: blksha1 curl gtk iconv perl threads tk webdav
+REPO: gentoo
+
+CPV: gnome-base/gdm-2.20.10-r3
+DEPEND: >=dev-libs/glib-2.12 >=x11-libs/gtk+-2.6 >=x11-libs/pango-1.3 >=gnome-base/libglade-2 >=gnome-base/libgnomecanvas-2 >=gnome-base/librsvg-1.1.1 >=dev-libs/libxml2-2.4.12 >=media-libs/libart_lgpl-2.3.11 x11-libs/gksu x11-libs/libXi x11-libs/libXau x11-libs/libX11 x11-libs/libXext x11-apps/sessreg x11-libs/libXdmcp sys-auth/consolekit dev-libs/dbus-glib virtual/pam >=sys-apps/tcp-wrappers-7.6 >=x11-misc/xdg-utils-1.0.2-r3 sys-devel/gettext x11-proto/inputproto >=dev-util/intltool-0.35 >=dev-util/pkgconfig-0.19 >=app-text/scrollkeeper-0.1.4 >=app-text/gnome-doc-utils-0.3.2 >=sys-apps/sed-4
+DESC: GNOME Display Manager
+EAPI: 2
+IUSE: accessibility afs branding +consolekit dmx ipv6 gnome-keyring pam remote selinux tcpd xinerama elibc_glibc debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: dfe36693471fa40134f834ccd696f25e
+MTIME: 1264298505
+PDEPEND: >=sys-auth/pambase-20090430[consolekit=,gnome-keyring=]
+RDEPEND: >=dev-libs/glib-2.12 >=x11-libs/gtk+-2.6 >=x11-libs/pango-1.3 >=gnome-base/libglade-2 >=gnome-base/libgnomecanvas-2 >=gnome-base/librsvg-1.1.1 >=dev-libs/libxml2-2.4.12 >=media-libs/libart_lgpl-2.3.11 x11-libs/gksu x11-libs/libXi x11-libs/libXau x11-libs/libX11 x11-libs/libXext x11-apps/sessreg x11-libs/libXdmcp sys-auth/consolekit dev-libs/dbus-glib virtual/pam >=sys-apps/tcp-wrappers-7.6 >=x11-misc/xdg-utils-1.0.2-r3
+SHA1: 0f712fa78540566dc2b33252e8e00476546ecaa7
+SIZE: 3687872
+USE: branding consolekit elibc_glibc ipv6 pam tcpd
+REPO: gentoo
+
+CPV: gnome-extra/gnome-screensaver-2.28.0-r1
+DEPEND: >=gnome-base/gconf-2.6.1 >=x11-libs/gtk+-2.14.0 >=gnome-base/gnome-desktop-2.23.2 >=gnome-base/gnome-menus-2.12 >=dev-libs/glib-2.15 >=gnome-base/libgnomekbd-0.1 >=dev-libs/dbus-glib-0.71 x11-libs/libnotify virtual/opengl virtual/pam x11-libs/libX11 x11-libs/libXext x11-libs/libXrandr x11-libs/libXScrnSaver x11-libs/libXxf86misc x11-libs/libXxf86vm >=dev-util/pkgconfig-0.9 >=dev-util/intltool-0.40 x11-proto/xextproto x11-proto/randrproto x11-proto/scrnsaverproto x11-proto/xf86miscproto >=sys-apps/sed-4
+DESC: Replaces xscreensaver, integrating with the desktop.
+EAPI: 2
+IUSE: debug doc libnotify opengl pam kernel_linux debug
+KEYWORDS: ~alpha ~amd64 ~hppa ~ia64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: GPL-2
+MD5: 79e247b6b43668c3e49ff706d47ea577
+MTIME: 1264298398
+RDEPEND: >=gnome-base/gconf-2.6.1 >=x11-libs/gtk+-2.14.0 >=gnome-base/gnome-desktop-2.23.2 >=gnome-base/gnome-menus-2.12 >=dev-libs/glib-2.15 >=gnome-base/libgnomekbd-0.1 >=dev-libs/dbus-glib-0.71 x11-libs/libnotify virtual/opengl virtual/pam x11-libs/libX11 x11-libs/libXext x11-libs/libXrandr x11-libs/libXScrnSaver x11-libs/libXxf86misc x11-libs/libXxf86vm
+SHA1: 9143a14b6736643bbb5aee20c8cdd6510dc7876f
+SIZE: 4621029
+USE: kernel_linux libnotify opengl pam
+REPO: gentoo
+
+CPV: gnome-extra/gnome-utils-2.28.3
+DEPEND: >=dev-libs/glib-2.20.0 >=x11-libs/gtk+-2.18.0 >=gnome-base/gnome-panel-2.13.4 >=gnome-base/libgtop-2.12 >=gnome-base/gconf-2 >=media-libs/libcanberra-0.4[gtk] x11-libs/libXext x11-proto/xextproto app-text/gnome-doc-utils app-text/scrollkeeper >=dev-util/intltool-0.40 >=dev-util/pkgconfig-0.9 >=sys-apps/sed-4
+DESC: Utilities for the Gnome2 desktop
+EAPI: 2
+IUSE: doc ipv6 test debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~sh ~sparc ~x86 ~x86-fbsd ~x86-freebsd ~amd64-linux ~x86-linux
+LICENSE: GPL-2
+MD5: 486f6257e2c76a6ddfbaea60cd8d9ae3
+MTIME: 1264298299
+RDEPEND: >=dev-libs/glib-2.20.0 >=x11-libs/gtk+-2.18.0 >=gnome-base/gnome-panel-2.13.4 >=gnome-base/libgtop-2.12 >=gnome-base/gconf-2 >=media-libs/libcanberra-0.4[gtk] x11-libs/libXext
+SHA1: df3899a81734cd33de0288d391328f682b858319
+SIZE: 6282009
+USE: ipv6
+REPO: gentoo
+
+CPV: media-libs/alsa-lib-1.0.22
+DEPEND: dev-lang/python >=media-sound/alsa-headers-1.0.22
+DESC: Advanced Linux Sound Architecture Library
+IUSE: doc debug alisp python alsa_pcm_plugins_copy alsa_pcm_plugins_linear alsa_pcm_plugins_route alsa_pcm_plugins_mulaw alsa_pcm_plugins_alaw alsa_pcm_plugins_adpcm alsa_pcm_plugins_rate alsa_pcm_plugins_plug alsa_pcm_plugins_multi alsa_pcm_plugins_shm alsa_pcm_plugins_file alsa_pcm_plugins_null alsa_pcm_plugins_empty alsa_pcm_plugins_share alsa_pcm_plugins_meter alsa_pcm_plugins_mmap_emul alsa_pcm_plugins_hooks alsa_pcm_plugins_lfloat alsa_pcm_plugins_ladspa alsa_pcm_plugins_dmix alsa_pcm_plugins_dshare alsa_pcm_plugins_dsnoop alsa_pcm_plugins_asym alsa_pcm_plugins_iec958 alsa_pcm_plugins_softvol alsa_pcm_plugins_extplug alsa_pcm_plugins_ioplug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86 ~amd64-linux ~x86-linux
+LICENSE: LGPL-2.1
+MD5: 94b646ca40670aec529e2fb1b49b36df
+MTIME: 1264298087
+RDEPEND: dev-lang/python
+SHA1: 43ef8b55737d77122ac2373e13a3cf9e95486be3
+SIZE: 817219
+USE: alsa_pcm_plugins_adpcm alsa_pcm_plugins_alaw alsa_pcm_plugins_asym alsa_pcm_plugins_copy alsa_pcm_plugins_dmix alsa_pcm_plugins_dshare alsa_pcm_plugins_dsnoop alsa_pcm_plugins_empty alsa_pcm_plugins_extplug alsa_pcm_plugins_file alsa_pcm_plugins_hooks alsa_pcm_plugins_iec958 alsa_pcm_plugins_ioplug alsa_pcm_plugins_ladspa alsa_pcm_plugins_lfloat alsa_pcm_plugins_linear alsa_pcm_plugins_meter alsa_pcm_plugins_mmap_emul alsa_pcm_plugins_mulaw alsa_pcm_plugins_multi alsa_pcm_plugins_null alsa_pcm_plugins_plug alsa_pcm_plugins_rate alsa_pcm_plugins_route alsa_pcm_plugins_share alsa_pcm_plugins_shm alsa_pcm_plugins_softvol python
+REPO: gentoo
+
+CPV: media-libs/jpeg-8
+DESC: Library to load, handle and manipulate images in the JPEG format
+EAPI: 2
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~x86-interix ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: as-is
+MD5: bc17f6bbd3fe11172bbd524a1101c671
+MTIME: 1264298168
+SHA1: fb72dde71839e9368c81fa54a468a8dab9c94608
+SIZE: 397321
+REPO: gentoo
+
+CPV: media-sound/alsa-headers-1.0.22
+DESC: Header files for Advanced Linux Sound Architecture kernel modules
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86
+LICENSE: GPL-2
+MD5: 9ac32ab8b9ed4a675fb4987a144dbf25
+MTIME: 1264298022
+SHA1: 728ba7f1e1a3b56a17b486aa95002d391022a131
+SIZE: 166783
+REPO: gentoo
+
+CPV: media-sound/alsa-utils-1.0.22-r1
+DEPEND: >=sys-libs/ncurses-5.1 dev-util/dialog >=media-libs/alsa-lib-1.0.22
+DESC: Advanced Linux Sound Architecture Utils (alsactl, alsamixer, etc.)
+EAPI: 2
+IUSE: doc nls minimal
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86
+LICENSE: GPL-2
+MD5: de6300fd679058a875d7a0963620f05c
+MTIME: 1264298123
+RDEPEND: >=sys-libs/ncurses-5.1 dev-util/dialog >=media-libs/alsa-lib-1.0.22 virtual/modutils sys-apps/pciutils
+SHA1: 47574253fa21e2d0c44323b2983e2558abc49fed
+SIZE: 991245
+SLOT: 0.9
+USE: nls
+REPO: gentoo
+
+CPV: media-video/mjpegtools-1.9.0-r1
+DEPEND: media-libs/jpeg:0 x11-libs/gtk+:2 >=media-libs/libdv-0.99 virtual/quicktime media-libs/libpng >=media-libs/libsdl-1.2.7-r3 x11-libs/libX11 x11-libs/libXt dev-lang/nasm >=sys-apps/sed-4 dev-util/pkgconfig
+DESC: Tools for MJPEG video
+EAPI: 1
+IUSE: gtk dv quicktime sdl X yv12 v4l dga png mmx
+KEYWORDS: ~alpha ~amd64 ~ppc ~ppc64 ~sparc ~x86
+LICENSE: as-is
+MD5: 0f8d14bb49c7452144f9f42da59df67d
+MTIME: 1264297981
+RDEPEND: media-libs/jpeg:0 x11-libs/gtk+:2 >=media-libs/libdv-0.99 virtual/quicktime media-libs/libpng >=media-libs/libsdl-1.2.7-r3 x11-libs/libX11 x11-libs/libXt
+SHA1: 6eea5264c255be8aed69533455e5ad313506ff32
+SIZE: 1184020
+SLOT: 1
+USE: X dv gtk mmx png quicktime sdl v4l
+REPO: gentoo
+
+CPV: net-fs/samba-3.4.5
+DESC: Meta package for samba-{libs,client,server}
+EAPI: 2
+IUSE: +client +server
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: GPL-3
+MD5: ff6e613b5a052817e38edeea74296ae9
+MTIME: 1264297884
+RDEPEND: ~net-fs/samba-libs-3.4.5 ~net-fs/samba-client-3.4.5 ~net-fs/samba-server-3.4.5
+SHA1: a4d1669379c6727533b1c92725ef4fd7004e3d33
+SIZE: 4841
+USE: client server
+REPO: gentoo
+
+CPV: net-fs/samba-client-3.4.5
+DEPEND: !<net-fs/samba-3.3 !net-fs/mount-cifs dev-libs/popt dev-libs/iniparser virtual/libiconv net-print/cups net-nds/openldap virtual/logger sys-libs/tdb sys-libs/talloc ~net-fs/samba-libs-3.4.5[caps?,cups?,ldap?,syslog?,winbind?,ads?,samba4?,netapi] =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Client bits of the samba network filesystem
+EAPI: 2
+IUSE: samba4 ads aio avahi caps cluster cups debug ldap minimal syslog winbind zeroconf
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: GPL-3
+MD5: f63cbda580c8e3cab3274e7899024ffa
+MTIME: 1264297864
+RDEPEND: !<net-fs/samba-3.3 !net-fs/mount-cifs dev-libs/popt dev-libs/iniparser virtual/libiconv net-print/cups net-nds/openldap virtual/logger sys-libs/tdb sys-libs/talloc ~net-fs/samba-libs-3.4.5[caps?,cups?,ldap?,syslog?,winbind?,ads?,samba4?,netapi]
+SHA1: 40a7f84d99285356d73969c51d34a2b03370c749
+SIZE: 16387850
+USE: avahi cups ldap syslog
+REPO: gentoo
+
+CPV: net-fs/samba-libs-3.4.5
+DEPEND: dev-libs/popt sys-libs/talloc sys-libs/tdb virtual/libiconv virtual/krb5 sys-fs/e2fsprogs sys-libs/libcap net-print/cups net-nds/openldap virtual/pam virtual/logger !<net-fs/samba-3.3 !=net-fs/samba-server-3.4.3[tools] =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Library bits of the samba network filesystem
+EAPI: 2
+IUSE: samba4 ads aio caps cluster cups debug examples ldap pam syslog winbind ldb +netapi +smbclient smbsharemodes addns tools
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: GPL-3
+MD5: 9be0477a3595631183d8e9542ec9b7d8
+MTIME: 1264297437
+RDEPEND: dev-libs/popt sys-libs/talloc sys-libs/tdb virtual/libiconv virtual/krb5 sys-fs/e2fsprogs sys-libs/libcap net-print/cups net-nds/openldap virtual/pam virtual/logger !<net-fs/samba-3.3 !=net-fs/samba-server-3.4.3[tools]
+SHA1: e0a1459615d016fe5609546736c6fc5ea6089ba2
+SIZE: 3958751
+USE: ads caps cups ldap netapi pam smbclient syslog
+REPO: gentoo
+
+CPV: net-fs/samba-server-3.4.5
+DEPEND: !<net-fs/samba-3.3 dev-libs/popt virtual/libiconv net-dns/avahi sys-libs/libcap net-print/cups dev-libs/libgamin net-nds/openldap sys-libs/pam virtual/logger sys-libs/tdb sys-libs/talloc ~net-fs/samba-libs-3.4.5[caps?,cluster?,cups?,ldap?,syslog?,winbind?,ads?,samba4?] =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Samba Server component
+EAPI: 2
+IUSE: samba4 acl ads aio avahi caps cluster cups debug doc examples fam ldap pam quota swat syslog winbind zeroconf
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: GPL-3
+MD5: a6a7786994543a7c7b4701c6e2b5176e
+MTIME: 1264297656
+RDEPEND: !<net-fs/samba-3.3 dev-libs/popt virtual/libiconv net-dns/avahi sys-libs/libcap net-print/cups dev-libs/libgamin net-nds/openldap sys-libs/pam virtual/logger sys-libs/tdb sys-libs/talloc ~net-fs/samba-libs-3.4.5[caps?,cluster?,cups?,ldap?,syslog?,winbind?,ads?,samba4?]
+SHA1: e1b9416b6ab579d126bd89e4a58298ec4be5282f
+SIZE: 16226041
+USE: avahi caps cups fam ldap pam swat syslog
+REPO: gentoo
+
+CPV: net-misc/rsync-3.0.7
+DEPEND: >=dev-libs/popt-1.5 virtual/acl virtual/libiconv
+DESC: File transfer program to keep remote files into sync
+IUSE: acl iconv ipv6 static xattr
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 49d5d5cc5d5b18eb811e3544114afe19
+MTIME: 1264300657
+RDEPEND: >=dev-libs/popt-1.5 virtual/acl virtual/libiconv
+SHA1: 6d018cbcef3e17d669a29789ed5ff0eb15cdd694
+SIZE: 335209
+USE: acl iconv ipv6
+REPO: gentoo
+
+CPV: net-misc/wget-1.12-r1
+DEPEND: >=dev-libs/openssl-0.9.6b sys-devel/gettext
+DESC: Network utility to retrieve files from the WWW
+IUSE: debug idn ipv6 nls ntlm ssl static
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-3
+MD5: 258c0fb155252501c242d52bf2215f25
+MTIME: 1264300596
+RDEPEND: >=dev-libs/openssl-0.9.6b
+SHA1: 09324271a9897a3709b79a8f9e93e8abc19cae6c
+SIZE: 561460
+USE: ipv6 nls ssl
+REPO: gentoo
+
+CPV: perl-core/Module-Build-0.36.03
+DEPEND: dev-perl/YAML-Tiny >=virtual/perl-ExtUtils-CBuilder-0.27 >=virtual/perl-Archive-Tar-1.09 >=virtual/perl-Test-Harness-3.16 dev-lang/perl[-build]
+DESC: Build and install Perl modules
+EAPI: 2
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd ~x86-freebsd ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: || ( Artistic GPL-2 )
+MD5: c23f38d6da0a2a5bbd0cee762ce75bee
+MTIME: 1264296675
+PDEPEND: >=virtual/perl-ExtUtils-ParseXS-2.21
+RDEPEND: dev-perl/YAML-Tiny >=virtual/perl-ExtUtils-CBuilder-0.27 >=virtual/perl-Archive-Tar-1.09 >=virtual/perl-Test-Harness-3.16 dev-lang/perl[-build]
+SHA1: a348aea68a6d91432da625ef421ccc8ae9c2b74a
+SIZE: 186756
+REPO: gentoo
+
+CPV: perl-core/Test-Harness-3.20
+DEPEND: dev-lang/perl
+DESC: Runs perl standard test scripts with statistics
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: || ( Artistic GPL-2 )
+MD5: 5585c6a28745f6a59bc477f74293d117
+MTIME: 1264296640
+RDEPEND: dev-lang/perl
+SHA1: fb3f4d9701a38bf199386e427473c5a501a13ff7
+SIZE: 129603
+REPO: gentoo
+
+CPV: sys-apps/openrc-0.6.0-r1
+DEPEND: virtual/init >=sys-libs/glibc-2.5 sys-libs/ncurses virtual/pam >=sys-apps/baselayout-2.0.0 !<sys-apps/module-init-tools-3.2.2-r2 !<sys-fs/udev-133 !<sys-apps/sysvinit-2.86-r11 virtual/os-headers
+DESC: OpenRC manages the services, startup and shutdown of a host
+EAPI: 1
+IUSE: debug elibc_glibc ncurses pam unicode kernel_linux kernel_FreeBSD
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd ~x86 ~x86-fbsd
+LICENSE: BSD-2
+MD5: 3cf179b08d4afaa8e36f12758c8ea19a
+MTIME: 1264297118
+RDEPEND: virtual/init >=sys-libs/glibc-2.5 sys-libs/ncurses virtual/pam >=sys-apps/baselayout-2.0.0 !<sys-apps/module-init-tools-3.2.2-r2 !<sys-fs/udev-133 !<sys-apps/sysvinit-2.86-r11
+SHA1: e88777495cef17c213c7935e2d50a08eecd1dfab
+SIZE: 229060
+USE: elibc_glibc kernel_linux ncurses pam unicode
+REPO: gentoo
+
+CPV: sys-apps/shadow-4.0.18.2
+DEPEND: >=sys-libs/cracklib-2.7-r3 >=sys-libs/pam-0.99 !sys-apps/pam-login !app-admin/nologin virtual/libintl sys-devel/gettext =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Utilities to deal with user accounts
+IUSE: nls pam selinux skey nousuid cracklib
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86
+LICENSE: BSD GPL-2
+MD5: fcbd34cfbcb0e3399c2f3fa47938f90f
+MTIME: 1264265029
+RDEPEND: >=sys-libs/cracklib-2.7-r3 >=sys-libs/pam-0.99 !sys-apps/pam-login !app-admin/nologin virtual/libintl
+SHA1: 8c3bcdb01ae7be7ce50951cc94c80b57839a39f3
+SIZE: 1176337
+USE: cracklib nls pam
+REPO: gentoo
+
+CPV: sys-apps/shadow-4.1.2.2
+DEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl sys-devel/gettext =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: Utilities to deal with user accounts
+IUSE: audit cracklib nls pam selinux skey
+KEYWORDS: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64 s390 sh sparc x86
+LICENSE: BSD GPL-2
+MD5: 0d1635fe78adf406111df9b0e7c711fb
+MTIME: 1264265117
+RDEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl >=sys-auth/pambase-20080219.1
+SHA1: 7915518b563eeadb03eea21d56e6e9a1dd181e15
+SIZE: 1338463
+USE: cracklib nls pam
+REPO: gentoo
+
+CPV: sys-apps/shadow-4.1.4.2-r1
+DEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl sys-devel/gettext
+DESC: Utilities to deal with user accounts
+IUSE: audit nls skey selinux pam cracklib
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: BSD GPL-2
+MD5: e96104ddb3a926dc483ffb5ddb1765b3
+MTIME: 1264264912
+RDEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl >=sys-auth/pambase-20080219.1
+SHA1: 9ffc5e5b6711c65aef3b09de1cf543f713aae14d
+SIZE: 1465224
+USE: cracklib nls pam
+
+CPV: sys-apps/shadow-4.1.4.2-r2
+DEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl sys-devel/gettext
+DESC: Utilities to deal with user accounts
+IUSE: audit cracklib nls pam selinux skey
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86
+LICENSE: BSD GPL-2
+MD5: d979450afe73a2f884e88249d515b359
+MTIME: 1264300537
+RDEPEND: >=sys-libs/cracklib-2.7-r3 virtual/pam !sys-apps/pam-login !app-admin/nologin virtual/libintl >=sys-auth/pambase-20080219.1
+SHA1: 7da596c64d4afcb33aa4b4fc221a3c0e2f3eb661
+SIZE: 1379534
+USE: cracklib nls pam
+REPO: gentoo
+
+CPV: sys-cluster/openmpi-1.4.1
+DEPEND: !sys-cluster/mpich !sys-cluster/lam-mpi !sys-cluster/mpich2 !sys-cluster/mpiexec =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool
+DESC: A high-performance message passing library (MPI)
+EAPI: 2
+IUSE: +cxx elibc_FreeBSD fortran heterogeneous ipv6 mpi-threads pbs romio threads vt debug
+KEYWORDS: ~alpha ~amd64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd
+LICENSE: BSD
+MD5: 89e732e99246058f5e2c165c3e0ac2e4
+MTIME: 1264297088
+RDEPEND: !sys-cluster/mpich !sys-cluster/lam-mpi !sys-cluster/mpich2 !sys-cluster/mpiexec
+SHA1: 095fa67854378fb6411a2d1f16fd1baea50f05e7
+SIZE: 1698447
+USE: cxx fortran ipv6
+REPO: gentoo
+
+CPV: sys-devel/make-3.81-r1
+DEPEND: sys-devel/gettext
+DESC: Standard tool to compile source trees
+IUSE: nls static
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: GPL-2
+MD5: dee4cc30ddd4572bfbf86006fad9f8ac
+MTIME: 1264300445
+RDEPEND: virtual/libintl
+SHA1: cd51781fb655f615ae6ceedbbc98a22dacf335c6
+SIZE: 480846
+USE: nls
+REPO: gentoo
+
+CPV: sys-fs/udev-150
+DEPEND: sys-apps/acl >=sys-apps/usbutils-0.82 virtual/libusb:0 sys-apps/pciutils dev-libs/glib:2 >=sys-apps/util-linux-2.16 >=sys-libs/glibc-2.9 dev-util/gperf >=sys-kernel/linux-headers-2.6.29
+DESC: Linux dynamic and persistent device naming support (aka userspace devfs)
+EAPI: 1
+IUSE: selinux +devfs-compat -extras test
+KEYWORDS: -alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 -sh ~sparc ~x86
+LICENSE: GPL-2
+MD5: 7c0af29d69df731992e14ec3c180f699
+MTIME: 1264300392
+PROVIDE: virtual/dev-manager
+RDEPEND: sys-apps/acl >=sys-apps/usbutils-0.82 virtual/libusb:0 sys-apps/pciutils dev-libs/glib:2 >=sys-apps/util-linux-2.16 >=sys-libs/glibc-2.9 !sys-apps/coldplug !<sys-fs/lvm2-2.02.45 !sys-fs/device-mapper >=sys-apps/baselayout-1.12.5
+SHA1: 817a08f5ea7fe1e2b0d5a24c9f7f217893247443
+SIZE: 411550
+USE: devfs-compat extras
+REPO: gentoo
+
+CPV: sys-libs/libstdc++-v3-3.3.6
+DESC: Compatibility package for running binaries linked against a pre gcc 3.4 libstdc++
+IUSE: multilib nls
+KEYWORDS: amd64 hppa ~mips ppc -ppc64 sparc x86 ~x86-fbsd
+LICENSE: GPL-2 LGPL-2.1
+MD5: bbd30835e34931e5cfbcf71fd552dcb3
+MTIME: 1264300213
+SHA1: 4aa8ec56fc28a78ab08f18ca9359cc3dce38395c
+SIZE: 515790
+SLOT: 5
+USE: multilib nls
+REPO: gentoo
+
+CPV: sys-libs/timezone-data-2010a
+DESC: Timezone data (/usr/share/zoneinfo) and utilities (tzselect/zic/zdump)
+IUSE: nls elibc_FreeBSD elibc_glibc
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd
+LICENSE: BSD public-domain
+MD5: 7d66db70226aafae9de4f3a2c0828d3d
+MTIME: 1264296710
+RDEPEND: !<sys-libs/glibc-2.3.5
+SHA1: 185776f76ed6ac0d877fbf656081fa0d7953da80
+SIZE: 596093
+USE: elibc_glibc nls
+REPO: gentoo
+
+CPV: virtual/perl-Module-Build-0.36.03
+DESC: Build and install Perl modules
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd ~x86-fbsd ~x86-freebsd ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+MD5: df63da5a04fff3934bd802441cdd4b78
+MTIME: 1264296690
+RDEPEND: ~perl-core/Module-Build-0.36.03
+SHA1: b50f0dfdef8a7459657d614c4ac82529437fc641
+SIZE: 4921
+REPO: gentoo
+
+CPV: virtual/perl-Test-Harness-3.20
+DESC: Virtual for Test-Harness
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~sparc-fbsd ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~ia64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+MD5: e5f9572a047b55e74178b3904cc07893
+MTIME: 1264296653
+RDEPEND: ~perl-core/Test-Harness-3.20
+SHA1: 868ab70f38449b6909b6f90fcba633943465d01e
+SIZE: 5019
+REPO: gentoo
+
+CPV: x11-libs/libX11-1.3.3
+DEPEND: >=x11-libs/xtrans-1.2.3 x11-proto/kbproto >=x11-proto/xproto-7.0.13 >=x11-libs/libxcb-1.1.92 x11-proto/xf86bigfontproto x11-proto/inputproto x11-proto/xextproto =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool >=sys-devel/libtool-1.5 >=sys-devel/m4-1.4 >=dev-util/pkgconfig-0.18 >=x11-misc/util-macros-1.3.0 sys-devel/binutils
+DESC: X.Org X11 library
+EAPI: 1
+IUSE: doc ipv6 +xcb debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: MIT
+MD5: e85bbc11b6eb49ea21291d8087069a18
+MTIME: 1264296590
+RDEPEND: >=x11-libs/xtrans-1.2.3 x11-proto/kbproto >=x11-proto/xproto-7.0.13 >=x11-libs/libxcb-1.1.92 !<=x11-base/xorg-x11-6.9
+SHA1: 0049699510acc9fa961429a87b2076800513897a
+SIZE: 2716558
+USE: ipv6 xcb
+REPO: gentoo
+
+CPV: x11-libs/pixman-0.17.4
+DEPEND: =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool >=sys-devel/libtool-1.5 >=sys-devel/m4-1.4 >=dev-util/pkgconfig-0.18 >=x11-misc/util-macros-1.3.0 sys-devel/binutils
+DESC: Low-level pixel manipulation routines
+IUSE: altivec mmx sse2 debug
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: MIT
+MD5: e41323dc2670f013788f2cd6a0b2238e
+MTIME: 1264296471
+RDEPEND: !<=x11-base/xorg-x11-6.9
+SHA1: b5c213200abcbae0fd474ca18ee367ec12824117
+SIZE: 265452
+USE: mmx sse2
+REPO: gentoo
+
+CPV: x11-libs/wxGTK-2.8.10.1-r4
+DEPEND: dev-libs/expat media-libs/libsdl >=x11-libs/gtk+-2.4 >=dev-libs/glib-2.4 media-libs/jpeg media-libs/tiff x11-libs/libSM x11-libs/libXinerama x11-libs/libXxf86vm gnome-base/libgnomeprintui >=gnome-base/gconf-2.0 >=media-libs/gstreamer-0.10 virtual/opengl dev-util/pkgconfig x11-proto/xproto x11-proto/xineramaproto x11-proto/xf86vidmodeproto
+DESC: GTK+ version of wxWidgets, a cross-platform C++ GUI toolkit.
+EAPI: 2
+IUSE: X doc debug gnome gstreamer odbc opengl pch sdl
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sh ~sparc ~x86 ~x86-fbsd
+LICENSE: wxWinLL-3 GPL-2
+MD5: b4c3f83df18f8775816bb0849aeab73f
+MTIME: 1264296353
+PDEPEND: >=app-admin/eselect-wxwidgets-0.7
+RDEPEND: dev-libs/expat media-libs/libsdl >=x11-libs/gtk+-2.4 >=dev-libs/glib-2.4 media-libs/jpeg media-libs/tiff x11-libs/libSM x11-libs/libXinerama x11-libs/libXxf86vm gnome-base/libgnomeprintui >=gnome-base/gconf-2.0 >=media-libs/gstreamer-0.10 virtual/opengl
+SHA1: 67137d092bc59524eb26434fa49e60f0d6112dd3
+SIZE: 5349784
+SLOT: 2.8
+USE: X gnome gstreamer opengl sdl
+REPO: gentoo
+
+CPV: x11-misc/util-macros-1.5.0
+DEPEND: =sys-devel/automake-1.10* >=sys-devel/autoconf-2.61 sys-devel/libtool >=sys-devel/libtool-1.5 >=sys-devel/m4-1.4 >=dev-util/pkgconfig-0.18
+DESC: X.Org autotools utility macros
+KEYWORDS: ~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~ppc-aix ~x86-fbsd ~x64-freebsd ~x86-freebsd ~hppa-hpux ~ia64-hpux ~x86-interix ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris
+LICENSE: MIT
+MD5: 89e9ed49ae7e8ce4fbf90fda82010dff
+MTIME: 1264296041
+RDEPEND: !<=x11-base/xorg-x11-6.9
+SHA1: e732842c14219ce7f03186c8ac1e15ca45269eb2
+SIZE: 49929
+REPO: gentoo
+
diff --git a/pym/gentoolkit/test/eclean/__init__.py b/pym/gentoolkit/test/eclean/__init__.py
new file mode 100644 (file)
index 0000000..e5a3c12
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+# Copyright 2010 Gentoo Foundation
+#
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
diff --git a/pym/gentoolkit/test/eclean/creator.py b/pym/gentoolkit/test/eclean/creator.py
new file mode 100644 (file)
index 0000000..8ab8739
--- /dev/null
@@ -0,0 +1,243 @@
+#!/usr/bin/python
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+__version__= "0.0.1"
+__author__ = "Brian Dolbec"
+__email__ = "brian.dolbec@gmail.com"
+
+from __future__ import with_statement
+from __future__ import print_function
+
+import os
+import sys
+import shutil
+import random
+
+import gentoolkit.pprinter as pp
+
+
+
+dir_mode = 0774
+file_mode = 0644
+
+
+def make_dir(path):
+       """create the directory at path
+
+       @param path: full pathname to create
+       capable of multiple intermediate directory creations.
+       Will Error and exit if the target dir already exits"""
+       try:
+               os.makedirs(path, dir_mode)
+       except EnvironmentError, er:
+               print( pp.error("Error creating path:%s" %path), file=sys.stderr)
+               print( pp.error("Error: %s" %str(er), file=sys.stderr)
+               sys.exit(1)
+
+
+def make_dist(path, files, clean_dict=None):
+       """Creates a small fake distfiles/binpkg directory @path populated
+       with generated files of small random sizes using real names from
+       the files list. udates the clean_dict with fullpathname.
+
+       @param path: the path to create the distfiles directory
+       @param files: list of file names to populate "path" with
+       @param clean_dict: dict of {file-key:[path/file-key,],}
+                       that will be updated with full file-path-names
+       """
+       make_dir(path)
+       for file_ in files:
+               size = random.randint(1000,5000)
+               data = "0" * size
+               filepath = os.path.join(path, file_)
+               with open(filepath, 'w', file_mode) as new_file:
+                       new_file.write(data)
+               if file_ not in clean_dict:
+                       # it is included in a multifile target
+                       continue
+               elif clean_dict[file_] = []:
+                       clean_dict[file_] = filepath
+               else:
+                       file_list = clean_dict[file_]
+                       for key in range(len(file_list)):
+                               file_list[key] = os.path.join(path, file_list[key])
+
+
+def make_pkgs(path, files_dict, clean_dict):
+       """Create a small fake packages directory and call make_dist() to
+       create and populate the category dir & package files
+
+       @param path: the path to create the packages directory
+       @param files_dict: dictionary of {cat: [pkg1, pkg2,...]}
+       """
+       make_dir(path)
+       for cat in files_dict.keys():
+               make_dist(os.path.join(path,cat),
+                       files_dict[cat],
+                       clean_dict)
+       # cp the Packages index file to path
+       source = os.path.join(os.path.dirname(__file__), 'Packages')
+       shutil.copy2(source, path)
+
+
+def make_symlinks(path, links, targets):
+       """Create some symlinks at path
+
+       @param path: the location to create the symlinks at
+       @param links: list of links to create
+       @param targets: list of targets to create links for,
+                       and need to be in the same index order as links
+       """
+       for i in range(len(links)):
+               os.symlink(os.path.join(path,target[i]),
+                       os.path.join(path, links[i]))
+
+
+class TestDirCreation(object):
+    """"""
+
+    distfile_list = ['ExtUtils-ParseXS-2.22.tar.gz',
+        'xorg-server-1.5.3.tar.bz2',
+        'portage-utils-0.2.1.tar.bz2',
+        'sysvinit_2.87dsf.orig.tar.gz',
+        'sysvinit-2.86.tar.gz',
+        'ExtUtils-ParseXS-2.20.tar.gz',
+        'libisofs-0.6.22.tar.gz',
+        'pixman-0.16.0.tar.bz2',
+        'libburn-0.7.2.pl01.tar.gz',
+        'libisofs-0.6.24.tar.gz',
+        'xorg-server-1.5.3-gentoo-patches-08.tar.bz2',
+        'ExtUtils-ParseXS-2.200401.tar.gz',
+        'sysvinit-2.87-patches-2.tar.bz2',
+        'sysvinit-2.86-kexec.patch',
+        'Module-Build-0.3601.tar.gz',
+        'libisofs-0.6.20.tar.gz',
+        'xine-lib-1.1.17.tar.bz2',
+        'pixman-0.14.0.tar.bz2',
+        'Archive-Tar-1.52.tar.gz',
+        'libburn-0.6.8.pl00.tar.gz',
+        'libexif-0.6.17.tar.bz2',
+        'portage-utils-0.3.tar.bz2',
+        'xine-lib-1.1.15-textrel-fix.patch',
+        'Module-Build-0.34.tar.gz',
+        'Archive-Tar-1.54.tar.gz',
+        'pixman-0.16.2.tar.bz2',
+        'libburn-0.7.4.pl00.tar.gz ',
+        'Module-Build-0.340201.tar.gz',
+        'pixman-0.17.2.tar.bz2',
+        'util-macros-1.3.0.tar.bz2',
+        'Module-Build-0.35.tar.gz',
+        'libburn-0.7.2.pl00.tar.gz',
+        'util-macros-1.4.1.tar.bz2',
+        'xine-lib-1.1.16.3.tar.bz2',
+        'sysvinit-2.86-extra.patch',
+        'libburn-0.7.0.pl00.tar.gz',
+        'ExtUtils-ParseXS-2.21.tar.gz',
+        'libexif-0.6.19.tar.bz2',
+        'sysvinit-2.87-patches-1.tar.bz2',
+        # now a base pkg with 2 additional symlink targets
+        'symlink-test-1.2.3.tar.bz2',
+        'target-1',
+        'target-2'
+        ]
+
+    distfile_symlink = ['symlink-test-1.2.3-symlink1',
+        'symlink-test-1.2.3-symlink2']
+
+    dist_clean = {
+        'Archive-Tar-1.52.tar.gz': [],
+        'ExtUtils-ParseXS-2.20.tar.gz': [],
+        'ExtUtils-ParseXS-2.200401.tar.gz': [],
+        'ExtUtils-ParseXS-2.21.tar.gz': [],
+        'Module-Build-0.34.tar.gz': [],
+        'Module-Build-0.340201.tar.gz': [],
+        'Module-Build-0.35.tar.gz': [],
+        'libburn-0.6.8.pl00.tar.gz': [],
+        'libburn-0.7.0.pl00.tar.gz': [],
+        'libburn-0.7.2.pl00.tar.gz': [],
+        'libburn-0.7.2.pl01.tar.gz': [],
+        'libexif-0.6.17.tar.bz2': [],
+        'libisofs-0.6.20.tar.gz': [],
+        'libisofs-0.6.22.tar.gz': [],
+        'pixman-0.14.0.tar.bz2': [],
+        'pixman-0.16.0.tar.bz2': [],
+        'pixman-0.16.2.tar.bz2': [],
+        'portage-utils-0.2.1.tar.bz2': [],
+        'sysvinit-2.86.tar.gz': ['sysvinit-2.86.tar.gz',
+            'sysvinit-2.86-kexec.patch', 'sysvinit-2.86-extra.patch'],
+        'util-macros-1.3.0.tar.bz2': [],
+        'xine-lib-1.1.15-textrel-fix.patch': [],
+        'xine-lib-1.1.16.3.tar.bz2': [],
+        'xorg-server-1.5.3.tar.bz2': ['xorg-server-1.5.3.tar.bz2',
+            'xorg-server-1.5.3-gentoo-patches-08.tar.bz2']
+        'symlink-test-1.2.3.tar.bz2': distfile_symlink
+    }
+
+    package_dict = {
+        'app-arch': ['p7zip-4.65.tbz2', 'p7zip-4.57.tbz2',
+            'file-roller-2.26.3.tbz2', 'tar-1.20.tbz2',
+            'p7zip-4.58.tbz2', 'file-roller-2.28.2.tbz2',
+            'file-roller-2.24.3.tbz2', 'gzip-1.4.tbz2', 'rar-3.9.0.tbz2',
+            'bzip2-1.0.5-r1.tbz2', 'cpio-2.10.tbz2', 'tar-1.21-r1.tbz2',
+            'cpio-2.10-r1.tbz2', 'file-roller-2.28.1.tbz2', 'cpio-2.9-r2.tbz2',
+            'tar-1.22.tbz2', 'cpio-2.9-r3.tbz2'],
+        'app-editors': ['nano-2.2.0.tbz2', 'nano-2.1.10.tbz2',
+            'nano-2.0.9.tbz2', 'nano-2.2.2.tbz2'],
+        'app-portage': ['layman-1.3.0_rc1-r3.tbz2', 'layman-1.2.6.tbz2',
+            'portage-utils-0.3.1.tbz2', 'layman-1.3.0.tbz2',
+            'layman-1.2.4-r3.tbz2', 'layman-1.2.3.tbz2',
+            'layman-1.3.0_rc1.tbz2'],
+        'sys-apps': ['shadow-4.0.18.2.tbz2', 'shadow-4.1.2.2.tbz2',
+            'openrc-0.6.0-r1.tbz2', 'shadow-4.1.4.2-r1.tbz2',
+            'shadow-4.1.4.2-r2.tbz2']
+        }
+
+    pkg_clean = {
+        'app-arch/p7zip-4.57.tbz2': [],
+        'app-arch/file-roller-2.26.3.tbz2': [],
+        'app-arch/tar-1.20.tbz2': [],
+        'app-arch/p7zip-4.58.tbz2': [],
+        'app-arch/file-roller-2.28.2.tbz2': [],
+        'app-arch/file-roller-2.24.3.tbz2': [],
+        'app-arch/bzip2-1.0.5-r1.tbz2': [],
+        'app-arch/cpio-2.10.tbz2': [],
+        'app-arch/tar-1.21-r1.tbz2': [],
+        'app-arch/cpio-2.9-r2.tbz2': [],
+        'app-arch/cpio-2.9-r3.tbz2': [],
+        'app-editors/nano-2.2.0.tbz2': [],
+        'app-editors/nano-2.1.10.tbz2': [],
+        'app-editors/nano-2.0.9.tbz2': [],
+        'app-portage/layman-1.3.0_rc1-r3.tbz2': [],
+        'app-portage/layman-1.2.6.tbz2': [],
+        'app-portage/layman-1.2.4-r3.tbz2': [],
+        'app-portage/layman-1.2.3.tbz2': [],
+        'app-portage/layman-1.3.0_rc1.tbz2': [],
+        'sys-apps/shadow-4.0.18.2.tbz2': [],
+        'sys-apps/shadow-4.1.2.2.tbz2': [],
+        'sys-apps/shadow-4.1.4.2-r1.tbz2': [],
+        }
+
+    def __init__(self, options):
+        """Initialization
+
+        @param options: dict.
+        """
+        self.options = options
+        self.targets_init = False
+        # create distfiles dir and populate it
+        make_dist(self.options['target_path'], self.distfile_list, self.dist_clean)
+        # add some symlinks to it
+        path = os.path.join(self.options['target_path'], 'distfiles')
+        make_symlinks(path, distfile_symlink,
+            dist_clean['symlink-test-1.2.3.tar.bz2']):
+        # create the packages dir and populate it
+        path = os.path.join(self.options['target_path'], 'packages')
+        make_pkgs(path, self.package_dict, self.pkg_clean):
+        self.targets_init = True
+
+    def get_
diff --git a/pym/gentoolkit/test/eclean/test_clean.py b/pym/gentoolkit/test/eclean/test_clean.py
new file mode 100644 (file)
index 0000000..82a9841
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+#
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+from __future__ import with_statement
+
+__version__= "0.0.1"
+__author__ = "Brian Dolbec"
+__email__ = "brian.dolbec@gmail.com"
+
+from getopt import gnu_getopt, GetoptError
+
+import unittest
+import os
+import sys
+
+import gentoolkit.pprinter as pp
+from test import test_support
+
+from gentoolkit.eclean.clean import CleanUp
+
+
+class Controllers(object):
+       """Contains controller methods for use in testing
+       the clean module methods"""
+       
+       def __init__(self):
+               self.gathered_data = []
+               self.authorize = True
+               self.authorize_list = []
+               self.authorize_index = 0
+
+       def authorize_all_controller(self, size, key, clean_list):
+               """data gatherering controller.
+               
+               @rtype: Boolean
+               @returns: self.authorize which controls the cleaning method
+               """
+               self.gathered_data.append([size, key, clean_list])
+               return self.authorize
+
+       def authorize_list_controller(self, size, key, clean_list):
+               """data gathering and controller which
+               authorizes acoring to a pre-determined list
+               
+               @rtype: Boolean
+               @return self.authorize_list[self.authorize_index]"""
+               self.gathered_data.append([size, key, clean_list])
+               index = self.authorize_index
+               self.authorize_index =+ 1
+               return self.authorize_list[index]
+               
+
+#class TestCleanUp(unittest.TestCase):
+#      """Test module for the various CleanUp class methods
+#      
+#      @param options: dict of module options
+#      @param testdata: dict. of path and test parameters
+#                      as created by the TestDirCreation class"""
+#
+#      def __init__(self, options, testdata):
+#              self.options = options
+#              self.tesdata = testdata
+#              
+#
+#      def test_symlink_clean():
+#              """Tests the symbolic link portion of the distfiles
+#              cleaning"""
+#              pass
+#
+#
+#      def test_dist_clean():
+#              """Test the distfiles cleaning"""
+#              pass
+#
+#
+#      def test_pkg_clean():
+#              """Test the packages cleaning"""
+#              pass
+#
+#
+#      def test_pretend_clean():
+#              """Test the pretend_clean output"""
+#              controlller = Controllers().authorize_all_controller
+#              clean = CleanUp(controller)
+#              clean.pretend_clean(self.dist_clean)
+#              data = controller.gathered_data
+               
+
+
+def useage():
+       """output run options"""
+       print "Useage: test_clean [OPTONS] path=test-dir"
+       print " where test-dir is the location to create and populate"
+       print "the testing distfiles and packages directories."
+       print "All tests in this module test only the clean.py module functions"
+       print
+       print "OPTIONS:"
+       print " -a, --all         run all tests"
+       print " -c, --clean       clean up any previous test dirs & files"
+       print " -D, --distfiles   run the distfiles cleaning test"
+       print " -k, --keep-dirs   keep the test directories and files after the test"
+       print " -p, --pretend     run the test in pretend mode only"
+       print " -P, --packages    run the packages cleaning test"
+       print " -S, --symlinks    run the symlinks test"
+       print " --path            the location to create the temporary distfiles"
+       print "                   and packages directories that will be test cleaned"
+       print " --version         test module version"
+       print
+
+
+def parse_opts():
+       """Parse the options dict
+       
+       @return options: dictionary of module options"""
+       try:
+               opts, args = getopt(sys.argv[1:], 'acDkpPS', ["version",
+                       "help", "path=", "all", "distfiles", "packages",
+                       "pretend", "symlinks", "keep-dirs", "clean"])
+               #print opts
+               #print args
+       except GetoptError, e:
+               print >> sys.stderr, e.msg
+               usage()
+               sys.exit(1)
+
+
+
+def main(cmdline=False):
+       """parse options and run the tests"""
+       
+       if cmdline:
+               options = parse_opts()
+       
+
+if __name__ == "__main__":
+       """actually call main() if launched as a script"""
+       try:
+               main(True)
+       except KeyboardInterrupt:
+               print "Aborted."
+               sys.exit(130)
+       sys.exit(0)
+
+
diff --git a/pym/gentoolkit/test/eclean/test_search.py b/pym/gentoolkit/test/eclean/test_search.py
new file mode 100644 (file)
index 0000000..1970784
--- /dev/null
@@ -0,0 +1,100 @@
+# Copyright(c) 2009, Gentoo Foundation
+# Copyright: 2006-2008 Brian Harring <ferringb@gmail.com>
+# Copyright 2010 Brian Dolbec <brian.dolbec@gmail.com>
+#
+# License: GPL2/BSD
+
+# $Header$
+
+
+from __future__ import print_function
+
+
+import unittest
+from test import test_support
+
+from gentoolkit.eclean.search import *
+
+class Dbapi(object):
+       """Fake portage dbapi class used to return
+       pre-determined test data in place of a live system
+
+       @param cp_all: list of cat/pkg's to use for testing
+                               eg: ['app-portage/gentoolkit', 'app-portage/porthole',...]
+       @param cpv_all: list of cat/pkg-ver's to use for testing.
+       @param props: dictionary of ebuild properties to use for testing.
+                               eg: {'cpv': {"SRC_URI": 'http://...', "RESTRICT": restriction},}
+       @param cp_list: ?????????
+       """
+
+       def __init__(self, cp_all=[], cpv_all=[], props={}, cp_list=[]):
+               self._cp_all = cp_all
+               self._cpv_all = cpv_all
+               self._props = props
+               self._cp_list = cp_list
+
+       def cp_all(self):
+               return self._cp_all[:]
+
+       def cp_list(self, package):
+               #need to determine the data to return
+               # and gather some from a live system to use for testing
+               pass
+
+       def cpv_all(self):
+               return self._cpv_all
+
+       def cpv_exists(self, cpv):
+               return cpv in self._cpv_all
+
+       def aux_get(self, cpv, prop_list):
+               """only need stubs for ["SRC_URI","RESTRICT"]
+               """
+               props = []
+               for prop in prop_list:
+                       props.append(self._props[cpv][prop])
+               return props
+
+
+
+
+"""Tests for eclean's search modules."""
+
+class TestFindDistfiles(unittest.TestCase):
+       uris = [
+               u'/usr/portage/distfiles/xdg-utils-1.0.2.tgz',
+               u'/usr/portage/distfiles/readline60-003',
+               u'/usr/portage/distfiles/bash-completion-1.1.tar.bz2',
+               u'/usr/portage/distfiles/libgweather-2.26.2.1.tar.bz2',
+               u'/usr/portage/distfiles/libwnck-2.26.2.tar.bz2',
+               u'/usr/portage/distfiles/gnome-cups-manager-0.33.tar.bz2',
+               u'/usr/portage/distfiles/audiofile-0.2.6-constantise.patch.bz2',
+               u'/usr/portage/distfiles/vixie-cron-4.1-gentoo-r4.patch.bz2',
+               u'/usr/portage/distfiles/evince-2.26.2.tar.bz2',
+               u'/usr/portage/distfiles/lxml-2.2.2.tgz'
+       ]
+       filenames = [
+               u'audiofile-0.2.6-constantise.patch.bz2',
+               u'bash-completion-1.1.tar.bz2',
+               u'evince-2.26.2.tar.bz2',
+               u'gnome-cups-manager-0.33.tar.bz2',
+               u'libgweather-2.26.2.1.tar.bz2',
+               u'libwnck-2.26.2.tar.bz2',
+               u'lxml-2.2.2.tgz',
+               u'readline60-003',
+               u'vixie-cron-4.1-gentoo-r4.patch.bz2',
+               u'xdg-utils-1.0.2.tgz'
+       ]
+
+       def test_get_filenames_from_uris(self):
+               fns = sorted(get_filenames_from_uris(self.uris))
+               print(fns)
+               for fn, fn2 in zip(self.filenames, fns):
+                       self.failUnlessEqual(fn, fn2)
+
+
+def test_main():
+       test_support.run_unittest(TestFindDistfiles)
+
+if __name__ == '__main__':
+       test_main()
index 50736596e70c2fa0d92a43a38b9740a27f22dcc5..6571f47dda4ed8b15f3a89da093b3c3dcb572987 100644 (file)
@@ -115,27 +115,6 @@ class TestFileOwner(unittest.TestCase):
                self.failUnlessRaises(AttributeError, extend_realpaths, set())
 
 
-class TestGentoolkitHelpers(unittest.TestCase):
-
-       def test_uses_globbing(self):
-               globbing_tests = [
-                       ('sys-apps/portage-2.1.6.13', False),
-                       ('>=sys-apps/portage-2.1.6.13', False),
-                       ('<=sys-apps/portage-2.1.6.13', False),
-                       ('~sys-apps/portage-2.1.6.13', False),
-                       ('=sys-apps/portage-2*', False),
-                       ('sys-*/*-2.1.6.13', True),
-                       ('sys-app?/portage-2.1.6.13', True),
-                       ('sys-apps/[bp]ortage-2.1.6.13', True),
-                       ('sys-apps/[!p]ortage*', True)
-               ]
-
-               for gt in globbing_tests:
-                       self.failUnless(
-                               helpers.uses_globbing(gt[0]) == gt[1]
-                       )
-
-
 def test_main():
        test_support.run_unittest(TestGentoolkitHelpers2)
 
index e054d357a3f6659f44e6e6e0fc0e173f85495ace..541896198720c376e23139ff533ce188135500bd 100644 (file)
@@ -22,7 +22,9 @@ class TestGentoolkitKeyword(unittest.TestCase):
                        # stable vs. unstable
                        ('amd64-linux', '~amd64-linux'),
                        # different OSes
-                       ('~x86-linux', '~x86-solaris')
+                       ('~x86-linux', '~x86-solaris'),
+                       # OS vs. no OS
+                       ('x86', '~amd64-linux')
                ]
                # Check less than
                for vt in version_tests:
@@ -34,6 +36,16 @@ class TestGentoolkitKeyword(unittest.TestCase):
                vt = ('~amd64-linux', '~amd64-linux')
                self.failUnless(compare_strs(vt[0], vt[1]) == 0)
 
+               kwds_presort = [
+                       '~amd64', '~amd64-linux', '~ppc', '~ppc-macos', '~x86',
+                       '~x86-linux', '~x86-macos', '~x86-solaris'
+               ]
+               kwds_postsort = [
+                       '~amd64', '~ppc', '~x86', '~amd64-linux', '~x86-linux',
+                       '~ppc-macos', '~x86-macos', '~x86-solaris'
+               ]
+               self.failUnlessEqual(sorted(kwds_presort, cmp=compare_strs), kwds_postsort)
+
 
 def test_main():
        test_support.run_unittest(TestGentoolkitHelpers2)
diff --git a/pym/gentoolkit/test/test_query.py b/pym/gentoolkit/test/test_query.py
new file mode 100644 (file)
index 0000000..56f26e6
--- /dev/null
@@ -0,0 +1,111 @@
+import unittest
+import warnings
+from tempfile import NamedTemporaryFile, mktemp
+try:
+       from test import test_support
+except ImportError:
+       from test import support as test_support
+
+from portage import os
+
+from gentoolkit import query
+from gentoolkit import errors
+
+
+class TestQuery(unittest.TestCase):
+
+       def setUp(self):
+               pass
+
+       def tearDown(self):
+               pass
+
+       def test_init(self):
+               # valid queries must have at least one ascii letter or '*'
+               invalid_queries = [
+                       '',
+                       '1',
+                       '/',
+                       '-1',
+                       '1/1',
+               ]
+               for q in invalid_queries:
+                       self.failUnlessRaises(errors.GentoolkitInvalidPackage,
+                               query.Query, q
+                       )
+
+               q1 = query.Query('gentoolkit')
+               q1_tests = [
+                       (q1.query, 'gentoolkit'),
+                       (q1.is_regex, False),
+                       (q1.repo_filter, None),
+                       (q1.query_type, "simple")
+               ]
+               for t in q1_tests:
+                       self.failUnlessEqual(t[0], t[1])
+
+               q2 = query.Query('gentoolkit-.*', is_regex=True)
+               q2_tests = [
+                       (q2.query, 'gentoolkit-.*'),
+                       (q2.is_regex, True),
+                       (q2.repo_filter, None),
+                       (q2.query_type, "complex")
+               ]
+               for t in q2_tests:
+                       self.failUnlessEqual(t[0], t[1])
+
+               q3 = query.Query('*::gentoo')
+               q3_tests = [
+                       (q3.query, '*'),
+                       (q3.is_regex, False),
+                       (q3.repo_filter, 'gentoo'),
+                       (q3.query_type, "complex")
+               ]
+               for t in q3_tests:
+                       self.failUnlessEqual(t[0], t[1])
+
+               q4 = query.Query('gcc:4.3')
+               q4_tests = [
+                       (q4.query, 'gcc:4.3'),
+                       (q4.is_regex, False),
+                       (q4.repo_filter, None),
+                       (q4.query_type, "simple")
+               ]
+               for t in q4_tests:
+                       self.failUnlessEqual(t[0], t[1])
+
+               q5 = query.Query('@system')
+               q5_tests = [
+                       (q5.query, '@system'),
+                       (q5.is_regex, False),
+                       (q5.repo_filter, None),
+                       (q5.query_type, "set")
+               ]
+               for t in q5_tests:
+                       self.failUnlessEqual(t[0], t[1])
+
+       def test_uses_globbing(self):
+               globbing_tests = [
+                       ('sys-apps/portage-2.1.6.13', False),
+                       ('>=sys-apps/portage-2.1.6.13', False),
+                       ('<=sys-apps/portage-2.1.6.13', False),
+                       ('~sys-apps/portage-2.1.6.13', False),
+                       ('=sys-apps/portage-2*', False),
+                       ('sys-*/*-2.1.6.13', True),
+                       ('sys-app?/portage-2.1.6.13', True),
+                       ('sys-apps/[bp]ortage-2.1.6.13', True),
+                       ('sys-apps/[!p]ortage*', True)
+               ]
+
+               for gt in globbing_tests:
+                       self.failUnless(
+                               query.Query(gt[0]).uses_globbing() == gt[1]
+                       )
+
+
+def test_main():
+       test_support.run_unittest(TestGentoolkitHelpers2)
+
+
+if __name__ == '__main__':
+       test_main()
index 179c3e4dff007b4ceae02aae61d5d033a74dda83..217d526b234c557b430e54a95e7b87dff8fcf01e 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -9,7 +9,7 @@ import distutils
 from distutils import core, log
 from glob import glob
 
-from portage import os
+import os
 
 sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'pym'))
 from gentoolkit.helpers import walk
@@ -29,6 +29,8 @@ python_scripts = [os.path.join(cwd, path) for path in (
        'bin/eclean',
        'bin/epkginfo',
        'bin/glsa-check',
+       'pym/gentoolkit/eclean/cli.py',
+       'pym/gentoolkit/analyse/__init__.py',
        'pym/gentoolkit/equery/__init__.py'
 )]