Tagging the 0.3.0_rc8 release of gentoolkit. gentoolkit-0.3.0_rc8
authorfuzzyray <fuzzyray@gentoo.org>
Wed, 6 Jan 2010 16:11:52 +0000 (16:11 -0000)
committerfuzzyray <fuzzyray@gentoo.org>
Wed, 6 Jan 2010 16:11:52 +0000 (16:11 -0000)
svn path=/tags/gentoolkit-0.3.0_rc8/; revision=730

220 files changed:
deprecated/change/AUTHORS [new file with mode: 0644]
deprecated/change/ChangeLog [new file with mode: 0644]
deprecated/change/README [new file with mode: 0644]
deprecated/change/change [new file with mode: 0644]
deprecated/change/change.1 [new file with mode: 0644]
deprecated/dep-clean/AUTHORS [new file with mode: 0644]
deprecated/dep-clean/ChangeLog [new file with mode: 0644]
deprecated/dep-clean/README [new file with mode: 0644]
deprecated/dep-clean/dep-clean [new file with mode: 0644]
deprecated/dep-clean/dep-clean.1 [new file with mode: 0644]
deprecated/dev-scripts/README [new file with mode: 0644]
deprecated/dev-scripts/included_headers.sh [new file with mode: 0755]
deprecated/dev-scripts/linking_libs.sh [new file with mode: 0755]
deprecated/distfiles-clean/AUTHORS [new file with mode: 0644]
deprecated/distfiles-clean/ChangeLog [new file with mode: 0644]
deprecated/distfiles-clean/TODO [new file with mode: 0644]
deprecated/distfiles-clean/distfiles-clean [new file with mode: 0644]
deprecated/epkginfo/AUTHORS [new file with mode: 0644]
deprecated/epkginfo/README [new file with mode: 0644]
deprecated/epkginfo/epkginfo [new file with mode: 0755]
deprecated/epkginfo/epkginfo.1 [new file with mode: 0644]
deprecated/epkgmove/AUTHORS [new file with mode: 0644]
deprecated/epkgmove/ChangeLog [new file with mode: 0644]
deprecated/epkgmove/Makefile [new file with mode: 0644]
deprecated/epkgmove/README [new file with mode: 0644]
deprecated/epkgmove/TODO [new file with mode: 0644]
deprecated/epkgmove/epkgmove [new file with mode: 0644]
deprecated/etc-update/AUTHORS [new file with mode: 0644]
deprecated/etc-update/ChangeLog [new file with mode: 0644]
deprecated/etc-update/Makefile [new file with mode: 0644]
deprecated/etc-update/README [new file with mode: 0644]
deprecated/etc-update/etc-update [new file with mode: 0755]
deprecated/etc-update/etc-update.1 [new file with mode: 0644]
deprecated/etcat/AUTHORS [new file with mode: 0644]
deprecated/etcat/ChangeLog [new file with mode: 0644]
deprecated/etcat/Makefile [new file with mode: 0644]
deprecated/etcat/README [new file with mode: 0644]
deprecated/etcat/TODO [new file with mode: 0644]
deprecated/etcat/etcat [new file with mode: 0755]
deprecated/etcat/etcat.1 [new file with mode: 0644]
deprecated/genpkgindex/Makefile [new file with mode: 0644]
deprecated/genpkgindex/genpkgindex [new file with mode: 0644]
deprecated/genpkgindex/genpkgindex.1 [new file with mode: 0644]
deprecated/gensync/AUTHORS [new file with mode: 0644]
deprecated/gensync/ChangeLog [new file with mode: 0644]
deprecated/gensync/Makefile [new file with mode: 0644]
deprecated/gensync/README [new file with mode: 0644]
deprecated/gensync/TODO [new file with mode: 0644]
deprecated/gensync/bmg-gnome-current.syncsource [new file with mode: 0644]
deprecated/gensync/bmg-main.syncsource [new file with mode: 0644]
deprecated/gensync/gensync [new file with mode: 0755]
deprecated/gensync/gensync.1 [new file with mode: 0644]
deprecated/gensync/gensync.conf [new file with mode: 0644]
deprecated/lintool/AUTHORS [new file with mode: 0644]
deprecated/lintool/COPYING [new file with mode: 0644]
deprecated/lintool/ChangeLog [new file with mode: 0644]
deprecated/lintool/NEWS [new file with mode: 0644]
deprecated/lintool/README [new file with mode: 0644]
deprecated/lintool/lintool.1 [new file with mode: 0644]
deprecated/lintool/lintool.py [new file with mode: 0755]
deprecated/lintool/lintool/__init__.py [new file with mode: 0644]
deprecated/lintool/lintool/changelog.py [new file with mode: 0644]
deprecated/lintool/lintool/digest.py [new file with mode: 0644]
deprecated/lintool/lintool/ebuild.py [new file with mode: 0644]
deprecated/lintool/lintool/test.py [new file with mode: 0644]
deprecated/moo/AUTHORS [new file with mode: 0644]
deprecated/moo/README [new file with mode: 0644]
deprecated/moo/TODO [new file with mode: 0644]
deprecated/moo/moo [new file with mode: 0755]
deprecated/moo/moo.1 [new file with mode: 0644]
deprecated/old-scripts/Makefile [new file with mode: 0644]
deprecated/old-scripts/dep-clean [new file with mode: 0644]
deprecated/old-scripts/dep-clean.1 [new file with mode: 0644]
deprecated/old-scripts/ewhich [new file with mode: 0755]
deprecated/old-scripts/ewhich.1 [new file with mode: 0644]
deprecated/old-scripts/mkebuild [new file with mode: 0644]
deprecated/old-scripts/mkebuild.1 [new file with mode: 0644]
deprecated/old-scripts/pkg-clean [new file with mode: 0644]
deprecated/old-scripts/pkg-clean.1 [new file with mode: 0644]
deprecated/old-scripts/pkg-size [new file with mode: 0644]
deprecated/old-scripts/pkg-size.1 [new file with mode: 0644]
deprecated/pkg-clean/AUTHORS [new file with mode: 0644]
deprecated/pkg-clean/ChangeLog [new file with mode: 0644]
deprecated/pkg-clean/README [new file with mode: 0644]
deprecated/pkg-clean/pkg-clean [new file with mode: 0644]
deprecated/pkg-clean/pkg-clean.1 [new file with mode: 0644]
deprecated/pkg-size/pkg-size [new file with mode: 0644]
deprecated/qpkg/AUTHORS [new file with mode: 0644]
deprecated/qpkg/ChangeLog [new file with mode: 0644]
deprecated/qpkg/Makefile [new file with mode: 0644]
deprecated/qpkg/README [new file with mode: 0644]
deprecated/qpkg/TODO [new file with mode: 0644]
deprecated/qpkg/qpkg [new file with mode: 0644]
deprecated/qpkg/qpkg.1 [new file with mode: 0644]
deprecated/qpkg/qpkg.sh [new file with mode: 0644]
deprecated/useflag/AUTHORS [new file with mode: 0644]
deprecated/useflag/ChangeLog [new file with mode: 0644]
deprecated/useflag/README [new file with mode: 0644]
deprecated/useflag/useflag [new file with mode: 0644]
deprecated/useflag/useflag.1 [new file with mode: 0644]
gentoolkit-dev/AUTHORS [new file with mode: 0644]
gentoolkit-dev/COPYING [new file with mode: 0644]
gentoolkit-dev/ChangeLog [new file with mode: 0644]
gentoolkit-dev/Makefile [new file with mode: 0644]
gentoolkit-dev/Makefile.skel [new file with mode: 0644]
gentoolkit-dev/NEWS [new file with mode: 0644]
gentoolkit-dev/README [new file with mode: 0644]
gentoolkit-dev/README.Developer [new file with mode: 0644]
gentoolkit-dev/TODO [new file with mode: 0644]
gentoolkit-dev/makedefs.mak [new file with mode: 0644]
gentoolkit-dev/src/ebump/AUTHORS [new file with mode: 0644]
gentoolkit-dev/src/ebump/ChangeLog [new file with mode: 0644]
gentoolkit-dev/src/ebump/Makefile [new file with mode: 0644]
gentoolkit-dev/src/ebump/README [new file with mode: 0644]
gentoolkit-dev/src/ebump/TODO [new file with mode: 0644]
gentoolkit-dev/src/ebump/ebump [new file with mode: 0755]
gentoolkit-dev/src/ebump/ebump.1 [new file with mode: 0644]
gentoolkit-dev/src/echangelog/AUTHORS [new file with mode: 0644]
gentoolkit-dev/src/echangelog/ChangeLog [new file with mode: 0644]
gentoolkit-dev/src/echangelog/Makefile [new file with mode: 0644]
gentoolkit-dev/src/echangelog/README [new file with mode: 0644]
gentoolkit-dev/src/echangelog/TODO [new file with mode: 0644]
gentoolkit-dev/src/echangelog/echangelog [new file with mode: 0755]
gentoolkit-dev/src/echangelog/echangelog.1 [new file with mode: 0644]
gentoolkit-dev/src/echangelog/test/TEST.pm [new file with mode: 0644]
gentoolkit-dev/src/echangelog/test/templates/test.patch [new file with mode: 0644]
gentoolkit-dev/src/echangelog/test/templates/vcstest-0.0.1.ebuild [new file with mode: 0644]
gentoolkit-dev/src/echangelog/test/test.sh [new file with mode: 0755]
gentoolkit-dev/src/ego/AUTHOR [new file with mode: 0644]
gentoolkit-dev/src/ego/AUTHORS [new file with mode: 0644]
gentoolkit-dev/src/ego/ChangeLog [new file with mode: 0644]
gentoolkit-dev/src/ego/Makefile [new file with mode: 0644]
gentoolkit-dev/src/ego/README [new file with mode: 0644]
gentoolkit-dev/src/ego/TODO [new file with mode: 0644]
gentoolkit-dev/src/ego/ego [new file with mode: 0644]
gentoolkit-dev/src/ekeyword/AUTHORS [new file with mode: 0644]
gentoolkit-dev/src/ekeyword/ChangeLog [new file with mode: 0644]
gentoolkit-dev/src/ekeyword/Makefile [new file with mode: 0644]
gentoolkit-dev/src/ekeyword/README [new file with mode: 0644]
gentoolkit-dev/src/ekeyword/TODO [new file with mode: 0644]
gentoolkit-dev/src/ekeyword/ekeyword [new file with mode: 0755]
gentoolkit-dev/src/ekeyword/ekeyword.pod [new file with mode: 0644]
gentoolkit-dev/src/ekeyword2/ekeyword2 [new file with mode: 0755]
gentoolkit-dev/src/eshowkw/Makefile [new file with mode: 0644]
gentoolkit-dev/src/eshowkw/eshowkw [new file with mode: 0644]
gentoolkit-dev/src/eshowkw/eshowkw.1 [new file with mode: 0644]
gentoolkit-dev/src/eviewcvs/AUTHORS [new file with mode: 0644]
gentoolkit-dev/src/eviewcvs/Makefile [new file with mode: 0644]
gentoolkit-dev/src/eviewcvs/README [new file with mode: 0644]
gentoolkit-dev/src/eviewcvs/eviewcvs [new file with mode: 0755]
gentoolkit-dev/src/eviewcvs/eviewcvs.pod [new file with mode: 0644]
gentoolkit-dev/src/imlate/Makefile [new file with mode: 0644]
gentoolkit-dev/src/imlate/imlate [new file with mode: 0755]
gentoolkit-dev/src/imlate/imlate.1 [new file with mode: 0644]
gentoolkit/AUTHORS [new file with mode: 0644]
gentoolkit/COPYING [new file with mode: 0644]
gentoolkit/CREDITS [new file with mode: 0644]
gentoolkit/ChangeLog [new file with mode: 0644]
gentoolkit/DEVELOPING [new file with mode: 0644]
gentoolkit/MANIFEST.in [new file with mode: 0644]
gentoolkit/NEWS [new file with mode: 0644]
gentoolkit/README [new file with mode: 0644]
gentoolkit/README.dev [new file with mode: 0644]
gentoolkit/THANKS [new file with mode: 0644]
gentoolkit/TODO [new file with mode: 0644]
gentoolkit/bin/eclean [new file with mode: 0755]
gentoolkit/bin/epkginfo [new file with mode: 0755]
gentoolkit/bin/equery [new file with mode: 0755]
gentoolkit/bin/eread [new file with mode: 0755]
gentoolkit/bin/euse [new file with mode: 0755]
gentoolkit/bin/glsa-check [new file with mode: 0755]
gentoolkit/bin/revdep-rebuild [new file with mode: 0755]
gentoolkit/data/99gentoolkit-env [new file with mode: 0644]
gentoolkit/data/eclean/distfiles.exclude [new file with mode: 0644]
gentoolkit/data/eclean/packages.exclude [new file with mode: 0644]
gentoolkit/data/revdep-rebuild/99revdep-rebuild [new file with mode: 0644]
gentoolkit/man/eclean.1 [new file with mode: 0644]
gentoolkit/man/epkginfo.1 [new file with mode: 0644]
gentoolkit/man/equery.1 [new file with mode: 0644]
gentoolkit/man/eread.1 [new file with mode: 0644]
gentoolkit/man/euse.1 [new file with mode: 0644]
gentoolkit/man/glsa-check.1 [new file with mode: 0644]
gentoolkit/man/revdep-rebuild.1 [new file with mode: 0644]
gentoolkit/pylintrc [new file with mode: 0644]
gentoolkit/pym/gentoolkit/__init__.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/atom.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/cpv.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/dbapi.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/dependencies.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/deprecated/helpers.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/__init__.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/belongs.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/changes.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/check.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/depends.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/depgraph.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/files.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/hasuse.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/list_.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/meta.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/size.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/uses.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/equery/which.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/errors.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/glsa/__init__.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/helpers.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/metadata.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/package.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/pprinter.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/query.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/test/__init__.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/test/equery/__init__.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/test/equery/test_init.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/test/test_atom.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/test/test_cpv.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/test/test_helpers.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/test/test_syntax.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/textwrap_.py [new file with mode: 0644]
gentoolkit/pym/gentoolkit/versionmatch.py [new file with mode: 0644]
gentoolkit/setup.py [new file with mode: 0755]

diff --git a/deprecated/change/AUTHORS b/deprecated/change/AUTHORS
new file mode 100644 (file)
index 0000000..4b3873a
--- /dev/null
@@ -0,0 +1,5 @@
+Dan Armak <danarmak@gentoo.org>
+ * Basic idea
+ * Initial version
+Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Gentoolkit-specific changes
diff --git a/deprecated/change/ChangeLog b/deprecated/change/ChangeLog
new file mode 100644 (file)
index 0000000..bd7d5dd
--- /dev/null
@@ -0,0 +1,7 @@
+2002-08-11 Dan Armak <danarmak@gentoo.org>:
+    * Fix two bugs which are long to describe, so I won't do so here.
+      They caused malformed or incomplete changelog files to be created.
+
+2002-08-09 Karl Trygve Kalleberg <karltk@gentoo.org>:
+       * Reformatted usage to work with 80 columns
+       * Now loads ~/.gentoo/gentool-env instead of ~/.change
diff --git a/deprecated/change/README b/deprecated/change/README
new file mode 100644 (file)
index 0000000..bda1842
--- /dev/null
@@ -0,0 +1,20 @@
+Package : change
+Version : 0.2.4
+Author  : See AUTHORS
+
+MOTIVATION
+
+Maintaing Gentoo's ChangeLog files in the Portage Tree is a tedious affair.
+Many of the details are well-defined enough for a tool to do. change is this
+tool.
+
+MECHANICS
+
+change can create a ChangeLog, add entries to the ChangeLog file, scan for
+updated files.
+
+
+IMPROVEMENTS
+
+For improvements, send a mail to karltk@gentoo.org or make out a bug at 
+bugs.gentoo.org and assign it to me.
diff --git a/deprecated/change/change b/deprecated/change/change
new file mode 100644 (file)
index 0000000..094573b
--- /dev/null
@@ -0,0 +1,343 @@
+#! /bin/bash
+
+# Copyright 1999-2002 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+# Author: Dan Armak <danarmak@gentoo.org>
+# $Header: /space/gentoo/cvsroot/gentoolkit/src/change/change,v 1.2 2002/08/11 13:32:12 karltk Exp $
+
+eval `grep PORTDIR= /etc/make.globals`
+eval `grep PORTDIR= /etc/make.conf`
+[ -z "$PORTDIR" ] && PORTDIR="/usr/portage"
+
+# register temp files (we delete them in the end)
+TMPMESSAGE=`tempfile -p change` || cleanup 1
+TMPHEADER=`tempfile -p change` || cleanup 1
+TMPENTRY=`tempfile -p change` || cleanup 1
+TMPOLDLOG=`tempfile -p change` || cleanup 1
+TMPCHANGELOG=`tempfile -p change` || cleanup 1
+
+# get user info from config file - $AUTHORNAME and $AUTHOREMAIL
+init() {
+    . ~/.gentoo/gentool-env || return 1
+}
+
+print_about() {
+
+    echo "change v 0.2.4 - A Gentoo ChangeLog editor."
+    echo "Author Dan Armak <danarmak@gentoo.org>"
+}
+
+print_usage() {
+
+    echo "Usage:
+change <package list> [-shv] [-m|--message msg] [-f|--message-file file] 
+                             [-a|--authorname name] [-l|--authormail mail] 
+                             [-n|--new-version ver] [-o|--output dest]
+
+<package list>: List of packages whose changelogs are to be  edited. All 
+changelogs edited in one run will be added the same log message.
+
+Acceptable formats:         Example:
+category/package            kde-base/kdebase
+path to package dir         kdebase || ../../kdebase
+path to changelog file      portage/kde-base/kdebase/ChangeLog
+
+Note that you must use -g for changelog files outside $PORTDIR.
+
+-m, --message \"msg\"        Use log message \"msg\", do not open editor.
+-f, --message-file <file>  Use contents of <file> as log message, do not open
+                           editor.
+-a, --authorname \"name\"    Use \"name\" (e.g. Dan Armak) in log.
+-l, --authormail \"email\"   Use \"email\" (e.g. danarmak@gentoo.org) in log.
+-n, --new-version \"ver\"    Add a line about a new version number \"ver\" to 
+                           the log.
+-g, --generate             Create a new changelog file if one does not exist.
+                           This option must come before the list of affected 
+                           changelog files. Incidentally, this option also 
+                           enables you to work with a changelog file outside 
+                           $PORTDIR. 
+                           You must use it every time you edit such a file.
+                           However, change won't be able to figure out the
+                           category and package names of your changelog file
+                           and those parts will be missing. (FIXME!)
+-o, --output \"file\"        Save new changelog in file \"file\".
+                           Default is the the same file we're changing (i.e. 
+                           no backup).
+-s, --stdout               Print new changelog to stdout (disables saving to 
+                           file). This suppresses the usual info messages.
+-c, --changed-files        List of changed files (goes into entry header). 
+                           Default is to simply say \"ChangeLog :\". Multiple 
+                           -c options can be given.
+-h, --help                 Print this usage information.
+-v, --version              Print a short about line and the version number and 
+                           exit.
+    
+See also the mandatory config file ~/.gentoo/gentool-env (the gentool-env man
+page contains a template).
+"
+
+}
+
+# parse command line parameters
+# this function should be called before all others (e.g. before init())
+# or else it might stomp on some settings
+parse_params() {
+
+    # at least one parameter required - changelog to process
+    if [ -z "$1" ]; then
+       echo "At least one parameter is required."
+       print_about
+       print_usage
+       cleanup 1
+    fi
+
+    while [ -n "$1" ]; do
+
+       # note: with parameters that come in two pieces (i.e. -m foo)
+       # we identify the first one, grab the second one from $2 and 
+       # shift an extra time
+       case "$1" in
+
+           # optional log message, if defined then we won't launch $EDITOR
+           # comes in explicit string and file reference variations
+           -m | --message)
+               MESSAGE="$2"
+               shift
+               ;;
+           -f | --message-file)
+               cp $2 $TMPMESSAGE
+               shift
+               ;;
+           
+           # general settings (usually set in .change)
+           -a | --authorname)
+               AUTHORNAME="$2"
+               shift
+               ;;
+           -l | --authormail)
+               AUTHOREMAIL="$2"
+               shift
+               ;;
+               
+           # add a line about a new version (starting with *) to the changelog
+           # to add the line but no changelog info, call with -n -m ""
+           -n | --new-version)
+               NEWVERSION="$2"
+               shift
+               ;;
+               
+           # create a new changelog file
+           -g | --generate)
+               GENERATE=true
+               ;;
+               
+           # output redirection. default (if $OUTPUT isn't set) is to change the
+           # specified changelog file.
+           # illegal if more than one changelog file/package is specified.
+           -o | --output)
+               OUTPUT="$2"
+               shift
+               ;;
+           # redirect output to stdout - can be combined with -o
+           -s | --stdout)
+               STDOUT="true"
+               OUTPUT="/dev/null"
+               ;;
+           
+           # list of files changed (second part inclosed in quotes!)
+           -c | --changed-files)
+               CHANGED="$CHANGED $2"
+               shift
+               ;;
+           
+           # request for version/usage information etc
+           -h | --help)
+               print_about
+               print_usage
+               cleanup 0
+               ;;
+           -v | --version)
+               print_about
+               cleanup 0
+               ;;
+           
+           # everything else we couldn't identify. most of it is packages/files to work on.
+           *)
+               for x in "$MYPORTDIR/$1/ChangeLog" "$PORTDIR/$1/ChangeLog" "$PWD/$1/ChangeLog" "$PWD/$1"; do
+                   if [ -f "$x" ]; then
+                       FILES="$FILES $x"
+                       shift # because by calling continue we skip the shift at the end of the case block
+                       continue 2 # next while iteration
+                   fi
+               done
+               # if we haveb't detected a changelog file, maybe we need to create one
+               if [ -n "$GENERATE" ]; then
+                   for x in "$PWD/$1" "$1" "$MYPORTDIR/$1" "$PORTDIR/$1"; do
+                       if [ -d "$x" ]; then
+                           touch $x/ChangeLog
+                           FILES="$FILES $x/ChangeLog"
+                           shift # because by calling continue we skip the shift at the end of the case block
+                           continue 2 # next while iteration
+                       fi
+                   done
+               fi
+               
+               echo "!!! Error: unrecognized option: $1"
+               echo
+               print_usage
+               cleanup 1
+           
+               ;;
+       
+       esac
+       
+       shift
+    done
+    
+    if [ -z "$FILES" ]; then
+       echo "No changelog path or package name passed, mandatory parameter missing."
+       echo
+       print_usage
+       cleanup 1
+    fi
+
+}
+
+# get the log message
+get_msg() {
+
+    if [ -n "`cat $TMPMESSAGE`" ]; then
+       echo "Using message-on-file."
+    elif [ -n "$MESSAGE" ]; then
+       echo "$MESSAGE" > $TMPMESSAGE
+    else # [ -z "$MESSAGE" ]
+    
+       echo > $TMPMESSAGE
+       echo "Please enter changelog. You can leave this line, it will be automatically removed." >> $TMPMESSAGE
+       $EDITOR $TMPMESSAGE
+       cp $TMPMESSAGE ${TMPMESSAGE}2
+       sed -e '/Please enter changelog. You can leave this line, it will be automatically removed./ D' \
+           ${TMPMESSAGE}2 > $TMPMESSAGE
+       rm ${TMPMESSAGE}2
+    
+    fi
+
+    # break up into 80-character columns (actually 78 chars because we'll
+    # add two spaces to every line)
+    cp $TMPMESSAGE ${TMPMESSAGE}2
+    fmt -s -w 78 ${TMPMESSAGE}2 > $TMPMESSAGE
+    rm ${TMPMESSAGE}2
+
+    # add two spaces to the beginning of every line of the message.
+    # do this separately from the sed in the else section above
+    # because it should be executed for the if and elif sections too.
+    cp $TMPMESSAGE ${TMPMESSAGE}2
+    sed -e 's:^:  :g' ${TMPMESSAGE}2 > $TMPMESSAGE
+    rm ${TMPMESSAGE}2
+    
+}
+
+# get list of files and wrap it in the following manner:
+# 1 item on the first list and upto 80 chars on every other.
+# also adds 2 spaces to the beginning of every line but the first.
+wrap_list() {
+
+    echo -n $1
+    shift
+    
+    while [ -n "$1" ]; do
+       if [ -n "$LIST" ]; then
+           LIST="$LIST, $1"
+       else
+           echo ,
+           LIST="$1"
+       fi
+       shift
+    done
+    LIST="$LIST :"
+    
+    echo $LIST | fmt -s -w 78 | sed -e 's:^:  :g' -
+
+}
+
+# do the actual work on te changelog file passed as $1
+process() {
+    # figure out category and package names
+    name=${1//${PORTDIR}}
+    name=${name//${MYPORTDIR}}
+    name=${name//\/ChangeLog}
+    
+    OLDIFS="$IFS"
+    IFS="/"
+    for x in $name; do
+       if [ -z "$CATEGORY" ]; then
+           CATEGORY="$x"
+       else
+           PACKAGE="$x"
+       fi
+    done
+    IFS="$OLDIFS"
+
+    # create header
+    echo \
+"# ChangeLog for $CATEGORY/$PACKAGE
+# Copyright 2002 Gentoo Technologies, Inc.; Distributed under the GPL v2
+# \$Header: \$
+" > $TMPHEADER
+
+    # create entry line
+    if [ -n "$NEWVERSION" ]; then
+       echo "*$PACKAGE-$NEWVERSION (`date '+%d %b %Y'`)" > $TMPENTRY
+       echo >> $TMPENTRY
+    fi
+    
+    echo -n "  `date "+%d %b %Y"`; ${AUTHORNAME} <${AUTHOREMAIL}> " >> $TMPENTRY
+    [ -z "$CHANGED" ] && CHANGED="ChangeLog "
+    wrap_list $CHANGED >> $TMPENTRY
+    
+    echo >> $TMPENTRY
+
+    # get the original changelog, minus the old header
+    sed -e '/^# ChangeLog for/ D
+           /^# Copyright 2002 Gentoo Technologies/ D
+           /^# \$Header:/ D' $1 > $TMPOLDLOG
+
+    # join everything together
+    cat $TMPHEADER $TMPENTRY $TMPMESSAGE $TMPOLDLOG > $TMPCHANGELOG
+    
+    # various output options
+    if [ -n "$OUTPUT" ]; then
+       cp $TMPCHANGELOG $OUTPUT
+       [ -z "$STDOUT" ] && echo "New changelog saved in $OUTPUT."
+    else
+       cp $TMPCHANGELOG $1
+       [ -z "$STDOUT" ] && echo "Original changelog $1 replaced."
+    fi
+
+    if [ -n "$STDOUT" ]; then
+       cat $TMPCHANGELOG
+    fi
+
+}
+
+# pass exit code to this function
+cleanup() {
+
+    rm -f $TMPMESSAGE $TMPHEADER $TMPENTRY $TMPCHANGELOG $TMPOLDLOG
+    
+    exit $1
+    
+}
+
+parse_params "${@}"
+
+init
+
+get_msg
+
+for x in $FILES; do
+    process $x
+done
+
+cleanup 0
+
diff --git a/deprecated/change/change.1 b/deprecated/change/change.1
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/dep-clean/AUTHORS b/deprecated/dep-clean/AUTHORS
new file mode 100644 (file)
index 0000000..2fa030f
--- /dev/null
@@ -0,0 +1,9 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Authors:
+Karl Trygve Kalleberg <karltk@gentoo.org> (dep-clean, man page)
+Jerry Haltom <ssrit@larvalstage.net> (dep-clean)
+Brandon Low <lostlogic@gentoo.org> (dep-clean)
+Paul Belt <gaarde@users.sourceforge.net> (man page)
+Brandon Low <lostlogic@gentoo.org> (dep-clean)
diff --git a/deprecated/dep-clean/ChangeLog b/deprecated/dep-clean/ChangeLog
new file mode 100644 (file)
index 0000000..dc6980e
--- /dev/null
@@ -0,0 +1,13 @@
+  04 Oct 2003: Karl Trygve Kalleberg <karltk@gentoo.org> dep-clean, dep-clean.1:
+  * Rewrote to Python
+  * Uses gentoolkit
+  * Changed the switches to be proper toggles
+  
+  25 Feb 2003; Brandon Low <lostlogic@gentoo.org> dep-clean, dep-clean.1:
+  * Update to work with current everything
+  * Add -q and change the default behaviour and the verbose behavior
+  * Make a lot faster by rewriting most everything
+  * Make script much more readable
+  * Make pay attention to PORTDIR_OVERLAY
+  * Bring back from the dead as it give more info
+    than the depclean action in portage.
diff --git a/deprecated/dep-clean/README b/deprecated/dep-clean/README
new file mode 100644 (file)
index 0000000..6521aef
--- /dev/null
@@ -0,0 +1,4 @@
+See man dep-clean or just run dep-clean --help.
+
+QuickStart:
+dep-clean displays missing, extra, and removed packages on your system.
diff --git a/deprecated/dep-clean/dep-clean b/deprecated/dep-clean/dep-clean
new file mode 100644 (file)
index 0000000..2f2bde0
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/python
+#
+# Terminology:
+#
+# portdir = /usr/portage + /usr/local/portage
+# vardir = /var/db/pkg
+
+import sys
+import gentoolkit
+try:
+       from portage.output import *
+except ImportError:
+       from output import *
+
+__author__      = "Karl Trygve Kalleberg, Brandon Low, Jerry Haltom"
+__email__       = "karltk@gentoo.org, lostlogic@gentoo.org, ssrit@larvalstage"
+__version__     = "0.2.0"
+__productname__ = "dep-clean"
+__description__ = "Portage auxiliary dependency checker"
+
+class Config:
+    pass
+
+def defaultConfig():
+    Config.displayUnneeded = 1
+    Config.displayNeeded = 1
+    Config.displayRemoved = 1
+    Config.color = -1
+    Config.verbosity = 2
+    Config.prefixes = { "R" : "",
+                        "U" : "",
+                        "N" : "" }
+def asCPVs(pkgs):
+    return map(lambda x: x.get_cpv(), pkgs)
+
+def asCPs(pkgs):
+    return map(lambda x: x.get_cp(), pkgs)
+
+def toCP(cpvs):
+    def _(x):
+        (c,p,v,r) = gentoolkit.split_package_name(x)
+        return c + "/" + p
+    return map(_, cpvs)
+          
+def checkDeps():
+    if Config.verbosity > 1:
+        print "Scanning packages, please be patient..."
+    
+    unmerged = asCPVs(gentoolkit.find_all_uninstalled_packages())
+    unmerged_cp = toCP(unmerged)
+
+    merged = asCPVs(gentoolkit.find_all_installed_packages())
+    merged_cp = toCP(merged)
+                    
+    (system, unres_system) = gentoolkit.find_system_packages()
+    system = asCPVs(system)
+    
+    (world, unres_world) = gentoolkit.find_world_packages()
+    world = asCPVs(world)
+
+    desired = system + world
+    
+    unneeded = filter(lambda x: x not in desired, merged)
+    needed = filter(lambda x: x not in merged, desired)
+    old = filter(lambda x: x not in unmerged_cp, merged_cp)
+
+    if len(needed):
+        print "Packages required, but not by world and system:"
+        for x in needed: print "  " + x
+        raise "Internal error, please report."
+    
+    if len(unres_system) and Config.displayNeeded:
+        if Config.verbosity > 0:
+            print white("Packages in system but not installed:")
+        for x in unres_system:
+            print "  " + Config.prefixes["N"] + red(x)
+
+    if len(unres_world) and Config.displayNeeded:
+        if Config.verbosity > 0:
+            print white("Packages in world but not installed:")
+        for x in unres_world:
+            print "  " + Config.prefixes["N"] + red(x)
+
+    if len(old) and Config.displayRemoved:
+        if Config.verbosity > 0:
+            print white("Packages installed, but no longer available:")
+        for x in old:
+            print "  " + Config.prefixes["R"] + yellow(x)
+
+    if len(unneeded) and Config.displayUnneeded:
+        if Config.verbosity > 0:
+            print white("Packages installed, but not required by system or world:")
+        for x in unneeded:
+            print "  " + Config.prefixes["U"] + green(x)
+    
+def main():
+
+    defaultConfig()
+
+    for x in sys.argv:
+        if 0:
+            pass
+        elif x in ["-h","--help"]:
+            printUsage()
+            sys.exit(0)
+        elif x in ["-V","--version"]:
+            printVersion()
+            sys.exit(0)
+            
+        elif x in ["-n","--needed","--needed=yes"]:
+            Config.displayNeeded = 1
+        elif x in ["-N","--needed=no"]:
+            Config.displayNeeded = 0
+            
+        elif x in ["-u","--unneeded","--unneeded=yes"]:
+            Config.displayUnneeded = 1
+        elif x in ["-U","--unneeded=no"]:
+            Config.displayUnneeded = 0
+            
+        elif x in ["-r","--removed","--removed=yes"]:
+            Config.displayRemoved = 1
+        elif x in ["-R","--removed","--removed=no"]:
+            Config.displayRemoved = 0
+        elif x in ["-c","--color=yes"]:
+            Config.color = 1
+        elif x in ["-C","--color=no"]:
+            Config.color = 0
+            
+        elif x in ["-v", "--verbose"]:
+            Config.verbosity += 1
+        elif x in ["-q", "--quiet"]:
+            Config.verbosity = 0
+
+    # Set up colour output correctly
+    if (Config.color == -1 and \
+        ((not sys.stdout.isatty()) or \
+         (gentoolkit.settings["NOCOLOR"] in ["yes","true"]))) \
+         or \
+         Config.color == 0:
+            nocolor()
+            Config.prefixes = { "R": "R ", "N": "N ", "U": "U " }
+            
+    checkDeps()
+
+def printVersion():
+    print __productname__ + "(" + __version__ + ") - " + \
+          __description__
+    print "Authors: " + __author__
+    
+def printUsage():
+    print white("Usage: ") + turquoise(__productname__) + \
+          " [" + turquoise("options") + "]"
+    print "Where " + turquoise("options") + " is one of:"
+    print white("Display:")
+    print "  -N,--needed       needed packages that are not installed."
+    print "  -R,--removed      installed packages not in portage."
+    print "  -U,--unneeded     potentially unneeded packages that are installed."
+    print white("Other:")
+    print "  -C,--nocolor      output without color. Categories will be denoted by P,N,U."
+    print "  -h,--help         print this help"
+    print "  -v,--version      print version information"
+
+if __name__ == "__main__":
+    main()
diff --git a/deprecated/dep-clean/dep-clean.1 b/deprecated/dep-clean/dep-clean.1
new file mode 100644 (file)
index 0000000..9e42019
--- /dev/null
@@ -0,0 +1,194 @@
+.\" Automatically generated by Pod::Man version 1.15
+.\" Thu Jul 18 15:59:55 2002
+.\"
+.\" Standard preamble:
+.\" ======================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  | will give a
+.\" real vertical bar.  \*(C+ will give a nicer C++.  Capital omega is used
+.\" to do unbreakable dashes and therefore won't be available.  \*(C` and
+.\" \*(C' expand to `' in nroff, nothing in troff, for use with C<>
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr
+.\" for titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and
+.\" index entries marked with X<> in POD.  Of course, you'll have to process
+.\" the output yourself in some meaningful fashion.
+.if \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.\"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it
+.\" makes way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.bd B 3
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ======================================================================
+.\"
+.IX Title "DEP-CLEAN 1"
+.TH DEP-CLEAN 1 "Copyright 2002 Gentoo Technologies, Inc." "2002-07-18" "GenToolKit's Dependency Checker!"
+.UC
+.SH "NAME"
+dep-clean \- Shows unrequired packages and missing dependencies.
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+.Vb 1
+\&  dep-clean [-RUNICv]
+.Ve
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+dep-clean displays extraneous, missing or extra packages.  Extra packages are those in which are not a part of the portage tree (/usr/portage).  It does \s-1NOT\s0 modify the system in any way.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.Ip "\-n, \-\-needed" 4
+.Ip "\-N, \-\-needed=no" 4
+.IX Item "-n, --needed"
+Toggle display of needed packages that are not installed. (red) (default=yes)
+.Ip "\-r, \-\-removed" 4
+.Ip "\-R, \-\-removed=no" 4
+.IX Item "-R, --removed"
+Toggle display of installed packages not in portage. (yellow) (default=yes)
+.Ip "\-u, \-\-unneeded" 4
+.Ip "\-U, \-\-unneeded=no" 4
+.IX Item "-U, --unneeded"
+Toggle display of unneeded packages that are installed. (green) (default=yes)
+.Ip "\-c, \-\-color" 4
+.Ip "\-C, \-\-color=no" 4
+.IX Item "-c, --color"
+Toggle output of color. Without color, package types will be noted with R, U and N.
+Default is use whatever Portage is set for.
+.Ip "\-v, \-\-verbose" 4
+.IX Item "-v, --verbose"
+Be more verbose.
+.Ip "\-q, \-\-quiet" 4
+.IX Item "-q, --quiet"
+Be quiet (display only packages).
+.SH "NOTES"
+.IX Header "NOTES"
+.Ip "" 4
+If this script is run on a system that is not up-to-date or which hasn't been cleaned (with 'emerge \-c') recently, the output may be deceptive.
+.Ip "" 4
+If the same package name appears in all three categories, then it is definitely time to update that package and then run 'emerge \-c'.
+.Ip "" 4
+The \-U, \-N and \-R options may be combined, default is \-UNR
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+Jerry Haltom <ssrit at larvalstage dot net> (dep-clean)
+.br
+Brandon Low <lostlogic at gentoo dot org> (dep-clean)
+.PP
+Paul Belt <gaarde at users dot sourceforge dot net> (man page)
+.br
+Karl Trygve Kalleberg <karltk at gentoo dot org> (dep-clean, man page)
diff --git a/deprecated/dev-scripts/README b/deprecated/dev-scripts/README
new file mode 100644 (file)
index 0000000..990a2ab
--- /dev/null
@@ -0,0 +1,2 @@
+This directory is intended to be used for small developer oriented scripts used in gentoolkit-dev.
+If a script develops into a full fledged tool, it will be moved into its own subdirectory.
diff --git a/deprecated/dev-scripts/included_headers.sh b/deprecated/dev-scripts/included_headers.sh
new file mode 100755 (executable)
index 0000000..915628b
--- /dev/null
@@ -0,0 +1,159 @@
+#!/bin/bash
+
+# CHANGES
+#
+# 20051211: Add qfile use from portage-utils, prefer over equery. Create new
+#      function track_headers() to handle package manager queries for both
+#      relative and absolute headers. Split relative and absolute queries into two
+#      separate places, since relative aren't quite as reliable. Prefer headers
+#      found in the tarball over those in /usr/include. Also, note which headers
+#      weren't considered in the calculation and the reasons why not.
+
+location=${1}
+
+usage() {
+       echo "${0##*/} [ -d ] source_location"
+       echo "  Returns owners of all include files used. ${0##*/} defaults to"
+       echo "  files in /usr/include, so if a file with the same name within the"
+       echo "  source is the actual one used, false dependencies may be printed."
+       echo
+       echo "  -d"
+       echo "    Show debug output: Print files not found"
+       exit 1
+}
+
+decho() {
+       if [[ -n "${DEBUG}" ]]; then
+               echo "${1}"
+       fi
+}
+
+if [[ $# -le 0 ]] || [[ $# -ge 3 ]]; then
+       usage
+fi
+
+# Handle command-line options
+while getopts d options; do
+       case ${options} in
+               d)      DEBUG=1
+                       ;;
+               *)      usage
+                       ;;
+       esac
+done
+# Reset post-option stuff to positional parameters
+shift $((OPTIND - 1))
+
+get_absolute_includes() {
+       grep '^#[[:space:]]*include' -r ${1} | grep '.*.[ch]' | grep -e '<' -e '>' \
+       | cut -d':' -f2 | cut -d'<' -f2 | cut -d'>' -f1 | grep '.*.[ch]' \
+       | sort | uniq
+}
+
+get_relative_includes() {
+       grep '^#[[:space:]]*include' -r ${1} | grep '.*.[ch]' | grep -e '"' -e '"' \
+       | cut -d':' -f2 | cut -d'"' -f2 | cut -d'"' -f1 | grep '.*.[ch]' \
+       | sort | uniq
+}
+
+track_headers() {
+       if [[ -x $(which qfile 2> /dev/null) ]]; then
+               qfile ${@} | cut -d'(' -f1 | sort | uniq
+       elif [[ -x $(which equery 2> /dev/null) ]]; then
+               equery -q belongs ${@} | cut -d'(' -f1
+       elif [[ -x $(which rpm 2> /dev/null) ]]; then
+               rpm -qf ${@}
+       else
+               echo "Couldn't find package query tool! Printing headerpaths instead."
+               echo
+               for header in ${@}; do
+                       echo ${header}
+               done
+       fi
+}
+
+echo "Analyzing source ... "
+absolute_headers="$(get_absolute_includes ${1})"
+relative_headers="$(get_relative_includes ${1})"
+
+echo "Looking for absolute headers ... "
+echo
+for header in ${absolute_headers}; do
+       absheader="/usr/include/${header}"
+       if [[ -e ${absheader} ]]; then
+               abs_headerpaths="${abs_headerpaths} ${absheader}"
+               echo "  Looking for ${absheader} ... OK"
+       else
+               # Try as a relative header in case people use -I with <>
+               relative_headers="${relative_headers} ${header}"
+               decho "  Looking for ${absheader} ... Not found!"
+       fi
+done
+
+echo
+echo "Looking for relative headers ... "
+echo
+for header in ${relative_headers}; do
+       fullheader=${header}
+       header=${header##*/}
+       # Prefer headers in tarball over /usr/include
+       header_options=$(find ${location} -name ${header} | grep ${fullheader})
+       if [[ -z ${header_options} ]]; then
+               header_options="$(find /usr/include -name ${header} | grep ${fullheader})"
+               header_loc="/usr/include"
+       else
+               decho "  Local header ${header} ... Not considering."
+               local_headers="${local_headers} ${header}"
+               continue
+       fi
+       count="0"
+       for found in ${header_options}; do
+               (( count++ ))
+       done
+       if [[ ${count} -ge 2 ]]; then
+               echo "  Looking for ${header} ... "
+               echo "    More than one option found for ${header} in ${header_loc}."
+               echo "    Not considering ${header}."
+               duplicate_headers="${duplicate_headers} ${header}"
+               continue
+       elif [[ ${count} -le 0 ]]; then
+               decho "  Looking for ${header} ... Not found!"
+               unfound_headers="${unfound_headers} ${header}"
+               continue
+       fi
+       header=${header_options}
+       if [[ -e ${header} ]] && [[ ${header_loc} = /usr/include ]]; then
+               rel_headerpaths="${rel_headerpaths} ${header}"
+               echo "  Looking for ${header} ... OK"
+       else
+               decho "  Looking for ${header} ... Not found!"
+       fi
+done
+
+echo "Tracing headers back to packages ..."
+echo
+echo "Headers ignored because they exist in the tarball:"
+echo
+for header in ${local_headers}; do
+       echo "${header}"
+done
+echo
+echo "Headers ignored because of duplicates in /usr/include:"
+echo
+for header in ${duplicate_headers}; do
+       echo "${header}"
+done
+echo
+echo "Headers ignored because they weren't found:"
+echo
+for header in ${unfound_headers}; do
+       echo "${header}"
+done
+echo
+echo "Absolute headers:"
+echo
+track_headers ${abs_headerpaths}
+echo
+echo "Relative headers:"
+echo
+track_headers ${rel_headerpaths}
diff --git a/deprecated/dev-scripts/linking_libs.sh b/deprecated/dev-scripts/linking_libs.sh
new file mode 100755 (executable)
index 0000000..a249305
--- /dev/null
@@ -0,0 +1,204 @@
+#!/bin/bash
+
+# CHANGES
+#
+# 20051211: Move most of the logic to check for bad links into get_libnames()
+#      seds, so we don't wrongly sed out whole link lines. Seems to catch more
+#      problems, such as ' or ` or -- in a link.
+# 20051210: Prefer qfile from portage-utils over equery if it's available.
+#      Check for ... in "link" lines because configure checks are not links.
+#      Change get_link_generic() to handle whole lines at a time instead of single
+#      words, so get_linklines() works.
+# 20051210: Rework get_libnames() to use a new style of grep, because the old
+#      way was broken on some packages from the \b. Also optimize the "Looking for
+#      libraries" section to only grep the log file once for links and cache it;
+#      also only grep the link lines ones for a given library, then parse the
+#      output for static or shared. Should speed things up considerably for large
+#      packages. I get 5 seconds in Analyzing log and 15 in Looking for libs on an
+#      xorg-x11-6.8.99.15 log on second run.
+#      Create get_link_generic() that both sections call with different options.
+
+usage() {
+       echo "${0##*/} compilation_log"
+       echo "  Checks for -lfoo link commands and finds the library owners."
+       exit 1
+}
+
+if [[ $# -lt 1 || $1 == -h || $1 == --help ]]; then
+       usage
+fi
+
+
+# Finds all lines in a file that involve linking
+# get_link_generic(char *grep_opts, char *filename)
+get_link_generic() {
+       egrep ${1} '\-l\w[^[:space:]]*' ${2} \
+               | while read linker; do
+                       # -linker is passed through to ld and doesn't mean the inker lib.
+                       # The new -w in grep makes sure they're separate "words", but its
+                       # "word" characters only include alnum and underscore, so -- gets
+                       # through.
+                       # Some configure lines with ... match, so we drop them
+                       # Some of the configure options match, so we get rid of = for that.
+                       if \
+                               [[ "${linker}" != *...* ]] \
+                               && [[ "${linker}" != -lib ]] \
+                               && [[ "${linker}" != -libs ]]; then
+                               echo ${linker}
+                       fi
+       done
+}
+
+# Note the lack of -o, as compared to get_libnames() egrep
+get_linklines() {
+       get_link_generic "-w" ${1} | sort | uniq
+}
+
+get_libnames() {
+    for x; do
+       get_link_generic "-o -w" ${x} \
+               | sed \
+                       -e "/^-link/d" \
+                       -e "/^-lib/d" \
+                       -e "s:^-l::g" \
+                       -e "/=/d" \
+                       -e "/'/d" \
+                       -e "/^-/d" \
+                       -e "s:\.*$::g" \
+                       -e "s:|::g" \
+                       -e "s:\"::g" \
+                       -e "/^-link/d" \
+                       -e "/^-lib/d"
+    done | sort | uniq
+}
+
+get_libdirs() {
+       cat /etc/ld.so.conf | sed -e "/^#/d"
+}
+
+check_exists() {
+       if [[ -n ${1// } ]]; then
+               return 0
+       fi
+
+       return 1
+}
+
+trace_to_packages() {
+       local paths=$1
+
+       check_exists "${paths}"
+       local ret=$?
+       if [[ $ret -ne 0 ]]; then
+               return 1
+       fi
+
+       if [[ -x $(which qfile 2> /dev/null) ]]; then
+               qfile -q ${paths} | sort | uniq
+       elif [[ -x $(which equery 2> /dev/null) ]]; then
+               equery -q belongs ${paths} | cut -d'(' -f1
+       elif [[ -x $(which rpm 2> /dev/null) ]]; then
+               rpm -qf ${paths}
+       else
+               echo "Couldn't find package query tool! Printing paths instead."
+               echo
+               for path in ${paths}; do
+                       echo ${path}
+               done
+       fi
+}
+
+# *64 needs to be first, as *lib is a symlink to it so equery screws up
+libdirs="/lib64 /usr/lib64 /lib /usr/lib $(get_libdirs)"
+
+echo "Analyzing log ..."
+libnames=$(get_libnames "$@")
+
+#echo libnames=$libnames
+
+echo "Looking for libraries ..."
+linker_lines=$(get_linklines ${1})
+
+#echo linker_lines=$linker_lines
+
+for libname in ${libnames}; do
+       static=0
+       shared=0
+       line=$(echo ${linker_lines} | grep "\b-l${libname}\b")
+       if echo ${line} | grep -q '\b-static\b'; then
+               static=1
+       fi
+       if ! echo ${line} | grep -q '\b-static\b'; then
+               shared=1
+       fi
+       staticlibname="lib${libname}.a"
+       sharedlibname="lib${libname}.so"
+       if [[ ${static} -eq 1 ]]; then
+               echo -n "  Looking for ${staticlibname} ... "
+               for libdir in ${libdirs}; do
+                       found=0
+                       if [[ -e ${libdir}/${staticlibname} ]]; then
+                               libpaths="${libpaths} ${libdir}/${staticlibname}"
+                               found=1
+                               echo "OK"
+                               break
+                       fi
+               done
+               if [[ ${found} -ne 1 ]]; then
+                       echo "Not found!"
+               fi
+       fi
+       if [[ ${shared} -eq 1 ]]; then
+               echo -n "  Looking for ${sharedlibname} ... "
+               for libdir in ${libdirs}; do
+                       found=0
+                       if [[ -e ${libdir}/${sharedlibname} ]]; then
+                               libpaths="${libpaths} ${libdir}/${sharedlibname}"
+                               found=1
+                               echo "OK"
+                               break
+                       fi
+               done
+               if [[ ${found} -ne 1 ]]; then
+                       echo "Not found!"
+               fi
+       fi
+done
+
+# Add backslashes in front of any + symbols
+libpaths=${libpaths//+/\\+}
+
+echo "Looking for build tools (imake, etc) ..."
+BUILD_PKGS=$(egrep -h "$@" \
+       -e '^(/usr/(X11R6/)?bin/)?rman' \
+       -e '^(/usr/(X11R6/)?bin/)?gccmakedep' \
+       -e '^(/usr/(X11R6/)?bin/)?makedepend' \
+       -e '^(/usr/(X11R6/)?bin/)?imake' \
+       -e '^(/usr/(X11R6/)?bin/)?rman' \
+       -e '^(/usr/(X11R6/)?bin/)?lndir' \
+       -e '^(/usr/(X11R6/)?bin/)?xmkmf' \
+       | awk '{ print $1 }' \
+       | sort \
+       | uniq)
+
+for PKG in ${BUILD_PKGS}; do
+       PKG=$(basename ${PKG})
+       echo -n "  Looking for ${PKG} ... "
+       if [[ -e /usr/bin/${PKG} ]]; then
+               echo "OK"
+               buildpaths="${buildpaths} ${PKG}"
+       else
+               echo "Not found!"
+       fi
+done
+
+echo
+echo "Tracing libraries back to packages ..."
+echo
+trace_to_packages "${libpaths}"
+
+echo
+echo "Tracing build tools back to packages ..."
+echo
+
+trace_to_packages "${buildpaths}"
diff --git a/deprecated/distfiles-clean/AUTHORS b/deprecated/distfiles-clean/AUTHORS
new file mode 100644 (file)
index 0000000..d913891
--- /dev/null
@@ -0,0 +1,6 @@
+José Fonseca <j_r_fonseca@yahoo.co.uk>
+ * Wrote the script
+
+Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Wrote the man page.
+
diff --git a/deprecated/distfiles-clean/ChangeLog b/deprecated/distfiles-clean/ChangeLog
new file mode 100644 (file)
index 0000000..dfe6aa8
--- /dev/null
@@ -0,0 +1,2 @@
+2002-15-11: Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Imported newest contributions from #10647.
diff --git a/deprecated/distfiles-clean/TODO b/deprecated/distfiles-clean/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/distfiles-clean/distfiles-clean b/deprecated/distfiles-clean/distfiles-clean
new file mode 100644 (file)
index 0000000..23af32b
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# distfiles-clean
+#
+# Cleans unused files from Portage's distfiles directory.
+#
+# José Fonseca <j_r_fonseca@yahoo.co.uk>
+
+PROGRAM=`basename "$0"`
+
+while [ ${#} -gt 0 ]
+do
+       case "$1" in
+               -h|--help)
+                       USAGE=y
+                       break
+                       ;;
+               -i|--ignore)
+                       IGNORE="$IGNORE $2"
+                       shift 2
+                       ;;
+               -I|--ignore-file)
+                       IGNORE="$IGNORE `cat "$2"`"
+                       shift 2
+                       ;;
+               -p|--pretend)
+                       PRETEND=y
+                       shift
+                       ;;
+               *)
+                       echo "$PROGRAM: Invalid option \'$1\'" 1>&2
+                       USAGE=y
+                       break
+                       ;;
+       esac
+done
+
+# For PORTDIR and DISTDIR
+. /etc/make.globals
+. /etc/make.conf
+
+if [ "$USAGE" ]
+then
+       echo "Usage:    $PROGRAM [-h|--help] [-i|--ignore <glob>] [-I|--ignore-file <globfile>] [-p|--pretend]"
+       echo "Cleans unused files from $DISTDIR directory."
+       exit
+fi
+
+DBDIR=/var/db/pkg
+CACHEDIR=/var/cache/edb/dep
+
+for DIR in "$PORTDIR" "$DISTDIR" "$DBDIR" "$CACHEDIR"
+do
+       if [ ! -d "$DIR" ]
+       then
+               echo "$PROGRAM: \'$DIR\' not found."
+               exit
+       fi
+done
+
+TMPFILE=`mktemp /tmp/$PROGRAM.XXXXXX`
+
+cd "$DISTDIR"
+
+{
+       echo "cvs-src"
+       [ "$IGNORE" ] && ls -1d $IGNORE
+       find "$DBDIR" -name '*.ebuild' | sed -n -e "s:^$DBDIR/\([^/]*\)/\([^/]*\)/\([^/]*\)\.ebuild$:$CACHEDIR/\1/\3:p" | xargs sed -s -e '4!d;/^$/d;s/[[:alnum:]]\+?\|(\|)//g;s/\<[^[:space:]]\+\/\<//g;s/^[[:space:]]\+//g;s/[[:space:]]\+$//g;s/[[:space:]]\+/\n/g'
+} | sort -u > "$TMPFILE" && ls -1 | comm -23 - "$TMPFILE" | {
+       if [ "$PRETEND" ]
+       then
+               cat
+       else
+               xargs rm -f
+       fi
+}
+
+rm "$TMPFILE"
diff --git a/deprecated/epkginfo/AUTHORS b/deprecated/epkginfo/AUTHORS
new file mode 100644 (file)
index 0000000..f499856
--- /dev/null
@@ -0,0 +1,3 @@
+Author: Ned Ludd <solar@gentoo.org> (glue all the parts together)
+Author: Eldad Zack <eldad@gentoo.org> (earch)
+Author : Eric Olinger <EvvL AT RustedHalo DOT net> (metadata)
diff --git a/deprecated/epkginfo/README b/deprecated/epkginfo/README
new file mode 100644 (file)
index 0000000..f0e3656
--- /dev/null
@@ -0,0 +1 @@
+epkginfo is now a an alias for equery meta.  This is the original version that was written by solar.
diff --git a/deprecated/epkginfo/epkginfo b/deprecated/epkginfo/epkginfo
new file mode 100755 (executable)
index 0000000..fd59e4b
--- /dev/null
@@ -0,0 +1,244 @@
+#!/usr/bin/python
+##############################################################################
+# $Header: $
+##############################################################################
+# Distributed under the terms of the GNU General Public License, v2 or later
+# Author: Ned Ludd <solar@gentoo.org> (glue all the parts together)
+# Author: Eldad Zack <eldad@gentoo.org> (earch)
+# Author : Eric Olinger <EvvL AT RustedHalo DOT net> (metadata)
+
+# Gentoo metadata xml and arch keyword checking tool.
+
+import os
+import sys
+import re
+from stat import *
+from xml.sax import saxutils, make_parser, handler
+from xml.sax.handler import feature_namespaces
+
+import portage
+from portage.output import *
+
+__version__ = "svn"
+
+def earch(workdir):
+       """Prints arch keywords for a given dir"""
+       portdir = portage.settings["PORTDIR"]
+       #workdir = "."
+       os.chdir(workdir)
+
+       archdict = {}
+       ebuildlist = []
+       for file in os.listdir(workdir):
+               if re.search("\.ebuild$",file):
+                       ebuildlist.append(re.split("\.ebuild$",file)[0])
+
+       ebuildlist.sort(lambda x,y: portage.pkgcmp(portage.pkgsplit(x),portage.pkgsplit(y)))
+
+       slot_list = []
+
+       for pkg in ebuildlist:          
+               portdb = portage.portdbapi(portdir)
+               aux = portdb.aux_get(workdir.rsplit("/")[-2] + "/" + pkg, ['SLOT', 'KEYWORDS'])
+               
+               slot = aux[0]
+               keywords = keywords = re.split(' ',aux[1])
+               
+               if not slot in slot_list:
+                       slot_list.append(slot)
+               
+               for arch in keywords:
+                       if arch in archdict:
+                               archdict[arch].append((pkg, slot))
+                       else:
+                               archdict[arch] = [ (pkg, slot) ]
+
+       archlist = archdict.keys();
+       archlist.sort()
+
+       slot_list.sort()
+
+       for slot in slot_list:
+               visible_stable = {}
+               visible_unstable = {}
+               
+               for arch in archlist:
+                       visible_stable[arch] = None
+                       visible_unstable[arch] = None
+                       
+               for pkg in ebuildlist:
+                       for arch in archlist:
+                               if (arch and (pkg, slot) in archdict[arch]):
+                                       if arch[0] == "-":
+                                               pass
+                                       elif "~" == arch[0]:
+                                               visible_unstable[arch] = pkg
+                                       else:
+                                               visible_unstable["~" + arch] = None
+                                               visible_stable[arch] = pkg
+               
+               for pkg in ebuildlist:
+                       found = False
+                       for arch in archlist:
+                               if (pkg, slot) in archdict[arch]:
+                                       found = True
+                       
+                       if not found:
+                               continue
+                       
+                       if not pkg == ebuildlist[0]:
+                               print ""
+                               
+                       print darkgreen("Keywords: ") + pkg + "[" + slot + "]:",
+                       
+                       for arch in archlist:
+                               if (arch and (pkg, slot) in archdict[arch]):
+                                       if arch[0] == "-":
+                                               print red(arch),
+                                       elif "~" == arch[0]:
+                                               if visible_unstable[arch] == pkg:
+                                                       print blue(arch),
+                                       else:
+                                               if visible_stable[arch] == pkg:
+                                                       print green(arch),
+
+
+class Metadata_XML(handler.ContentHandler):
+       _inside_herd="No"
+       _inside_maintainer="No"
+       _inside_email="No"
+       _inside_longdescription="No"
+
+       _herd = []
+       _maintainers = []
+       _longdescription = ""
+
+       def startElement(self, tag, attr):
+               if tag == "herd":
+                       self._inside_herd="Yes"
+               if tag == "longdescription":
+                       self._inside_longdescription="Yes"
+               if tag == "maintainer":
+                       self._inside_maintainer="Yes"
+               if tag == "email":
+                       self._inside_email="Yes"
+
+       def endElement(self, tag):
+               if tag == "herd":
+                       self._inside_herd="No"
+               if tag == "longdescription":
+                       self._inside_longdescription="No"
+               if tag == "maintainer":
+                       self._inside_maintainer="No"
+               if tag == "email":
+                       self._inside_email="No"
+
+       def characters(self, contents):
+               if self._inside_herd == "Yes":
+                       self._herd.append(contents)
+
+               if self._inside_longdescription == "Yes":
+                       self._longdescription = contents
+                       
+               if self._inside_maintainer=="Yes" and self._inside_email=="Yes":
+                       self._maintainers.append(contents)
+
+
+def check_metadata(full_package):
+       """Checks that the primary maintainer is still an active dev and list the herd the package belongs to"""
+       metadata_file=portage.settings["PORTDIR"] + "/" + portage.pkgsplit(full_package)[0] + "/metadata.xml"
+       if not os.path.exists(metadata_file):
+               print darkgreen("Maintainer: ") + red("Error (Missing metadata.xml)")
+               return 1
+
+       parser = make_parser()
+       handler = Metadata_XML()
+       handler._maintainers = []
+       parser.setContentHandler(handler)
+       parser.parse( metadata_file )
+
+       if handler._herd:
+               herds = ", ".join(handler._herd)
+               print darkgreen("Herd: ") + herds
+       else:       
+               print darkgreen("Herd: ") + red("Error (No Herd)")
+               return 1
+
+
+       if handler._maintainers:
+               print darkgreen("Maintainer: ") + ", ".join(handler._maintainers)
+       else:
+               print darkgreen("Maintainer: ") + "none"
+
+       if len(handler._longdescription) > 1:
+               print darkgreen("Description: ") + handler._longdescription
+       print darkgreen("Location: ") + os.path.normpath(portage.settings["PORTDIR"] + "/" + portage.pkgsplit(full_package)[0])
+
+
+def usage(code):
+       """Prints the uage information for this script"""
+       print green("epkginfo"), "(%s)" % __version__
+       print
+       print "Usage: epkginfo [package-cat/]package"
+       sys.exit(code)
+
+
+# default color setup
+if ( not sys.stdout.isatty() ) or ( portage.settings["NOCOLOR"] in ["yes","true"] ):
+       nocolor()
+
+def fc(x,y):
+       return cmp(y[0], x[0])
+
+
+def grab_changelog_devs(catpkg):
+       try:
+               os.chdir(portage.settings["PORTDIR"] + "/" + catpkg)
+               foo=""
+               r=re.compile("<[^@]+@gentoo.org>", re.I)
+               s="\n".join(portage.grabfile("ChangeLog"))
+               d={}
+               for x in r.findall(s):
+                       if x not in d:
+                               d[x] = 0
+                       d[x] += 1
+
+               l=[(d[x], x) for x in d.keys()]
+               #l.sort(lambda x,y: cmp(y[0], x[0]))
+               l.sort(fc)
+               for x in l:
+                       p = str(x[0]) +" "+ x[1].lstrip("<").rstrip(">")
+                       foo += p[:p.find("@")]+", "
+               return foo
+       except:
+               raise
+
+def main ():
+       if len( sys.argv ) < 2:
+               usage(1)
+
+       for pkg in sys.argv[1:]:
+
+               if sys.argv[1:][:1] == "-":
+                       print "NOT WORKING?=="+sys.argv[1:]
+                       continue
+
+               try:
+                       package_list = portage.portdb.xmatch("match-all", pkg)
+                       if package_list:
+
+                               catpkg = portage.pkgsplit(package_list[0])[0]
+
+                               print darkgreen("Package: ") + catpkg
+                               check_metadata(package_list[0])
+                               earch(portage.settings["PORTDIR"] + "/" + catpkg)
+                               #print darkgreen("ChangeLog: ") + grab_changelog_devs(catpkg)
+                               print ""
+                       else:
+                               print "!!! No package '%s'" % pkg
+               except:
+                       print red("Error: "+pkg+"\n")
+
+
+if __name__ == '__main__':
+       main()
diff --git a/deprecated/epkginfo/epkginfo.1 b/deprecated/epkginfo/epkginfo.1
new file mode 100644 (file)
index 0000000..cefe602
--- /dev/null
@@ -0,0 +1,34 @@
+.TH "epkginfo" "1" "0.4.1" "Ned Ludd" "gentoolkit"
+.SH "NAME"
+.LP 
+epkginfo \- Displays metadata information from packages in portage
+.SH "SYNTAX"
+.LP 
+epkginfo [\fIpackage\-cat/\fP]package
+.SH "EXAMPLES"
+$ epkginfo app\-portage/gentoolkit
+.br 
+\fBPackage:\fR app\-portage/gentoolkit
+.br 
+\fBHerd:\fR tools\-portage
+.br 
+\fBMaintainer:\fR tools\-portage
+.br 
+\fBLocation:\fR /usr/portage/app\-portage/gentoolkit
+.br 
+\fBKeywords:\fR gentoolkit\-0.2.2:
+.br 
+\fBKeywords:\fR gentoolkit\-0.2.3:  mips
+.br 
+\fBKeywords:\fR gentoolkit\-0.2.3\-r1:  ppc ppc64 alpha arm s390 amd64 hppa x86 sparc ia64 m68k sh
+.br 
+\fBKeywords:\fR gentoolkit\-0.2.4_pre3:
+.br 
+\fBKeywords:\fR gentoolkit\-0.2.4_pre4:
+.br 
+\fBKeywords:\fR gentoolkit\-0.2.4_pre5:  ~arm ~hppa ~x86 ~m68k ~amd64 ~ppc ~sh ~x86\-fbsd ~ia64 ~alpha ~sparc ~ppc64 ~sparc\-fbsd ~mips ~s390
+.SH "AUTHORS"
+.LP 
+Ned Ludd <solar@gentoo.org>
+.SH "BUGS"
+Please report any bugs to http://bugs.gentoo.org
diff --git a/deprecated/epkgmove/AUTHORS b/deprecated/epkgmove/AUTHORS
new file mode 100644 (file)
index 0000000..38fdca1
--- /dev/null
@@ -0,0 +1,2 @@
+Maintainer:
+Ian Leitch <port001@gentoo.org>
diff --git a/deprecated/epkgmove/ChangeLog b/deprecated/epkgmove/ChangeLog
new file mode 100644 (file)
index 0000000..6bfb8d7
--- /dev/null
@@ -0,0 +1,20 @@
+2004-02-03 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Updated epkgmove to 1.3.1, as availble on
+       http://dev.gentoo.org/~port001/DevTools/epkgmove/epkgmove-1.3.1.py
+
+2004-09-27 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Updated epkgmove to 1.1, as availble on
+       http://dev.gentoo.org/~port001/DevTools/epkgmove/epkgmove-1.1.py
+
+2004-09-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Updated epkgmove to 1.0, as availble on
+       http://dev.gentoo.org/~port001/DevTools/epkgmove/epkgmove-1.0.py
+
+2004-07-21 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Updated epkgmove to 0.5, as availble on
+       http://dev.gentoo.org/~port001/DevTools/epkgmove/epkgmove-0.5.py
+
+2004-04-02 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Updated epkgmove to 0.4, as availble on
+       http://dev.gentoo.org/~port001/DevTools/epkgmove/epkgmove-0.4.py
+
diff --git a/deprecated/epkgmove/Makefile b/deprecated/epkgmove/Makefile
new file mode 100644 (file)
index 0000000..ce9b950
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+.PHONY: all
+all: 
+
+dist: 
+       mkdir -p ../../$(distdir)/src/epkgmove/
+       cp Makefile AUTHORS README TODO ChangeLog epkgmove ../../$(distdir)/src/epkgmove/
+
+install: all
+       install -m 0755 epkgmove $(bindir)/
+       install -d $(docdir)/epkgmove
+       install -m 0644 AUTHORS README TODO ChangeLog $(docdir)/epkgmove/
+#      install -m 0644 epkgmove.1 $(mandir)/
diff --git a/deprecated/epkgmove/README b/deprecated/epkgmove/README
new file mode 100644 (file)
index 0000000..4668fa3
--- /dev/null
@@ -0,0 +1,16 @@
+
+Package : epkgmove
+Version : 0.5
+Author  : See AUTHORS
+
+MOTIVATION
+
+Eases moving around ebuilds in the Portage CVS tree.
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+N/A
diff --git a/deprecated/epkgmove/TODO b/deprecated/epkgmove/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/epkgmove/epkgmove b/deprecated/epkgmove/epkgmove
new file mode 100644 (file)
index 0000000..42b6e7d
--- /dev/null
@@ -0,0 +1,895 @@
+#!/usr/bin/python -O
+# Copyright 2004 Ian Leitch
+# Copyright 1999-2004 Gentoo Foundation
+# $Header$
+#
+# Author:
+#  Ian Leitch <port001@gentoo.org>
+#
+
+import os
+import re
+import sys
+import signal
+import commands
+from time import sleep
+from random import randint
+from optparse import OptionParser
+
+import portage
+try:
+       from portage.output import *
+except ImportError:
+       from output import *
+
+__author__     =       "Ian Leitch"
+__email__      =       "port001@gentoo.org"
+__productname__ =      "epkgmove"
+__version__    =       "1.3.1 - \"Moving Fusion + Bandages + Plasters\""
+__description__ =      "A tool for moving and renaming packages in CVS"
+
+def print_usage():
+
+    print
+    print "%s %s [ %s ] [ %s ] [ %s ]" % (white("Usage:"), turquoise(__productname__), green("option"), green("origin"), green("destination"))
+    print "   '%s' and '%s' are expected as a full package name, e.g. net-im/gaim" % (green("origin"), green("destination"))
+    print "   See %s --help for a list of options" % __productname__
+    print
+
+def check_cwd(portdir):
+
+    if os.getcwd() != portdir:
+        print
+        print "%s Not in PORTDIR!" % yellow(" *")
+        print "%s Setting to: %s" % (yellow("   *"), os.getcwd())
+       os.environ["PORTDIR"]=os.getcwd()
+        return os.getcwd()
+       
+    return portdir
+
+def check_args(args, portdir, options, cvscmd):
+
+    booboo = False
+    re_expr = "^[\da-zA-Z-]{,}/[\d\w0-9-]{,}$"
+    o_re_expr = re.compile(re_expr)
+
+    if len(args) == 0:
+        print "\n%s ERROR: errr, didn't you forget something" % red("!!!"),
+       count = range(3)
+       for second in count:
+           sys.stdout.write(".")
+           sys.stdout.flush()
+           sleep(1)
+        sys.stdout.write("?")
+       sys.stdout.flush()
+       sleep(1)
+       print "\n\n%s %s hits you with a clue stick %s" % (green("    *"), __productname__, green("*"))
+       sleep(1)
+       print_usage()
+       sys.exit(1)
+
+    if options.remove:
+        if len(args) > 1:
+           error("Please remove packages one at a time")
+           sys.exit(1)
+       elif not o_re_expr.match(args[0].rstrip("/")):
+           error("Expected full package name as argument")
+           sys.exit(1)
+        elif not os.path.exists(os.path.join(portdir, args[0].rstrip("/"))):
+           error("No such package '%s'" % args[0].rstrip("/"))
+           sys.exit(1)
+       else:
+            if not options.cvs_up:
+                update_categories(portdir, args, cvscmd["update"])
+            return (args[0].rstrip("/"), None)
+
+    if len(args) == 2:
+        if not o_re_expr.match(args[0].rstrip("/")):
+           error("Expected full package name as origin argument")
+           booboo = True
+       elif not o_re_expr.match(args[1].rstrip("/")):
+           error("Expected full package name as destination argument")
+           booboo = True
+
+       if booboo == True:
+           sys.exit(1)
+    else:
+        error("Expected two arguments as input.")
+        print_usage()
+       sys.exit(1)
+
+    if not options.cvs_up:
+        update_categories(portdir, args, cvscmd["update"])
+
+    if not os.path.exists(os.path.join(portdir, args[0].rstrip("/"))):
+        error("No such package '%s'" % args[0].rstrip("/"))
+       booboo = True
+    elif os.path.exists(os.path.join(portdir, args[1].rstrip("/"))):
+        error("Package '%s' already exists" % args[1].rstrip("/"))
+        booboo = True
+
+    if booboo == True:
+        sys.exit(1)
+    
+    return (args[0].rstrip("/"), args[1].rstrip("/"))
+
+def check_repos(portdir):
+
+    files = os.listdir(portdir)
+
+    for file in files:
+        if not os.path.isdir(file):
+            files.remove(file)
+                                                           
+    if "CVS" not in files:
+        error("Current directory doesn't look like a CVS repository")
+       sys.exit(1)
+
+def check_commit_queue():
+
+    empty = True
+
+    print
+    print "%s Checking commit queue for outstanding changes..." % green(" *")
+    print "%s Note: This may take a VERY long time" % yellow("   *")
+    print
+        
+    output = commands.getoutput("cvs diff")
+    
+    for line in output.split("\n"):
+        if not line.startswith("?"):
+           empty = False
+           break
+           
+    if empty == False:
+        error("Commit queue not empty! Please commit all outstanding changes before using %s." % __productname__)
+       sys.exit(1)
+
+def update_categories(portdir, catpkgs, cvsupcmd):
+
+    my_catpkgs = []
+
+    print
+    print "%s Updating categories: " % green(" *")
+
+    if len(catpkgs) >= 2:
+        if catpkgs[0].split("/", 1)[0] == catpkgs[1].split("/", 1)[0]:
+            my_catpkgs.append(catpkgs[0])
+        else:
+            my_catpkgs.append(catpkgs[0])
+           my_catpkgs.append(catpkgs[1])
+    else:
+        my_catpkgs.append(catpkgs[0])
+
+    for catpkg in my_catpkgs:
+        (category, package) = catpkg.split("/", 1)
+        if os.path.exists(os.path.join(portdir, category)):
+           os.chdir(os.path.join(portdir, category))
+           print "   %s %s" % (green("*"), category)
+            do_cmd(cvsupcmd)
+       else:
+           print "   %s %s" % (red("!"), category)
+
+    os.chdir(portdir)
+    
+def error(msg):
+
+    sys.stderr.write("\n%s ERROR: %s\n" % (red("!!!"), msg))
+
+def signal_handler(signal_number=None, stack_frame=None):
+
+    error("Caught SIGINT; exiting...")
+    sys.exit(1)
+    os.kill(0, signal.SIGKILL)
+
+def do_cmd(cmd):
+
+    (status, output) = commands.getstatusoutput(cmd)
+    if status != 0:
+        error("Command '%s' failed with exit status %d." % (cmd, status))
+       for line in output.split("\n"):
+           if line != "":
+               print "    %s %s" % (red("!"), line)
+       sys.exit(1)
+       
+class CVSAbstraction:
+
+    def __init__(self, portdir, oldcatpkg, newcatpkg, cvscmd, options):
+    
+        self._portdir = portdir
+       self._cvscmd = cvscmd
+       self._options = options
+        self._ignore = ("CVS")
+
+       self._old_category = ""
+       self._old_package = ""
+       self._new_category = ""
+       self._new_package = ""
+        self._old_catpkg = oldcatpkg
+       self._new_catpkg = newcatpkg
+
+        self._action = ""
+       
+       self._distinguish_action(oldcatpkg, newcatpkg)
+
+    def _distinguish_action(self, oldcatpkg, newcatpkg):
+                
+        (self._old_category, self._old_package) = oldcatpkg.split("/")
+        if newcatpkg:
+            (self._new_category, self._new_package) = newcatpkg.split("/")
+
+        if self._old_category != self._new_category and self._new_category:
+           if self._old_package != self._new_package and self._new_package:
+               self._action = "MOVE & RENAME"
+            else:
+               self._action = "MOVE"
+        elif self._old_package != self._new_package and self._new_package:
+           self._action = "RENAME"
+       elif not self._new_package:
+           self._action = "REMOVE"
+       else:
+           error("Unable to distingush required action.")
+           sys.exit(1)
+
+    def __backup(self):
+
+        print "%s Backing up %s..." % (green("   *"), turquoise(self._old_catpkg))
+       
+       if not os.path.exists("/tmp/%s" % __productname__):
+           os.mkdir("/tmp/%s" % __productname__)
+       
+       if os.path.exists("/tmp/%s/%s" % (__productname__, self._old_package)):
+           do_cmd("rm -rf /tmp/%s/%s" % (__productname__, self._old_package))
+           
+       do_cmd("cp -R %s /tmp/%s/%s" % (os.path.join(self._portdir, self._old_catpkg), __productname__, self._old_package))
+
+    def perform_action(self):
+
+        count_down = 5
+
+        print
+       if self._action == "REMOVE":
+           print "%s Performing a '%s' of %s..." % (green(" *"), green(self._action), turquoise(self._old_catpkg))
+        else:
+            print "%s Performing a '%s' of %s to %s..." % (green(" *"), green(self._action), turquoise(self._old_catpkg), yellow(self._new_catpkg))
+       
+       if not self._options.countdown:
+           print "%s Performing in: " % green(" *"),
+            count = range(count_down)
+           count.reverse()
+           for second in count:
+               sys.stdout.write("%s " % red(str(second + 1)))
+               sys.stdout.flush()
+               sleep(1)
+           print
+
+        if not self._action == "REMOVE":
+            self.__backup()
+
+        if self._action == "MOVE & RENAME":
+           self._perform_move_rename()
+       elif self._action == "MOVE":
+           self._perform_move()
+       elif self._action == "RENAME":
+           self._perform_rename()
+       elif self._action == "REMOVE":
+           self._perform_remove()
+
+    def _perform_remove(self):
+
+        deps = self.__get_reverse_deps()
+
+       if deps:
+           print "%s The following ebuild(s) depend on this package:" % red("     *")
+           for dep in deps:
+               print "%s %s" % (red("       !"), dep)
+           if self._options.force:
+               print "%s Are you sure you wish to force removal of this package?" % yellow("   *"),
+               try:
+                   choice = raw_input("(Yes/No): ")
+               except KeyboardInterrupt:
+                   error("Interrupted by user.")
+                   sys.exit(1)
+               if choice.strip().lower() != "yes":
+                   error("Bailing on forced removal.")
+                   sys.exit(1)
+           else:
+               error("Refusing to remove from CVS, package has dependents.")
+                sys.exit(1)
+
+       self.__remove_old_package()
+
+    def _perform_move(self):
+
+        self.__add_new_package()
+        self.__regen_manifest()
+        self.__update_dependents(self.__get_reverse_deps())
+       self.__remove_old_package()
+       
+    def _perform_move_rename(self):
+
+        self._perform_rename()
+    def _perform_rename(self):
+        
+        self.__rename_files()
+       self.__add_new_package()
+        self.__regen_digests()
+       self.__update_dependents(self.__get_reverse_deps())
+       self.__remove_old_package()
+
+    def __rename_files(self):
+
+        def rename_files(arg, dir, files):
+        
+           if os.path.basename(dir) not in self._ignore:
+               if os.path.basename(dir) != self._old_package:
+                   for file in files:
+                       new_file = ""
+                       if file.find(self._old_package) >= 0:
+                           new_file = file.replace(self._old_package, self._new_package)
+                           do_cmd("mv %s %s" % (os.path.join(dir, file), os.path.join(dir, new_file)))
+                       if not os.path.isdir(os.path.join(dir, new_file)) and not file.startswith("digest-"):
+                           self.__rename_file_contents(os.path.join(dir, new_file))
+                else:
+                   for file in files:
+                       if file.endswith(".ebuild"):
+                           new_file = file.replace(self._old_package, self._new_package)
+                           do_cmd("mv %s %s" % (os.path.join(dir, file), os.path.join(dir, new_file)))
+                            self.__rename_file_contents(os.path.join(dir, new_file))
+                       elif file.endswith(".xml"):
+                           self.__rename_file_contents(os.path.join(dir, file))
+
+       print "%s Renaming files..." % green("   *")
+       os.path.walk("/tmp/%s/%s" % (__productname__, self._old_package), rename_files , None)
+       do_cmd("mv /tmp/%s/%s /tmp/%s/%s" % (__productname__, self._old_package, __productname__, self._new_package))
+
+    def __regen_manifest(self, path=None, dep=False):
+
+        if dep:
+           print "%s Regenerating Manifest..." % green("      *")
+        else:
+            print "%s Regenerating Manifest..." % green("   *")
+       if path:
+           os.chdir(path)
+       else:
+            os.chdir(os.path.join(self._portdir, self._new_catpkg))
+        for ebuild in os.listdir("."):
+            if ebuild.endswith(".ebuild"):
+                do_cmd("/usr/lib/portage/bin/ebuild %s manifest" % ebuild)
+                break
+
+        self.__gpg_sign(dep)
+
+    def __regen_digests(self):
+
+        print "%s Regenerating digests:" % green("   *")
+        os.chdir(os.path.join(self._portdir, self._new_catpkg))
+       for digest in os.listdir("files/"):
+           if digest.startswith("digest-"):
+               os.unlink("files/%s" % digest)
+       for ebuild in os.listdir("."):
+           if ebuild.endswith(".ebuild"):
+                print "      >>> %s" % ebuild  
+               do_cmd("/usr/lib/portage/bin/ebuild %s digest" % ebuild)
+
+        self.__gpg_sign()
+
+    def __gpg_sign(self, dep=False):
+
+        gpg_cmd = ""
+
+        os.chdir(os.path.join(self._portdir, self._new_catpkg))
+
+        if "sign" in portage.features:
+            if dep:
+                print "%s GPG Signing Manifest..." % green("      *")
+            else:
+                print "%s GPG Signing Manifest..." % green("   *")
+           gpg_cmd = "gpg --quiet --sign --clearsign --yes --default-key %s" % portage.settings["PORTAGE_GPG_KEY"]
+           if portage.settings.has_key("PORTAGE_GPG_DIR"):
+               gpg_cmd = "%s --homedir %s" % (gpg_cmd, portage.settings["PORTAGE_GPG_DIR"])
+           do_cmd("%s Manifest" % gpg_cmd)
+           do_cmd("mv Manifest.asc Manifest")
+       do_cmd("%s 'Manifest recommit'" % self._cvscmd["commit"])
+
+    def __rename_file_contents(self, file):
+        
+       def choice_loop(line, match_count, choice_list, type="name", replace=""):
+
+            new_line = line
+           accepted = False
+           skipp = False
+
+           while(not accepted):
+               print "         ",
+               if len(choice_list) > 0:
+                    for choice in choice_list:
+                       print "%s: Replace with '%s'," % (white(choice), green(choice_list[choice])),
+               if match_count == 0:
+                   print "%s: Pass, %s: Skip this file, %s: Custom" % (white(str(len(choice_list)+1)), white(str(len(choice_list)+2)), white(str(len(choice_list)+3))),
+               else:
+                   print "%s: Pass, %s: Custom" % (white(str(len(choice_list)+1)), white(str(len(choice_list)+2))),
+               try:
+                   input = raw_input("%s " % white(":"))
+               except KeyboardInterrupt:
+                   print
+                   error("Interrupted by user.")
+                   sys.exit(1)
+               if choice_list.has_key(input):
+                   if type == "name":
+                       new_line = new_line.replace(self._old_package, choice_list[input])
+                       accepted = True
+                   elif type == "P":
+                       new_line = new_line.replace("${P}", choice_list[input])
+                        accepted = True
+                   elif type == "PN":
+                       new_line = new_line.replace("${PN}", choice_list[input])
+                        accepted = True
+                   elif type == "PV":
+                       new_line = new_line.replace("${PV}", choice_list[input])
+                        accepted = True
+                   elif type == "PVR":
+                       new_line = new_line.replace("${PVR}", choice_list[input])
+                        accepted = True
+                   elif type == "PR":
+                       new_line = new_line.replace("${PR}", choice_list[input])
+                        accepted = True
+                   elif type == "PF":
+                       new_line = new_line.replace("${PF}", choice_list[input])
+                        accepted = True
+               elif input == str(len(choice_list)+1):
+                   accepted = True
+               elif input == str(len(choice_list)+2) and match_count == 0:
+                   accepted = True
+                   skipp = True
+               elif input == str(len(choice_list)+3) and match_count == 0 or input == str(len(choice_list)+2) and match_count != 0:
+                   
+                   input_accepted = False
+                   
+                   while(not input_accepted):
+                       try:
+                           custom_input = raw_input("            %s Replacement string: " % green("*"))
+                       except KeyboardInterrupt:
+                           print
+                           error("Interrupted by user.")
+                           sys.exit(1)
+                       while(1):
+                           print "              %s Replace '%s' with '%s'? (Y/N)" % (yellow("*"), white(replace), white(custom_input)),
+                           try:
+                               yes_no = raw_input(": ")
+                           except KeyboardInterrupt:
+                               print
+                               error("Interrupted by user.")
+                               sys.exit(1)
+                           if yes_no.lower() == "y":
+                               input_accepted = True
+                               break
+                           elif yes_no.lower() == "n":
+                               break
+
+                    new_line = new_line.replace(replace, custom_input)
+                    accepted = True
+               else:
+                   accepted = False
+                   
+               if skipp:
+                   break
+
+           return (new_line, skipp)
+
+        found = False
+       contents = []
+       new_contents = []
+       match_count = 0
+       skipp = False
+
+        try:
+           readfd = open(file, "r")
+           contents = readfd.readlines()
+           readfd.close()
+       except IOError, e:
+           error(e)
+           sys.exit(1)
+       
+       for line in contents:
+           if line.find(self._old_package) >= 0:
+               found = True
+               break
+
+       if found == True:
+           print "%s Editing %s:" % (green("     *"), white(file.split("/tmp/%s/%s/" % (__productname__, self._old_package))[1]))
+           try:
+               writefd = open(file, "w")
+           except IOError, e:
+               error(e)
+               sys.exit(1)
+           for line in contents:
+               tmp_line = line 
+               if not line.startswith("# $Header:"):
+                    if line.find(self._old_package) >= 0:
+                       print "%s %s" % (green("       !"), line.strip().replace(self._old_package, yellow(self._old_package)))
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {"1": self._new_package,
+                                                                                "2": "${PN}"}, type="name", replace=self._old_package)
+                       match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${P}") >= 0:
+                        print "%s %s" % (green("       !"), line.strip().replace("${P}", yellow("${P}")))
+                       (pkg, version, revising) = portage.pkgsplit(os.path.split(file)[1][:-7])
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {"1": "%s-%s" % (pkg, version),
+                                                                                "2": "%s-%s" % (self._old_package, version)}, type="P", replace="${P}")
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PN}") >= 0:
+                       print "%s %s" % (green("       !"), line.strip().replace("${PN}", yellow("${PN}")))
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {"1": self._new_package,
+                                                                               "2": self._old_package}, type="PN", replace="${PN}")
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PV}") >= 0:
+                        print "%s %s" % (green("       !"), line.strip().replace("${PV}", yellow("${PV}")))
+                       (pkg, version, revision) = portage.pkgsplit(os.path.split(file)[1][:-7])
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {"1": version}, type="PV", replace="${PV}")
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PVR}") >= 0:
+                        print "%s %s" % (green("       !"), line.strip().replace("${PVR}", yellow("${PVR}")))
+                       (pkg, version, revision) = portage.pkgsplit(os.path.split(file)[1][:-7])
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {"1": "%s-%s" % (version, revision)}, type="PVR", replace="${PVR}")
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PR}") >= 0:
+                        print "%s %s" % (green("       !"), line.strip().replace("${PR}", yellow("${PR}")))
+                       (pkg, version, revision) = portage.pkgsplit(os.path.split(file)[1][:-7])
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {"1": revision}, type="PR", replace="${PR}")
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PF}") >= 0:
+                        print "%s %s" % (green("       !"), line.strip().replace("${PF}", yellow("${PF}")))
+                       (pkg, version, revision) = portage.pkgsplit(os.path.split(file)[1][:-7])
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {"1": "%s-%s-%s" % (pkg, version, revision),
+                                                                               "2": "%s-%s-%s" % (self._old_package, version, revision)}, type="PF", replace="${PF}")
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${P/") >= 0:
+                       start = line.find("${P/")
+                       i = 0
+                       hi_str = ""
+                       while(line[start + (i - 1)] != "}"):
+                           hi_str += line[start + i]
+                            i += 1
+                        print "%s %s" % (green("       !"), line.strip().replace(hi_str, red(hi_str)))
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {}, type=None, replace=hi_str)
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PN/") >= 0:
+                       start = line.find("${PN/")
+                       i = 0
+                       hi_str = ""
+                       while(line[start + (i - 1)] != "}"):
+                           hi_str += line[start + i]
+                            i += 1
+                       print "%s %s" % (green("       !"), line.strip().replace(hi_str, red(hi_str)))
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {}, type=None, replace=hi_str)
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PV/") >= 0:
+                       start = line.find("${PV/")
+                       i = 0
+                       hi_str = ""
+                       while(line[start + (i - 1)] != "}"):
+                           hi_str += line[start + i]
+                            i += 1
+                       print "%s %s" % (green("       !"), line.strip().replace(hi_str, red(hi_str)))
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {}, type=None, replace=hi_str)
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PVR/") >= 0:
+                       start = line.find("${PVR/")
+                       i = 0
+                       hi_str = ""
+                       while(line[start + (i - 1)] != "}"):
+                           hi_str += line[start + i]
+                            i += 1
+                       print "%s %s" % (green("       !"), line.strip().replace(hi_str, red(hi_str)))
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {}, type=None, replace=hi_str)
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PR/") >= 0:
+                       start = line.find("${PR/")
+                       i = 0
+                       hi_str = ""
+                       while(line[start + (i - 1)] != "}"):
+                           hi_str += line[start + i]
+                            i += 1
+                       print "%s %s" % (green("       !"), line.strip().replace(hi_str, red(hi_str)))
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {}, type=None, replace=hi_str)
+                        match_count += 1
+                       if skipp:
+                           break
+                   if line.find("${PF/") >= 0:
+                       start = line.find("${PF/")
+                       i = 0
+                       hi_str = ""
+                       while(line[start + (i - 1)] != "}"):
+                           hi_str += line[start + i]
+                            i += 1
+                       print "%s %s" % (green("       !"), line.strip().replace(hi_str, red(hi_str)))
+                       (tmp_line, skipp) = choice_loop(tmp_line, match_count, {}, type=None, replace=hi_str)
+                        match_count += 1
+                       if skipp:
+                           break
+
+               new_contents.append(tmp_line)
+
+            if not skipp:
+                for line in new_contents:
+                    writefd.write(line)
+           else:
+               for line in contents:
+                   writefd.write(line)
+            
+           writefd.close()
+
+    def __update_dependents(self, dep_list):
+
+        if len(dep_list) <= 0:
+           return
+        
+       print "%s Updating dependents:" % green("   *")
+        os.chdir(self._portdir)
+
+        for dep in dep_list:
+           print "      >>> %s" % dep
+           new_contents = []
+           (category, pkg) = dep.split("/")
+           pkg_split = portage.pkgsplit(pkg)
+           os.chdir(self._portdir)
+           do_cmd("%s %s" % (self._cvscmd["update"], os.path.join(category, pkg_split[0])))
+
+            try:
+               readfd = open(os.path.join(self._portdir, category, pkg_split[0], "%s.ebuild" % pkg), "r")
+               contents = readfd.readlines()
+               readfd.close()
+           except IOError, e:
+               error(e)
+               sys.exit(1)
+           
+           for line in contents:
+               if self._old_catpkg in line:
+                   new_contents.append(line.replace(self._old_catpkg, self._new_catpkg))
+               else:
+                   new_contents.append(line)
+
+           try:
+               writefd = open(os.path.join(self._portdir, category, pkg_split[0], "%s.ebuild" % pkg), "w")
+               writefd.write("".join(new_contents))
+               writefd.close()
+           except IOError, e:
+               error(e)
+               sys.exit(1)
+       
+           os.chdir(os.path.join(self._portdir, category, pkg_split[0]))
+            do_cmd("echangelog 'Dependency update: %s -> %s.'" % (self._old_catpkg, self._new_catpkg))
+           do_cmd("%s 'Dependency update: %s -> %s.'" % (self._cvscmd["commit"], self._old_catpkg, self._new_catpkg))
+           self.__regen_manifest(path=os.path.join(self._portdir, category, pkg_split[0]), dep=True)
+            
+    def __get_reverse_deps(self):
+
+        dep_list = []
+        conf_portdir = "/usr/portage"
+
+       def scan_for_dep(arg, dir, files):
+
+            (null, category) = os.path.split(dir)
+
+            for file in files:
+               if self._old_catpkg not in os.path.join(category, file):
+                   if not os.path.isdir(os.path.join(dir, file)):
+                       try:
+                           fd = open(os.path.join(dir, file), "r")
+                           contents = fd.readlines()
+                           fd.close()
+                       except IOError, e:
+                           error(e)
+                           sys.exit(1)
+                       if self._old_catpkg in contents[0] or self._old_catpkg in contents[1] or self._old_catpkg in contents[12]:
+                           dep_list.append(os.path.join(category, file))
+        
+        print "%s Resolving reverse dependencies..." % green("   *")
+
+       try:
+           fd = open("/etc/make.conf", "r")
+           contents = fd.readlines()
+           fd.close()
+       except IOError, e:
+           error(e)
+           sys.exit(1)
+       
+       for line in contents:
+           if line.startswith("PORTDIR="):
+               (null, conf_portdir) = line.strip("\"\n").split("=")
+               break
+
+       os.path.walk(os.path.join(conf_portdir, "metadata/cache"), scan_for_dep, None)
+
+       return dep_list
+
+    def __remove_old_package(self):
+
+        def remove_files(arg, dir, files):
+
+          if os.path.basename(dir) not in self._ignore:
+              for file in files: 
+                  if not os.path.isdir(os.path.join(dir, file)): 
+                      print "      <<< %s" % (os.path.join(dir.strip("./"), file))
+                      os.unlink(os.path.join(dir, file))
+                      do_cmd("%s %s" % (self._cvscmd["remove"], os.path.join(dir, file)))
+
+        print "%s Removing %s from CVS:" % (green("   *"), turquoise(self._old_catpkg))
+        os.chdir(os.path.join(self._portdir, self._old_catpkg))
+        os.path.walk('.', remove_files , None)
+        os.chdir("..")
+
+        print "%s Commiting changes..." % green("     *")
+       if self._options.remove:
+           explanation = ""
+           while(1):
+               print "%s Please provide an explanation for this removal:" % yellow("       *"),
+               try:
+                   explanation = raw_input("")
+                   explanation = explanation.replace("'", "\\'").replace('"', '\\"')
+               except KeyboardInterrupt:
+                   print
+                   error("Interrupted by user.")
+                   sys.exit(1)
+               if explanation != "":
+                   break
+            do_cmd("""%s "Removed from %s: %s" """ % (self._cvscmd["commit"], self._old_category, explanation))
+        else:
+           do_cmd("%s 'Moved to %s.'" % (self._cvscmd["commit"], self._new_catpkg))
+        do_cmd("rm -rf %s" % os.path.join(self._portdir, self._old_catpkg))
+
+        print "%s Checking for remnant files..." % (green("     *"))
+        do_cmd(self._cvscmd["update"])
+                                                                                                                  
+        if not os.path.exists(os.path.join(self._portdir, self._old_catpkg)):
+           if not self._action == "REMOVE":
+                print "%s %s successfully removed from CVS." % (green("   *"), turquoise(self._old_catpkg))
+        else:
+            error("Remnants of %s still remain in CVS." % (turquoise(self._old_catpkg)))
+
+    def __add_new_package(self):
+
+        def add_files(arg, dir, files):
+
+           (null, null, null, dirs) = dir.split("/", 3)
+       
+            if os.path.basename(dir) not in self._ignore:
+               os.chdir(os.path.join(self._portdir, self._new_category))
+                if os.path.basename(dir) != self._new_package:
+                    print "      >>> %s/" % dirs   
+                    os.mkdir(dirs)
+                   do_cmd("%s %s" % (self._cvscmd["add"], dirs))
+                for file in files:
+                    if not os.path.isdir(os.path.join(dir, file)):
+                        print "      >>> %s" % os.path.join(dirs, file)
+                        do_cmd("cp %s %s" % (os.path.join(dir, file), dirs))
+                        do_cmd("%s %s" % (self._cvscmd["add"], os.path.join(dirs, file)))
+
+        print "%s Adding %s to CVS:" % (green("   *"), turquoise(self._new_catpkg))
+        os.chdir(os.path.join(self._portdir, self._new_category))
+        print "      >>> %s/" % self._new_package
+        os.mkdir(self._new_package)
+        do_cmd("%s %s" % (self._cvscmd["add"], self._new_package))
+        os.chdir(self._new_package)
+        os.path.walk("/tmp/%s/%s" % (__productname__, self._new_package), add_files , None)
+        os.chdir(os.path.join(self._portdir, self._new_catpkg))
+
+        print "%s Adding ChangeLog entry..." % green("     *")
+        do_cmd("echangelog 'Moved from %s to %s.'" % (self._old_catpkg, self._new_catpkg))
+
+        print "%s Commiting changes..." % green("     *")
+       do_cmd("%s 'Moved from %s to %s.'" % (self._cvscmd["commit"], self._old_catpkg, self._new_catpkg))
+
+        print "%s %s successfully added to CVS." % (green("   *"), turquoise(self._new_catpkg))
+
+    def log_move(self):
+
+        if not self._action == "REMOVE":
+       
+           update_files = []
+            update_regex = "^[\d]Q-[\d]{4}$"
+
+            p_file_regex = re.compile(update_regex)
+
+            print "%s Logging move:" % (green("   *"))
+            os.chdir(os.path.join(self._portdir, "profiles/updates"))
+            do_cmd(self._cvscmd["update"])
+
+            for file in os.listdir("."):
+                o_file_regex = p_file_regex.match(file)
+                if file not in self._ignore and o_file_regex:
+                           (q, y) = file.split("-")
+                    update_files.append("%s-%s" % (y, q))
+
+           update_files.sort()
+           (y, q) = update_files[-1].split("-")
+           upfile = "%s-%s" % (q, y)
+           print "      >>> %s" % upfile
+
+            try:
+               fd = open(upfile, "a")
+               fd.write("move %s %s\n" % (self._old_catpkg, self._new_catpkg))
+                fd.close()
+           except IOError, e:
+               error(e)
+               sys.exit(1)
+
+           do_cmd("%s 'Moved %s to %s'" % (self._cvscmd["commit"], self._old_catpkg, self._new_catpkg))
+           os.chdir(self._portdir)
+
+    def clean_up(self):
+
+        if not self._action == "REMOVE":
+            print "%s Removing back-up..." % (green("   *"))
+            do_cmd("rm -rf /tmp/%s/%s" % (__productname__, self._old_package))
+            if len(os.listdir("/tmp/%s" % __productname__)) == 0:
+                do_cmd("rmdir /tmp/%s" % __productname__)
+
+       os.chdir(self._portdir)
+           
+if __name__ == "__main__":
+    
+    signal.signal(signal.SIGINT, signal_handler)
+
+    cvscmd = {"remove": "cvs -Qf rm",
+             "commit": "cvs -Qf commit -m",
+             "update": "cvs -Qf up -dPC",
+             "add":    "cvs -Qf add"}
+
+    parser = OptionParser(usage="%prog [ option ] [ origin ] [ destination ]", version="%s-%s" % (__productname__, __version__))
+    parser.add_option("--usage", action="store_true", dest="usage", default=False, help="Pint usage information")
+    parser.add_option("-q", "--queue-check", action="store_true", dest="commit_queue", default=False, help="Check the cvs tree for files awaiting commit")
+    parser.add_option("-u", "--no-cvs-up", action="store_true", dest="cvs_up", default=False, help="Skip running cvs up in the origin and destination categories")
+    parser.add_option("-c", "--no-countdown", action="store_true", dest="countdown", default=False, help="Skip countdown before performing")
+    parser.add_option("-R", "--remove", action="store_true", dest="remove", default=False, help="Remove package")
+    parser.add_option("-F", "--force", action="store_true", dest="force", default=False, help="Force removal of package, ignoring any reverse deps")
+    (options, args) = parser.parse_args()
+
+    if options.usage:
+        print_usage()
+       sys.exit(0)
+
+    if randint(1, 100) == 50:
+        print "%s I put on my robe and wizard hat..." % green(" *")
+    portdir = check_cwd(portage.settings["PORTDIR"].rstrip("/"))
+    check_repos(portdir)
+    (oldcatpkg, newcatpkg) = check_args(args, portdir, options, cvscmd)
+    
+    if options.commit_queue:
+        check_commit_queue()
+
+    ThisPackage = CVSAbstraction(portdir, oldcatpkg, newcatpkg, cvscmd, options)
+
+    ThisPackage.perform_action()
+    ThisPackage.log_move()
+    ThisPackage.clean_up()
+
+    if options.remove:
+        print "%s %s successfully removed from CVS." % (green(" *"), turquoise(oldcatpkg))
+    else:
+        print "%s %s successfully moved to %s." % (green(" *"), turquoise(oldcatpkg), yellow(newcatpkg))
+
diff --git a/deprecated/etc-update/AUTHORS b/deprecated/etc-update/AUTHORS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/etc-update/ChangeLog b/deprecated/etc-update/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/etc-update/Makefile b/deprecated/etc-update/Makefile
new file mode 100644 (file)
index 0000000..95838ad
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+       echo "PAPPLE (vb.) To do what babies do to soup with their spoons."
+
+dist:
+       mkdir -p ../../$(distdir)/src/etc-update
+       cp Makefile AUTHORS README TODO ChangeLog etc-update etc-update.1 ../../$(distdir)/src/etc-update/
+
+install:
+       install -m 0755 etc-update $(bindir)/
+       install -d $(docdir)/etc-update
+       install -m 0644 AUTHORS ChangeLog README $(docdir)/etc-update/
+       install -m 0644 etc-update.1 $(mandir)/
diff --git a/deprecated/etc-update/README b/deprecated/etc-update/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/etc-update/etc-update b/deprecated/etc-update/etc-update
new file mode 100755 (executable)
index 0000000..f566dff
--- /dev/null
@@ -0,0 +1,165 @@
+#! /usr/bin/python
+#
+# $Header$
+#
+# Distributed under the terms of the GNU General Public License v2
+# Copyright (c) 2003 Karl Trygve Kalleberg
+#
+# Based on previous versions, by
+#  - Brandon Low <lostlogic@gentoo.org>
+#  - Jochem Kossen <j.kossen@home.nl>
+#  - Leo Lipelis <aeoo@gentoo.org>
+
+from dialog import Dialog
+import portage
+import time
+import re
+import os
+
+__author__ = "Karl Trygve Kalleberg"
+__email__ = "karltk@gentoo.org"
+__version__ = "0.2.0"
+__productname__ = "etc-update"
+__description__ = "Interactive config file updater"
+
+globals = portage.settings.configdict["globals"]
+
+for i in globals["CONFIG_PROTECT"].split():
+    print i
+
+# list all files in all CONFIG_PROTECT dirs
+# list them in the gui
+# one-by-one:
+#  - is update to header only? 
+#  - is the original unmodified from the previous package? (not checkable - duh!)
+#  - 
+
+class Config:
+    pass
+
+def loadConfig():
+    cfg = Config()
+    globals = portage.settings.configdict["globals"]
+    cfg.config_protect = globals["CONFIG_PROTECT"].split()
+    return cfg
+
+def _recurseFiles(path):
+    files = []
+    if os.path.exists(path):
+        try:
+            tmpfiles = os.listdir(path)
+            for i in tmpfiles:
+                fn = path + "/" + i
+                if os.path.isdir(fn):
+                    files += _recurseFiles(fn)
+                elif os.path.isfile(fn):
+                    m = re.search("\._cfg...._",fn)
+                    if m:
+                        files.append(fn)
+                else:
+                    print "What is this anyway?:", fn
+        except OSError:
+            pass
+            # print "Access denied:", path
+            
+    return files
+                
+def findAllFiles(dlg, config):
+    files = []
+    gauge = dlg.gauge(0,
+                      "Processing CONFIG_PROTECT directories...",
+                      7,
+                      60,
+                      "Gauge",
+                      sleep=3)
+    num_dirs = len(config.config_protect)
+    for i in xrange(num_dirs):
+        rem = repr(num_dirs - i / num_dirs)
+        gauge.update(rem, "Directories remaining: %s" % rem)
+        files += _recurseFiles(config.config_protect[i])
+    return files
+
+def prettifyFiles(files):
+    rx = re.compile("\._cfg...._")
+    def strip_cfg(x):
+        """Remove ._cfg????_ part """
+        m = rx.search(x)
+        if m:
+            s,e = m.span(0)
+            return x[:s] + x[e:]
+        return x
+    return map(strip_cfg, files)
+
+def updateFile(dlg, original):
+
+    # Find candidates
+
+    dir = os.path.dirname(original)
+    filename = os.path.basename(original)
+    rx = re.compile("\._cfg...._" + filename)
+    cand = filter(lambda x: rx.search(x), os.listdir(dir))
+
+    if len(cand) > 1:
+        
+        # Add mtimes
+        for i in xrange(len(cand)):
+            stamp = time.localtime(os.path.getmtime(dir + "/" + cand[i]))
+            tstr = time.strftime("%a, %d %b %Y %H:%M:%S", stamp)
+            cand[i] = cand[i] + " - " + tstr
+
+    
+        # Show selection
+        replacement = dlg.menu("Files that need updating",
+                          Dialog.AUTO_SIZE,
+                          list = cand,
+                          showHelp = False,
+                          title="Checklist")
+
+    else:
+        replacement = cand[0]
+
+    
+    # Display diff
+
+    dlg.yesno("Would you like to update \n" + \
+              "[" + original + "]with\n[" + dir + "/" + replacement + "] ?")
+    
+def displayFiles(dlg, config, files):
+
+    pretty_files = prettifyFiles(files)
+
+    while 1:
+        result = dlg.menu("Files that need updating",
+                          Dialog.AUTO_SIZE,
+                          list = pretty_files,
+                          showHelp = False,
+                          title="Checklist")
+        if result == None:
+            if dlg.ERR_CANCEL:
+                break
+
+        updateFile(dlg, result)
+        
+    if len(pretty_files):
+        print "!!! Warning: There are still files that require updating."
+        
+    
+def main():
+    dlg = Dialog("etc-update")
+#    dlg = None
+    
+    config = loadConfig()
+    files = findAllFiles(dlg, config)
+    displayFiles(dlg, config,files)
+    
+    
+if __name__ == "__main__":
+    try:
+        main()
+    except KeyboardInterrupt:
+        print "Operation aborted!"
+
+# TODO:
+# - option for automatically update untouched files
+# - show coloured diff
+# - proper progress bar
diff --git a/deprecated/etc-update/etc-update.1 b/deprecated/etc-update/etc-update.1
new file mode 100644 (file)
index 0000000..53477d8
--- /dev/null
@@ -0,0 +1,12 @@
+.TH etc-update "1" "Nov 2003" "gentoolkit"
+.SH NAME
+etc-update \- Gentoo: Configuration Update Utility
+.SH SYNOPSIS
+.B etc-update
+.SH BUGS
+This tool does not yet have a man page. Feel free to submit a bug about it to
+http://bugs.gentoo.org
+.SH AUTHORS
+This informative man page was written by Karl Trygve Kalleberg 
+<karltk@gentoo.org>.
+
diff --git a/deprecated/etcat/AUTHORS b/deprecated/etcat/AUTHORS
new file mode 100644 (file)
index 0000000..5da0b07
--- /dev/null
@@ -0,0 +1,5 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Authors:
+Alastair Tse <liquidx@gentoo.org> (original author)
diff --git a/deprecated/etcat/ChangeLog b/deprecated/etcat/ChangeLog
new file mode 100644 (file)
index 0000000..bdadcb6
--- /dev/null
@@ -0,0 +1,36 @@
+2004-04-20 Marius Mauch <genone@gentoo.org>
+       - fixing -u behavior so it matches equery (bug #47690)
+
+2004-03-13 Marius Mauch <genone@gentoo.org>
+       - grouping version in --version output
+
+2004-01-23 Marius Mauch <genone@gentoo.org>
+       - now catches exceptions thrown by portage
+       - minor bugfixes
+       
+2004-01-07 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added man page from app-portage/gentoolkit
+       * Added Makefile
+       * Added sys.path workaround for Portage >=2.0.50
+       
+* etcat-0.2.0 (06 May 2003)
+  06 May 2003; Alastair Tse <liquidx@gentoo.org>
+  Trying to add a dependency graph feature. kind of works with
+  some weird hacks parsing the depenency string. probably needs
+  to use functions in portage to parse it properly.
+
+* etcat-0.1.4 (27 Apr 2003)
+
+  27 Apr 2003; Alastair Tse <liquidx@gentoo.org>
+  Added "files", "belongs", "depends" functions.
+
+*etcat-0.1.3 (24 Apr 2003)
+
+  24 Apr 2003; Alastair Tse <liquidx@gentoo.org>
+  Fixed bug if ebuild doesn't exist. Added manpage.
+
+*etcat-0.1.2 (29 Mar 2003)
+
+  29 Mar 2003; Alastair Tse <liquidx@gentoo.org>
+  Initial commit to gentoolkit. Check source for previous changes
+   
diff --git a/deprecated/etcat/Makefile b/deprecated/etcat/Makefile
new file mode 100644 (file)
index 0000000..2281646
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+
+dist:
+       mkdir -p ../../$(distdir)/src/etcat
+       cp Makefile AUTHORS README TODO ChangeLog etcat etcat.1 ../../$(distdir)/src/etcat
+
+install:
+       install -d $(docdir)/deprecated/etcat
+       install -m 0755 etcat $(docdir)/deprecated/etcat/
+       install -m 0644 etcat.1 README AUTHORS $(docdir)/deprecated/etcat/
diff --git a/deprecated/etcat/README b/deprecated/etcat/README
new file mode 100644 (file)
index 0000000..50bd2f3
--- /dev/null
@@ -0,0 +1,2 @@
+For more information, this tool was originally hosted on:
+http://www.liquidx.net/projects/etcat/
diff --git a/deprecated/etcat/TODO b/deprecated/etcat/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/etcat/etcat b/deprecated/etcat/etcat
new file mode 100755 (executable)
index 0000000..5137683
--- /dev/null
@@ -0,0 +1,688 @@
+#!/usr/bin/env python2
+#
+# -*- mode: python; -*-
+#
+# --| Version Information |------------------------------------------
+# 
+#  etcat v0.1.4 (27 Apr 2003)
+#
+#  $Header$
+#
+# --| About |--------------------------------------------------------
+#
+#  etcat is a Portage/Ebuild Information Extractor. Basically, it
+#  provides higher level convienence functions to the Portage system
+#  used by Gentoo Linux. 
+#  
+#  You can use it to quickly find out the recent changes of your
+#  favourite package, the size of installed packages, the
+#  available versions for a package and more.
+#
+# --| License |------------------------------------------------------
+#
+#  Distributed under the terms of the GNU General Public License v2
+#  Copyright (c) 2002 Alastair Tse.
+#
+# --| Usage |--------------------------------------------------------
+#
+#  etcat [options] <command> <package[-ver]|ebuild|category/package[-ver]>
+#
+#  -b/belongs  ) checks what package(s) a file belongs to
+#  -c/changes  ) list the more recent changelog entry
+#  -d/depends  ) list all those that have this in their depends
+#  -f/files    ) list all files that belong to this package
+#  -g/graph    ) graph dependencies
+#  -s/size      ) guesses the size of a installed packaged.
+#  -u/uses      ) list all the use variables used in this package/ebuild
+#  -v/versions ) list all the versions available for a package
+#
+# --| TODO |---------------------------------------------------------
+# 
+# - in ver_cmp: 1.2.10a < 1.2.10, need to fix that
+# 
+# --| Changes |------------------------------------------------------
+#
+#  * etcat-0.3.1 (10 Oct 2004) [karltk]
+#      - Fixed changes help examples
+#  * etcat-0.3.1 (08 Jan 2004) [genone]
+#      - adding missing python searchpath modification
+#      - fixing sort order
+#  * etcat-0.3.0 (12 Jul 2003) [karltk]
+#      - Refactored interesting stuff into the Gentoolkit module
+#  * etcat-0.2.0 (13 Jun 2003)
+#      - Updated "versions" with PORTAGE_OVERLAY detection
+#      - Added "graph" feature
+#  * etcat-0.1.5 (30 Apr 2003)
+#      - Fixed disappearing short opts. Oops.
+#  * etcat-0.1.4 (27 Apr 2003)
+#      - Cleaned up command execution code to provide a single place
+#        to specify functions
+#      - Added own custom wrapping print code.
+#      - Added "files" feature
+#      - Added "depends" feature
+#  * etcat-0.1.3 (24 Apr 2003)
+#      - Overhaul of commandline interpreter
+#      - Added "belongs" feature
+#      - Reimplemented "uses" to deal with IUSE more cleanly
+#        and sources use.local.desc
+#      - Natural Order Listings for version
+#  * etcat-0.1.2 (29 Mar 2003)
+#      - Added unstable indicator to complement masked
+#      - improved use flag listing
+#  * etcat-0.1.1 (21 Jan 2003)
+#      - Add package to versions listing even if it's not in
+#        the portage anymore (21 Jan 2003)
+#      - Fixed old ehack references (17 Jan 2003)
+#  * etcat-0.1 (31 Oct 2002)
+#      Initial Release;
+#
+# -------------------------------------------------------------------
+
+
+
+import os
+import sys
+import re
+import pprint
+import getopt
+import glob
+
+# portage and gentoolkit need special path modifications
+sys.path.insert(0, "/usr/lib/portage/pym")
+sys.path.insert(0, "/usr/lib/gentoolkit/pym")
+
+import gentoolkit
+from stat import *
+try:
+       from portage.output import *
+except ImportError:
+       from output import *
+
+__author__ = "Alastair Tse"
+__email__ = "liquidx@gentoo.org"
+__version__ = "0.3.1"
+__productname__ = "etcat"
+__description__ = "Portage Information Extractor"
+
+# .-------------------------------------------------------.
+# | Initialise Colour Settings                                                 |
+# `-------------------------------------------------------'
+if (not sys.stdout.isatty()) or (gentoolkit.settings["NOCOLOR"] in ["yes","true"]):
+       nocolor()
+
+# "option": ("shortcommand","desc",["example one", "example two"])
+options = {
+"belongs": \
+("b","Searches for a package that owns a specified file with an option to restrict the search space.",
+["etcat belongs /usr/bin/gimp media-gfx",
+ "etcat belongs /usr/lib/libjpeg.so media-*",
+ "etcat belongs /usr/lib/libmpeg.so"]),
+"changes": \
+("c","Outputs the changelog entry to screen. It is possible to give a version number along with the package name.",
+["etcat changes mozilla", 
+ "etcat changes =mozilla-1.1-r1", 
+ "etcat changes gkrellm$"]),
+"depends": \
+("d","Finds all packages that are directly dependent to a regex search string.",
+["etcat depends 'gnome-base/libgnome'", 
+ "etcat depends '>=dev-lang/python-2.2'"]),
+"files": \
+("f","Lists files that belongs to a package and optionally with version.",[]),
+"graph": \
+("g","Graphs Dependencies (NON WORKING)",[]),
+"size": \
+("s","Lists the installed size of a package.",[]),
+"uses": \
+("u", "Advanced output of USE vars in a package. Tells you flags used by a package at time of installation, flags in current config and flag description.",[]),
+"versions": \
+("v","Displays the versions available for a specific package. Colour coded to indicate installation status and displays slot information.",
+[turquoise("(I)") + "nstalled", 
+ yellow("(~)") + "Unstable Testing Branch", 
+ red("(M)") + "asked Package"])
+}
+
+# .-------------------------------------------------------.
+# | Small Wrapping Printer with Indent Support                 |
+# `-------------------------------------------------------'
+
+def wrap_print(string, indent=0, width=74):
+       line_len = width - indent
+       str_len = len(string)
+       lines = []
+       
+       pos = 0
+       thisline = ""
+       while pos < str_len:
+               # if we still have space stuff the
+               # character in this line
+               if len(thisline) < line_len-1:
+                       thisline += string[pos]
+                       pos += 1
+               # if we're at the end of the line,
+               # check if we should hyphenate or
+               # append
+               elif len(thisline) == line_len -1:
+                       # end of a text
+                       if pos == str_len -1:
+                               thisline += string[pos]
+                               pos += 1
+                       # end of a word
+                       elif string[pos] != " " and string[pos+1] == " ":
+                               thisline += string[pos]         
+                               pos += 1
+                       # just a space
+                       elif string[pos] == " ":
+                               thisline += string[pos]                         
+                               pos += 1
+                       # start of a word, we start the word on the next line
+                       elif pos>0 and string[pos-1] == " ":
+                               thisline += " "
+                       # needs hyphenating
+                       else:
+                               thisline += "-"
+                               
+                       # append the line
+                       lines.append(thisline)
+                       thisline = ""
+
+       # append last line
+       if thisline:
+               lines.append(thisline)
+                       
+       for line in lines:
+               print " "*indent + line
+
+# +-------------------------------------------------------+
+# | Pretty Print Log                                                                     |
+# +-------------------------------------------------------+
+# | Extracts and prints out the log entry corresponding   |
+# | to a certain revision if given. If not supplied,     |
+# | prints out the latest/topmost entry                                   |
+# `-------------------------------------------------------'    
+       
+# output the latest entry in changelog
+def output_log(lines, package_ver=""):
+       # using simple rules that all changelog entries have a "*"
+       # as the first char
+       is_log = 0
+       is_printed = 0
+
+       for line in lines:
+               if package_ver:
+                       start_entry = re.search("^[\s]*\*[\s]*(" + package_ver + ")[\s]+.*(\(.*\))",line)
+               else:
+                       start_entry = re.search("^[\s]*\*[\s]*(.*)[\s]+.*(\(.*\))",line)
+               if not is_log and start_entry:
+                       is_printed = 1
+                       is_log = 1
+                       print green("*") + "  " + white(start_entry.group(1)) + "  " + turquoise(start_entry.group(2)) + " :"
+               elif is_log and re.search("^[\s]*\*[\s]*(.*)[\s]+.*(\(.*\))",line):
+                       break
+               elif is_log:
+                       print line.rstrip()
+               else:
+                       pass
+               
+       return is_printed
+
+# .-------------------------------------------------------.
+# | Changes Function                                                                     |
+# +-------------------------------------------------------+
+# | Print out the ChangeLog entry for package[-version]   |
+# `-------------------------------------------------------'
+
+def changes(query, matches):
+       if not report_matches(query,matches,installed_only=0):
+               return
+
+       for pkg in matches:
+               changelog_file = pkg.get_package_path() + "/ChangeLog"
+               if os.path.exists(changelog_file):
+                       output_log(open(changelog_file).readlines(), pkg.get_name()+"-"+pkg.get_version())
+               else:
+                       print red("Error") + ": No Changelog for " + pkg.get_cpv()
+
+
+# .-------------------------------------------------------.
+# | Versions Function                                                                   |
+# +-------------------------------------------------------+
+# | Prints out the available version, masked status and   |
+# | installed status.                                                                   |
+# `-------------------------------------------------------'            
+               
+def versions(query, matches):
+       # this function should also report masked packages
+       matches = gentoolkit.find_packages(query, masked=True)
+       if not report_matches(query,matches):
+               return
+
+       # sorting result list
+       matches = gentoolkit.sort_package_list(matches)
+               
+       # FIXME: old version printed result of regex search on name,
+       # so "ant" would return app-emacs/semantic, etc...
+       
+       last_cp = ""
+
+       for pkg in matches:
+               new_cp = pkg.get_category()+"/"+pkg.get_name()
+               if last_cp != new_cp:
+                       print green("*") + "  " + white(new_cp) + " :"
+               last_cp = new_cp
+                       
+               state = []
+               color = green
+               unstable = 0
+               overlay = ""
+                       
+               # check if masked
+               if pkg.is_masked():
+                       state.append(red("M"))
+                       color = red
+               else:
+                       state.append(" ")
+
+               # check if in unstable
+               kwd = pkg.get_env_var("KEYWORDS")
+               if "~" + gentoolkit.settings["ARCH"] in kwd.split():
+                       state.append(yellow("~"))
+                       if color != red:
+                               color = yellow
+                       unstable = 1
+               else:
+                       state.append(" ")
+                               
+               # check if installed
+               if pkg.is_installed():
+                       state.append(turquoise("I"))
+                       color = turquoise
+               else:
+                       state.append(" ")
+
+               # check if this is a OVERLAY ebuilds
+               if pkg.is_overlay():
+                       overlay = " OVERLAY"
+
+               ver = pkg.get_version()
+               slot = pkg.get_env_var("SLOT")
+               print " "*8 + "[" + "".join(state) + "] " + color(ver) + " (" + color(slot) + ")" + overlay
+
+# .-------------------------------------------------------.
+# | List USE flags for a single ebuild, if it's installed |
+# +-------------------------------------------------------+
+# | Just uses the new IUSE parameter in ebuilds                   |
+# `-------------------------------------------------------' 
+def uses(query, matches):
+       useflags = gentoolkit.settings["USE"].split()   
+       usedesc = {}
+       uselocaldesc = {}
+
+       # Load global USE flag descriptions
+       try:
+               fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.desc")
+               usedesc = {}
+               for line in fd.readlines():
+                       if line[0] == "#":
+                               continue
+                       fields = line.split(" - ")
+                       if len(fields) == 2:
+                               usedesc[fields[0].strip()] = fields[1].strip()
+       except IOError:
+               pass
+
+       # Load local USE flag descriptions
+       try:
+               fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.local.desc")
+               for line in fd.readlines():
+                       if line[0] == "#":
+                               continue
+                       fields = line.split(" - ")
+                       if len(fields) == 2:
+                               catpkguse = re.search("([a-z]+-[a-z]+/.*):(.*)", fields[0])
+                               if catpkguse:
+                                       if not uselocaldesc.has_key(catpkguse.group(1).strip()):
+                                               uselocaldesc[catpkguse.group(1).strip()] = {catpkguse.group(2).strip() : fields[1].strip()}
+                                       else:
+                                               uselocaldesc[catpkguse.group(1).strip()][catpkguse.group(2).strip()] = fields[1].strip()
+       except IOError:
+               pass
+               
+       print "[ Colour Code : " + green("set") + " " + red("unset") + " ]"
+       print "[ Legend   : (U) Col 1 - Current USE flags        ]"
+       print "[          : (I) Col 2 - Installed With USE flags ]"
+
+       if filter(gentoolkit.Package.is_installed, matches):
+               only_installed = True
+       else:
+               only_installed = False
+
+       # Iterate through matches, printing a report for each package
+       for p in matches:
+               if not p.is_installed() and only_installed:
+                       continue
+       
+               bestver = p.get_cpv()
+               iuse = p.get_env_var("IUSE")
+               
+               if iuse: usevar = iuse.split()
+               else: usevar = []
+               
+               inuse = []
+               used = p.get_use_flags().split()
+
+               # store (inuse, inused, flag, desc)
+               output = []
+
+               for u in usevar:
+                       inuse = 0
+                       inused = 0
+                       try:
+                               desc = usedesc[u]
+                       except KeyError:
+                               try:
+                                       desc = uselocaldesc[p.get_category()+"/"+p.get_name()][u]
+                               except KeyError:
+                                       desc = ""
+
+                       if u in p.get_settings("USE"): inuse = 1
+                       if u in used: inused = 1
+                               
+                       output.append((inuse, inused, u, desc))
+
+               # pretty print
+               if output:
+                       print
+                       print white(" U I ") + "[ Found these USE variables in : " + white(bestver) + " ]"
+                       maxflag_len = 0
+                       for inuse, inused, u, desc in output:
+                               if len(u) > maxflag_len:
+                                       maxflag_len = len(u)
+               
+                       for inuse, inused, u, desc in output:
+                               flag = ["-","+"]
+                               colour = [red, green]
+                               if inuse != inused:
+                                       print yellow(" %s %s" % (flag[inuse], flag[inused])),
+                               else:
+                                       print " %s %s" % (flag[inuse], flag[inused]),
+
+                               print colour[inuse](u.ljust(maxflag_len)),
+                       
+                               # print description
+                               if desc:
+                                       print ":", desc
+                               else:
+                                       print ": unknown"
+               else:
+                       print "[ No USE flags found for :", white(p.get_cpv()), "]"             
+
+       return
+
+# .-------------------------------------------------------.
+# | Graphs the Dependency Tree for a package                     |
+# +-------------------------------------------------------+
+# | Naive graphing of dependencies
+# `-------------------------------------------------------'
+
+def graph(query, matches):
+       if not report_matches(query, matches):
+               return
+
+       for pkg in matches:
+               if not pkg.is_installed():
+                       continue
+               rgraph(pkg)
+
+def rgraph(pkg,level=0,pkgtbl=[],suffix=""):
+
+       cpv=pkg.get_cpv()
+
+       print level*" " + "`-- " + cpv + suffix
+       pkgtbl.append(cpv)
+       
+       for x in pkg.get_runtime_deps():
+               suffix=""
+               cpv=x[2]
+               pkg=gentoolkit.find_best_match(x[0] + cpv)
+               if not pkg:
+                       continue
+               if pkg.get_cpv() in pkgtbl:
+                       continue
+               if cpv.find("virtual")==0:
+                       suffix+=" (" + cpv + ")"
+               if len(x[1]):
+                       suffix+=" [ " + "".join(x[1]) + " ]"
+               pkgtbl=rgraph(pkg,level+1,pkgtbl,suffix)
+       return pkgtbl
+
+# .-------------------------------------------------------.
+# | Required By Function                                                                 |
+# +-------------------------------------------------------+
+# | Find what packages require a given package name       |
+# `-------------------------------------------------------'
+
+def depends(query, matches):
+       
+       print "[ Results for search key : " + white(query) + " ]"
+
+       isdepend = gentoolkit.split_package_name(query)
+       
+       for pkg in matches:
+               if pkg.is_installed():
+                       deps = pkg.get_runtime_deps()
+                       for x in deps:
+                               cpvs=gentoolkit.split_package_name(x[2])
+                               cat_match=0
+                               ver_match=0
+                               name_match=0
+                               if not isdepend[0] or isdepend[0] == cpvs[0]:
+                                       cat_match=1
+                               if not isdepend[2] or \
+                                  (isdepend[2] == cpvs[2] and isdepend[3] == cpvs[3]):
+                                       ver_match=1
+                               if isdepend[1] == cpvs[1]:
+                                       name_match=1
+                               if cat_match and ver_match and name_match:
+                                       print turquoise("*"), white(pkg.get_cpv()), white("[ ") + "".join(x[1]), white("]")
+
+# .-------------------------------------------------------.
+# | Belongs to which package                                                     |
+# +-------------------------------------------------------+
+# | Finds what package a file belongs to                                 |
+# `-------------------------------------------------------'                    
+                       
+def belongs(query,matches):
+
+       q = query.split()
+
+       if len(q) > 1:
+               item=q[0]
+               cat=q[1]
+               fn=lambda x: x.find(cat)==0
+       else:
+               item=q[0]
+               cat="*"
+               fn=None
+       matches = gentoolkit.find_all_installed_packages(fn)
+
+       print "Searching for " + item + " in " + cat + " ..."
+
+       rx = re.compile(item)
+
+       for pkg in matches:
+               if pkg.get_contents():
+                       for fn in pkg.get_contents().keys():
+                               if rx.search(fn):
+                                       print pkg.get_cpv()
+                                       break # We know this pkg matches, look for any more matches
+       return
+               
+# .-------------------------------------------------------.
+# | Size of all packages matching query                                   |
+# +-------------------------------------------------------+
+# | Finds the size of installed packages                                 |
+# `-------------------------------------------------------'                    
+def size(query,packages):
+       packages = gentoolkit.find_packages(query)
+       if not report_matches(query, packages):
+               return
+
+       for pkg in packages:
+               if not pkg.is_installed():
+                       continue
+               x=pkg.size()
+               size=x[0]
+               files=x[1]
+               uncounted=x[2]
+               print turquoise("*") + " " + white(pkg.get_cpv())
+               print " Total Files : ".rjust(25) + str(files)
+               if uncounted:
+                       print " Inaccessible Files : ".rjust(25) + str(uncounted)
+               print " Total Size : ".rjust(25) + "%.2f KB" % (size/1024.0)
+
+
+def report_matches(query, matches, installed_only=1):
+       print "[ Results for search key    : " + white(query) + " ]"
+       print "[ Candidate applications found : " + white(str(len(matches))) + " ]"
+       print
+
+       if installed_only and matches:
+               print " Only printing found installed programs."
+               print
+       elif installed_only:
+               print "No packages found."
+
+       if matches:
+               return 1
+       else:
+               return 0
+
+
+# .-------------------------------------------------------.
+# | Files in a package                                                                 |
+# +-------------------------------------------------------+
+# | Lists all the files in a package                                     |
+# `-------------------------------------------------------'                    
+def files(query,matches):
+       if not report_matches(query, matches):
+               return
+       
+       for package in matches:
+               if not package.is_installed():
+                       continue
+               contents = package.get_contents()
+               
+               print yellow(" * ") + white(package.get_cpv())
+               for x in contents.keys():
+                       t = contents[x][0]
+                       if t == "obj":
+                               print x
+                       elif t == "sym":
+                               print turquoise(x)
+                       elif t == "dir":
+                               print blue(x)
+                       else:
+                               print x
+
+# .-------------------------------------------------------.
+# | Help Function                                                                               |
+# `-------------------------------------------------------'
+def ver():
+       print __productname__ + " (" + __version__ + ") - " + __description__ + " - By: " + __author__
+
+def help():
+       screenwidth = 74
+       margin = 2
+       margin_desc = 4
+       margin_ex = 8
+       
+       ver()
+       print yellow("NOTICE: ") + "This tool will be phased out at some point in"
+       print "        the future, please use equery instead."
+       print "        Bugs are still fixed, but new features won't be added."
+       print
+       print white("Usage: ") + turquoise(__productname__) + " [ " + green("options") + " ] [ " + turquoise("action") + " ] [ " + turquoise("package") + " ]"
+       print
+       print turquoise("Actions:")
+       print
+       for name,tup in options.items():
+               print " "*margin + green(name) + " (" + green("-" + tup[0]) + " short option)"
+               wrap_print(tup[1],indent=margin_desc)
+               for example in tup[2]:
+                       print " "*margin_ex + example
+               print
+
+# .-------------------------------------------------------.
+# | Main Function                                                                               |
+# `-------------------------------------------------------'
+def main():
+       
+       action = ''
+       query = ''
+       
+       if len(sys.argv) < 3:
+               help()
+               sys.exit(1)
+               
+       # delegates the commandline stuff to functions
+       pointer = 2
+       # short/long opts mapping
+       shortopts = ["-"+x[0] for x in options.values()]
+       short2long = {}
+       for k,v in options.items():
+               short2long[v[0]] = k
+       longopts = options.keys()
+       # loop thru arguments
+       for arg in sys.argv[1:]:
+               if arg[0] == "-" and len(arg) == 2 and arg in shortopts:
+                       action = short2long[arg[1]]
+                       query = ' '.join(sys.argv[pointer:])
+                       break
+               elif arg in longopts:
+                       action = arg
+                       query = ' '.join(sys.argv[pointer:])
+                       break
+               else:
+                       pointer += 1
+                       
+       # abort if we don't have an action or query string
+       if not query or action not in options.keys():
+               help()
+               sys.exit(1)
+       else:
+               try:
+                       matches = gentoolkit.find_packages(query)
+               except KeyError, e:
+                       if e[0].find("Specific key requires operator") == 0:
+                               print red("!!!"), "Invalid syntax: missing operator"
+                               print red("!!!"), "If you want only specific versions please use one of"
+                               print red("!!!"), "the following operators as prefix for the package name:"
+                               print red("!!!"), "   >  >=  =  <=  <"
+                               print red("!!!"), "Example to only match gcc versions greater or equal 3.2:"
+                               print red("!!!"), "   >=sys-devel/gcc-3.2"
+                       else:
+                               print red("!!!"), "Internal portage error, terminating"
+                               if len(e[0]):
+                                       print red("!!!"), e
+                       sys.exit(2)
+               except ValueError, e:
+                       if isinstance(e[0],list):
+                               print red("!!!"), "Ambiguous package name \"%s\"" % query
+                               print red("!!!"), "Please use one of the following long names:"
+                               for p in e[0]:
+                                       print red("!!!"), "    "+p
+                       else:
+                               print red("!!!"), "Internal portage error, terminating"
+                               if len(e[0]):
+                                       print red("!!!"), e[0]
+                       sys.exit(2)
+               function = globals()[action]
+               function(query, matches)
+       
+if __name__ == "__main__":
+       try:
+               main()
+       except KeyboardInterrupt:
+               print "Operation Aborted!"
+
+
diff --git a/deprecated/etcat/etcat.1 b/deprecated/etcat/etcat.1
new file mode 100644 (file)
index 0000000..6704f3b
--- /dev/null
@@ -0,0 +1,79 @@
+.TH "etcat" "1" "0.1.4" "Alastair Tse <liquidx@gentoo.org>" "Gentoo Administration"
+.SH "NAME"
+.LP 
+etcat \- Gentoo: Portage Information Extractor
+.SH "SYNTAX"
+.LP 
+etcat [\fIoption\fP|command] <\fIquery|package\fP>
+
+.SH "DESCRIPTION"
+.LP 
+etcat tries to complement the existing portage related tools but geared specifically for the power user. It enables users and developers to quickly find out information on particular packages without resorting to manually poking the portage tree and portage database.
+
+.LP 
+More specifically, it lists the versions available highlighted by their status (stable/testing/masked/install) for a package, lists use flags per package with descriptions, calculates the installed size of a package and also outputs the relevent entries in the changelog related to the package version.
+
+.LP 
+It also employes a smarter package query syntax than emerge where examples such as:
+.LP .IP 
+mozilla\-1.1
+.br 
+mozilla\-1.*
+.LP 
+are accepted.
+
+.SH "OPTIONS"
+.LP 
+\fB\-b\fR <\fI/path/to/file\fR> [\fIcategory\fR]
+.br 
+\fBbelongs\fR <\fI/path/to/file\fR> [\fIcategory\fR]
+.IP 
+Searches for the package which a file belongs to with an option to restrict a search to a single or multiple category. Wildcards in the category name is accepted to speed up searching. (eg. etcat belongs /usr/lib/libmpeg.so "media\-*")
+
+.LP 
+\fB\-c\fR <\fIpackage[\-version]\fR>
+.br 
+\fBchanges\fR <\fIpackage[\-version]\fR>
+.IP 
+Outputs ChangeLog entry for the package and version specified. Uses the latest package version if none specified.
+
+.LP 
+\fB\-d\fR <\fIregex expression\fR>
+.br 
+\fBdepends\fR <\fIregex expression\fR>
+.IP 
+Searches through portage for a dependency string satisfying that regular expression.
+
+.LP 
+\fB\-f\fR <\fIpackage[\-version]\fR>
+.br 
+\fBfiles\fR <\fIpackage[\-version]\fR>
+.IP 
+Lists all the files installed for this package.
+
+.LP 
+\fB\-s\fR <\fIpackage\fR>
+.br 
+\fBsize\fR <\fIpackage\fR>
+.IP 
+Outputs the installed size of the package.
+
+.LP 
+\fB\-u\fR <\fIpackage[\-version]\fR>
+.br 
+\fBuses\fR <\fIpackage[\-version]\fR>
+.IP 
+Outputs the USE flags supported by this package and also their installed state and description.
+
+.LP 
+\fB\-v\fR <\fIpackage\fR>
+.br 
+\fBversions\fR <\fIpackage\fR>
+.IP 
+Output all the versions for packages that match the \fIpackage\fR name given with indication of whether the packages is stable, masked, unstable or installed.
+.SH "SEE ALSO"
+.LP 
+Type \fBetcat\fR for examples and more information
+.SH "AUTHORS"
+.LP 
+Alastair Tse <liquidx@gentoo.org>
diff --git a/deprecated/genpkgindex/Makefile b/deprecated/genpkgindex/Makefile
new file mode 100644 (file)
index 0000000..9f0ea2c
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+       echo "ABWONG (AB-wong vb.) To bounce cheerfully on a bed."
+
+dist:
+       mkdir -p ../../$(distdir)/src/genpkgindex
+       cp Makefile genpkgindex genpkgindex.1 ../../$(distdir)/src/genpkgindex/
+
+install:
+       install -m 0755 genpkgindex $(bindir)/
+       install -m 0644 genpkgindex.1 $(mandir)/
diff --git a/deprecated/genpkgindex/genpkgindex b/deprecated/genpkgindex/genpkgindex
new file mode 100644 (file)
index 0000000..c079b83
--- /dev/null
@@ -0,0 +1,336 @@
+#!/usr/bin/python
+# Copyright 2006-2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+
+import os
+import stat
+import sys
+import time
+import getopt
+from stat import *
+
+if getattr(__builtins__, "set", None) is None:
+       from sets import Set as set
+
+for x in ['CFLAGS','CXXFLAGS', 'LDFLAGS','USE']:
+       os.environ[x] = ''
+del x
+
+os.environ["USE_EXPAND"] = "-*"
+
+import portage
+
+try:
+       import portage.xpak as xpak
+       import portage.checksum as portage_checksum
+       import portage.dep as portage_dep
+       import portage.util as portage_util
+       import portage.const as portage_const
+except ImportError:
+       import xpak
+       import portage_checksum
+       import portage_dep
+       import portage_util
+       import portage_const
+
+compress = bool(os.environ.get("COMPRESSPKGFILE", ''))
+pkgdir = portage.settings["PKGDIR"]
+opt_args_short="hqvcP:"
+opt_args_long=["help", "quiet", "verbose", "compress", "pkgdir"]
+quiet = False
+verbose = False
+
+def usage():
+       print portage.output.green("Usage:")+"\t"+portage.output.yellow("genpkgindex")+" -"+portage.output.blue("["+opt_args_short+"]")
+       print portage.output.white(" Options:")+"  --"+" --".join(opt_args_long)
+       sys.exit(1)
+
+def update_pkgdir():
+       if not os.path.exists(portage.settings["PKGDIR"]+"/All"):
+               return
+
+       os.chdir(portage.settings["PKGDIR"]+"/All")
+       for x in os.listdir("."):
+               pkg = os.path.basename(x)
+               if pkg[-5:] != ".tbz2":
+                       continue
+
+               mode = os.lstat(pkg)[ST_MODE]
+               if not S_ISREG(mode):
+                       if S_ISLNK(mode):
+                               if not os.path.exists(os.readlink(x)):
+                                       if verbose:
+                                               portage.writemsg(portage.output.yellow(" * ")+"Removing broken symlink: "+x+"\n")
+                                       os.unlink(x)
+                       continue
+               tbz2 = xpak.tbz2(pkg)
+               data = tbz2.getboth()
+               cat = xpak.getitem(data, "CATEGORY")
+               cat = cat[:-1]
+               if not os.path.exists("../"+cat):
+                       os.mkdir("../"+cat)
+               if os.path.exists("../"+ cat + "/" + pkg):
+                       os.unlink("../"+ cat + "/" + pkg)
+               os.rename(pkg, "../"+ cat + "/" + pkg)
+               os.symlink("../"+ cat + "/"+ pkg, pkg)
+
+def grabpkgnames(dirp):
+       names = []
+       categories = portage.grabfile(portage.settings["PORTDIR"]+"/profiles/categories")
+       os.chdir(dirp)
+       for cat in os.listdir('.'):
+               if cat in categories:
+                       for pkg in os.listdir(cat):
+                               if os.path.basename(pkg).endswith("tbz2"):
+                                       names.append(cat+"/"+pkg)
+       names.sort()
+       return names
+
+def cleanxfiles(dirp):
+       global verbose
+       # Clean up stale cache files
+       os.chdir(portage_const.CACHE_PATH+"/xpak")
+       for pkg in os.listdir('.'):
+               p = os.path.basename(pkg)
+               if not p.endswith(".xpak"):
+                       continue
+               tbz2 = xpak.tbz2(p)
+               stuff = tbz2.getboth()
+               cat = xpak.getitem(stuff, "CATEGORY")
+               if not os.path.exists(dirp + "/" + cat[:-1] + "/" + p[:-5] + ".tbz2"):
+                       # tidy up
+                       if verbose:
+                               portage.writemsg(portage.output.yellow(" * ") + "Stale entry: " + dirp + "/" + cat[:-1] + "/" + p[:-5] + ".tbz2\n")
+                       os.unlink(p)
+                       os.unlink(p[:-5]+".md5")
+
+def cleanpkgdir():
+       if os.path.exists("/usr/bin/eclean"):
+               os.system("/usr/bin/eclean -d packages")
+
+
+def parseargs():
+       global pkgdir
+       global compress
+       global verbose
+       global quiet
+
+       if portage.settings.get("NOCOLOR") not in ("yes","true"):
+               portage.output.havecolor = 1
+       else:
+               portage.output.havecolor = 0
+
+       # Parse the cmdline.
+       try:
+               opts, args = getopt.getopt(sys.argv[1:], opt_args_short, opt_args_long)
+       except getopt.GetoptError:
+               usage()
+               sys.exit(2)
+
+       for opt, optarg in opts:
+               if opt in ("-v", "verbose"):
+                       verbose = True
+               if opt in ("-h", "--help"):
+                       usage()
+               if opt in ("-c", "--compress"):
+                       compress = True
+               if opt in ("-q", "--quiet"):
+                       quiet = True
+               if opt in ("-P", "--pkgdir"):
+                       pkgdir = optarg
+
+       if "cleanpkgdir" in portage.settings["FEATURES"]:
+               cleanpkgdir()
+
+def serialize_depset(src, context='and'):
+       l = []
+       if not src:
+               return ''
+       if isinstance(src, basestring):
+               return src
+       i = iter(src)
+       for x in i:
+               if isinstance(x, basestring):
+                       if x != '||':
+                               l.append(x)
+                               continue
+                       x = i.next()
+                       if len(x) == 1:
+                               l.append(serialize_depset(x[0]))
+                       else:
+                               l.append("|| ( %s )" % serialize_depset(x))
+               else:
+                       # and block.
+                       if context == 'and':
+                               v = serialize_depset(x, context=context)
+                               if v.strip():
+                                       l.append(v)
+                       else:
+                               v = serialize_depset(x, context='or')
+                               if v.strip():
+                                       l.append("( %s )" % v.strip())
+       return ' '.join(l)
+
+def getallpkgs():
+       packages = []
+       os.chdir(pkgdir)
+       for pkg in grabpkgnames(pkgdir):
+
+               st = os.stat(pkg)
+
+               if not os.path.exists(portage_const.CACHE_PATH+"/xpak/"):
+                       os.mkdir(portage_const.CACHE_PATH+"/xpak/")
+
+               fname = portage_const.CACHE_PATH+"/xpak/"+os.path.basename(pkg)[:-5]+".xpak"
+
+               if os.path.exists(fname):
+                       if st.st_mtime != os.stat(fname).st_mtime:
+                               #print "unlinking "+fname
+                               os.unlink(fname)
+
+               if not os.path.exists(fname):
+                       tbz2 = xpak.tbz2(pkg)
+                       xpdata = xpak.xpak_mem(tbz2.get_data())
+                       fp = open(fname, "w")
+                       fp.write(xpdata+xpak.encodeint(len(xpdata))+"STOP")
+                       fp.close()
+
+                       chksum = portage_checksum.perform_md5(pkg)
+                       fp = open(fname[:-5]+".md5", "w")
+                       fp.write(chksum)
+                       fp.close()
+
+                       os.utime(fname, (st.st_mtime, st.st_mtime))
+
+               else:
+                       if os.path.exists(fname[:-5]+".md5"):
+                               chksum = "".join(portage.grabfile(fname[:-5]+".md5"))
+                       else:
+                               chksum = portage_checksum.perform_md5(pkg)
+
+                       tbz2 = xpak.tbz2(fname)
+
+               packages.append((pkg, tbz2, chksum, st))
+       return packages
+
+def genpkgindex_header(fp, packages):
+       import re
+       profilever = os.path.normpath("///"+os.readlink("/etc/make.profile"))
+       basepath = os.path.normpath("///"+portage.settings["PORTDIR"]+"/profiles")
+       if re.match(basepath,profilever):
+               profilever = profilever[len(basepath)+1:]
+       else:
+               profilever = "!"+profilever
+       del basepath
+
+       timestamp = str(time.time()).split(".")[0]
+       fp.write("# This file was auto generated by " + os.path.basename(sys.argv[0]) + "\n")
+       if pkgdir == portage.settings["PKGDIR"]:
+               fp.write("PROFILE: "+profilever+"\n")
+               fp.write("PACKAGES: "+str(len(packages)) +"\n")
+               fp.write("TIMESTAMP: "+timestamp+"\n")
+
+               vmask = [ "AUTOCLEAN", "DISTDIR", "PKGDIR", "PORTDIR" , "PORTAGE_TMPDIR" , "PORTAGE_RSYNC_OPTS" ]
+               variables = portage_util.grabfile(portage.settings["PORTDIR"]+"/profiles/info_vars")
+               variables = [v for v in variables if v not in vmask]
+               variables.sort()
+
+               for var in variables:
+                       if var in portage.settings:
+                               if (len(portage.settings[var])):
+                                       fp.write(var+": "+portage.settings[var]+"\n")
+       else:
+               fp.write("PACKAGES: "+str(len(packages)) +"\n")
+               fp.write("TIMESTAMP: "+timestamp+"\n")
+       fp.write("\n")
+
+def genpkgindex(packages):
+       os.chdir(pkgdir)
+       control_file = ".Packages"
+       fp = open(control_file, "w")
+       genpkgindex_header(fp, packages)
+
+       for pkg, tbz2, chksum, st in packages:
+               stuff = tbz2.getboth()
+               if not stuff:
+                       print "Not a tbz2: "+str(pkg)
+                       continue
+
+               cat = xpak.getitem(stuff, "CATEGORY")
+
+               use = xpak.getitem(stuff, "USE")
+               if use is None:
+                       use = ''
+               iuse = xpak.getitem(stuff, "IUSE")
+               if iuse is None:
+                       iuse = ''
+
+               s = xpak.getitem(stuff, "DESCRIPTION")
+               if s is not None:
+                       s = ' '.join(s.split())
+                       if s:
+                               fp.write("DESC: %s\n" % s)
+               # drop '.tbz2'
+               fp.write("CPV: %s/%s\n" % (cat.strip(), os.path.basename(pkg[:-5])))
+               s = xpak.getitem(stuff, "SLOT")
+               if s is not None:
+                       s = ' '.join(s.split())
+                       if s and s != "0":
+                               fp.write("SLOT: %s\n" % s)
+
+               split_use = use.split()
+               for name in ("LICENSE", "RDEPEND", "PDEPEND", "PROVIDE"):
+                       item = xpak.getitem(stuff, name)
+                       if item is None:
+                               continue
+                       val = portage_dep.use_reduce(portage_dep.paren_reduce(' '.join(item.split())), uselist=split_use)
+                       if val:
+                               fp.write("%s: %s\n" % (name, serialize_depset(val)))
+
+               # map IUSE->USE and look for matching flags, filter dupes
+               # if both flags match then this is what matters.
+               s = set(split_use).intersection(iuse.split())
+               if s:
+                       l = list(s)
+                       l.sort()
+                       fp.write("USE: %s\n" % ' '.join(l))
+
+               fp.write("SIZE: "+ str(st[stat.ST_SIZE]) +"\n")
+               fp.write("MD5: "+chksum+"\n")
+               fp.write("\n")
+
+       fp.write("\n")
+       fp.flush()
+       fp.close()
+
+       if (compress):
+               os.system("bzip2 < .Packages > .Packages.bz2")
+               os.rename(".Packages.bz2", "Packages.bz2")
+       else:
+               if os.path.exists("Packages.bz2"):
+                       os.unlink("Packages.bz2")
+
+       os.rename(".Packages", "Packages")
+
+
+def main():
+       update_pkgdir()
+
+       parseargs()
+
+       if not quiet:
+               portage.writemsg(portage.output.green(' * ')+'Update binary package index %s/Packages\n' % pkgdir);
+
+       start = time.time()
+       packages = getallpkgs()
+       genpkgindex(packages)
+       cleanxfiles(pkgdir)
+       finish = time.time()
+
+       if not quiet:
+               portage.writemsg(portage.output.green(' * ')+"PKGDIR contains "+ str(len(packages)) + ' packages. (%.01fsec)\n' % (finish - start));
+
+
+if __name__ == "__main__":
+       main()
diff --git a/deprecated/genpkgindex/genpkgindex.1 b/deprecated/genpkgindex/genpkgindex.1
new file mode 100644 (file)
index 0000000..8a3956e
--- /dev/null
@@ -0,0 +1,59 @@
+.TH "genpkgindex" "1" "" "Ned Ludd" "gentoolkit"
+.SH "NAME"
+.LP 
+genpkgindex \- Generates package metadata from binary packages for use with programs such a qmerge from portage\-utils
+.SH "USAGE"
+.LP 
+genpkgindex [\fI\-\-options\fP]
+
+.SH "DESCRIPTION"
+.LP 
+Generates package metadata from binary packages for use with programs such a qmerge from portage\-utils
+.SH "OPTIONS"
+.LP 
+.TP
+\fB\-h, \-\-help\fR
+ Display help and exit
+.TP 
+\fB\-v, \-\-verbose\fR
+ Be verbose
+.TP 
+\fB\-q, \-\-quiet\fR
+ Be quiet
+.TP 
+\fB\-c, \-\-compress\fR
+ Compresses the generated metadata with bzip2.
+.TP 
+\fB\-P, \-\-pkgdir <path>\fR
+ Set the base location of the binary packages.  The default is normally /usr/portage/packages
+.TP 
+
+.SH "ENVIRONMENT VARIABLES"
+.LP 
+.TP 
+\fBPKGDIR\fP
+is the location of binary packages that you can have created with FEATURES=buildpkg, '\-\-buildpkg' or '\-b/\-B' while emerging a package.
+.SH "EXAMPLES"
+.LP 
+Typical usage is to simply run:
+.LP 
+genpkgindex
+.LP
+Alternatively if you want the metadata compressed:
+.LP 
+genpkgindex \-\-compress
+.LP
+.SH "NOTES"
+.LP 
+When no package directories are directly given to genpkgindex on the command line it will output additional variables that it assumes from the running portage environment.
+.LP
+When FEATURES=cleanpkgdir is enabled genpkgindex will invoke "/usr/bin/eclean \-d packages" before creating any package metadata.
+.LP
+genpkgindex intended use is to be run from /etc/portage/bashrc in the $EBUILD_PHASE of "postinst".
+.LP
+.SH "AUTHORS"
+.LP 
+Ned Ludd <solar@gentoo.org>
+.SH "SEE ALSO"
+.LP 
+emerge(1) qmerge(1) make.conf(5) portage(5) 
diff --git a/deprecated/gensync/AUTHORS b/deprecated/gensync/AUTHORS
new file mode 100644 (file)
index 0000000..389c51b
--- /dev/null
@@ -0,0 +1,5 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Original Author:
+Matthew Schick <matt@breakmygentoo.net>
diff --git a/deprecated/gensync/ChangeLog b/deprecated/gensync/ChangeLog
new file mode 100644 (file)
index 0000000..7ec2b86
--- /dev/null
@@ -0,0 +1,12 @@
+2004-09-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Fixed portage import, fixes #45079.
+       * Added all sources sync patch from Adam Ashley
+        <adam_ashley@softhome.net>, fixes #45079.
+       * Fixed config dir scanning, suggested by burrelln@colorado.edu,
+       fixes #47390.
+       * Applied config file parser fix by Mamoru KOMACHI <usata@gentoo.org>,
+       fixes #44777.
+
+2004-02-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Initial import
+
diff --git a/deprecated/gensync/Makefile b/deprecated/gensync/Makefile
new file mode 100644 (file)
index 0000000..d5b879a
--- /dev/null
@@ -0,0 +1,24 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+.PHONY: all
+all: 
+
+dist: 
+       mkdir -p ../../$(distdir)/src/gensync/
+       cp Makefile AUTHORS README TODO ChangeLog \
+          gensync gensync.1 gensync.conf \
+          bmg-main.syncsource bmg-gnome-current.syncsource \
+           ../../$(distdir)/src/gensync/
+
+install: all
+       install -d $(docdir)/deprecated/gensync
+       install -m 0755 gensync $(docdir)/deprecated/gensync/
+       install -m 0644 AUTHORS README TODO ChangeLog $(docdir)/deprecated/gensync/
+       install -m 0644 gensync.1 $(docdir)/deprecated/gensync/
+       install -m 0644 gensync.conf bmg-main.syncsource bmg-gnome-current.syncsource $(docdir)/deprecated/gensync/
diff --git a/deprecated/gensync/README b/deprecated/gensync/README
new file mode 100644 (file)
index 0000000..cf54dec
--- /dev/null
@@ -0,0 +1,16 @@
+
+Package : gensync
+Version : 0.1.0
+Author  : See AUTHORS
+
+MOTIVATION
+
+Gensync handles rsyncing to multiple sync sources. 
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+N/A
diff --git a/deprecated/gensync/TODO b/deprecated/gensync/TODO
new file mode 100644 (file)
index 0000000..733a959
--- /dev/null
@@ -0,0 +1,4 @@
+- Add using cvs to sync
+- Update portage cache after sync
+- Add overlay dirs defined in /etc/conf.d/gensync to /etc/make.conf
+- Sync up with Marius on the multiple overlay code
diff --git a/deprecated/gensync/bmg-gnome-current.syncsource b/deprecated/gensync/bmg-gnome-current.syncsource
new file mode 100644 (file)
index 0000000..51dd908
--- /dev/null
@@ -0,0 +1,18 @@
+
+# This id must be unique among all the ids in /etc/gensync/*.syncsource
+id="bmg-gnome-current"
+
+# This is a human-readable description of the source
+description="BreakMyGentoo GNOME current"
+
+# The rsync url
+rsync="rsync://rsync.breakmygentoo.net/gnome-current"
+
+# By default, the overlay directory is set to ${base_overlay}/${id}, 
+# where base_overlay is picked from /etc/gensync/gensync.conf
+# 
+# You may specify a different relative path, such as 
+overlay="bmg-gnome-current.alternative"
+#
+# Or an entirely new absolute path (remeber to create the path first)
+#overlay="/my/absolute/path"
diff --git a/deprecated/gensync/bmg-main.syncsource b/deprecated/gensync/bmg-main.syncsource
new file mode 100644 (file)
index 0000000..8d74ca2
--- /dev/null
@@ -0,0 +1,18 @@
+
+# This id must be unique among all the ids in /etc/gensync/*.syncsource
+id="bmg-main"
+
+# This is a human-readable description of the source
+description="BreakMyGentoo main tree"
+
+# The rsync url
+rsync="rsync://rsync.breakmygentoo.net/bmg-overlay"
+
+# By default, the overlay directory is set to ${base_overlay}/${id}, 
+# where base_overlay is picked from /etc/gensync/gensync.conf
+# 
+# You may specify a different relative path, such as 
+overlay="bmg-main.alternative"
+#
+# Or an entirely new absolute path (remeber to create the path first)
+#overlay="/my/absolute/path"
diff --git a/deprecated/gensync/gensync b/deprecated/gensync/gensync
new file mode 100755 (executable)
index 0000000..52f25ed
--- /dev/null
@@ -0,0 +1,226 @@
+#!/usr/bin/python
+#
+# Simple program to rsync from various 3rd party ebuild servers. 
+#
+#
+# Copyright (c) 2004, Matthew Schick <matt@breakmygentoo.net> (original author)
+# Copyright (c) 2004, Gentoo Technologies, Inc
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+#
+# Distributed under the terms of the GNU General Public License v2
+
+__author__ = "Matt Schick <matt@breakmygentoo.net>, Karl Trygve Kalleberg <karltk@gentoo.org>"
+__email__ = "karltk@gentoo.org"
+__version__ = "0.1.0"
+__productname__ = "gensync"
+__description__ = "Gentoo Overlay Sync Tool"
+
+
+import sys
+import os
+import fileinput
+
+sys.path.insert(0, "/usr/lib/portage/pym")
+import portage
+
+try:
+       from portage.output import *
+except ImportError:
+       from output import *
+
+class ConfigDefaults:
+       def __init__(self):
+               self.rsync_timeout = 0
+               self.base_overlay = "/usr/local/overlays"
+               self.confdir = '/etc/gensync'
+               self._init_from_file()
+               self._setup_rsync()
+       def _init_from_file(self):
+               try:
+                       ins = open("/etc/gensync/gensync.conf")
+                       for x in ins.readlines():
+                               # Skip line if it's a comment or not well-formed
+                               if x.find("=") == -1 or \
+                                      (len(x) and x[0] == "#"):
+                                       continue
+                               (attrib, value) = x.split('=')
+                               attrib = attrib.strip().strip('"')
+                               value = value.strip().strip('"')
+                               if attrib == "rsync_timeout":
+                                       self.rsync_timeout = value
+                               elif attrib == "base_overlay":
+                                       self.base_overlay = value
+               except:
+                       pass
+
+       def _setup_rsync(self):
+               if self.rsync_timeout == 0:
+                       try:
+                               self.rsync_timeout = portage.settings["RSYNC_TIMEOUT"]
+                       except:
+                               self.rsync_timeout = 180
+               self.rsync_command = "/usr/bin/rsync " + \
+                                    "-rlptDvz --progress " + \
+                                    "--delete --delete-after " + \
+                                    "--timeout=" + str(self.rsync_timeout) + " " \
+                                    "--exclude='distfiles/*' " + \
+                                    "--exclude='local/*' " + \
+                                    "--exclude='packages/*' "
+
+Config = ConfigDefaults()
+
+class SyncSource:
+       """Contains all details about an upstream rsync source."""
+       def __init__(self,filename):
+               self.id = "#unset#"
+               self.name = "#unset#"
+               self.url = "#unset#"
+               self.overlay = "#unset#"
+               self._init_from_file(filename)
+
+               if self.overlay == "#unset#":
+                       self.overlay = Config.base_overlay + "/" + self.id
+               elif len(self.overlay) and self.overlay[0] != "/":
+                       self.overlay = Config.base_overlay + "/" + self.overlay
+               elif len(self.overlay) and self.overlay[0] == "/":
+                       pass
+               else:
+                       print "Malformed overlay, '" + self.overlay + "'"
+                       sys.exit(-2)
+
+       def _init_from_file(self, filename):
+               """Loads configuration settings from a .syncsource file."""
+               ins = open(filename)
+               for x in ins.readlines():
+                       # Skip line if it's a comment or not well-formed
+                       if x.find("=") == -1 or \
+                              (len(x) and x[0] == "#"):
+                               continue
+                       (attrib, value) = x.split("=")
+                       attrib = attrib.strip().strip('"')
+                       value = value.strip().strip('"')
+                       if attrib == "id":
+                               self.id = value
+                       elif attrib == "description":
+                               self.name = value
+                       elif attrib == "rsync":
+                               self.url = value
+                       elif attrib == "overlay":
+                               self.overlay = value
+       def perform_sync(self):
+               """Syncs with upstream source."""
+               cmd = Config.rsync_command + self.url +"/* " + self.overlay
+               exitcode = portage.spawn(cmd, portage.settings, free=1)
+
+       def dump(self,ous=sys.stdout):
+               ous.write("id     =\"%s\"\n" % (self.id) + \
+                         "name   =\"%s\"\n" % (self.name) + \
+                         "rsync  =\"%s\"\n" % (self.url) + \
+                         "overlay=\"%s\"\n\n" % (self.overlay))
+
+class SyncSourceManager:
+       """Holds information on all known upstream rsync sources."""
+       def __init__(self):
+               self.sources = []
+               self._load_source_info()
+       def _load_source_info(self):
+               for x in os.listdir(Config.confdir):
+                       if x.rfind(".syncsource") > 0 and \
+                               x.endswith(".syncsource"):
+                               ss = SyncSource(Config.confdir + "/" + x)
+                               self.sources.append(ss)
+       def list_sources(self):
+               for x in self.sources:
+                       x.dump()
+       def get_sync_source(self, source_id):
+               for x in self.sources:
+                       if x.id == source_id:
+                               return x
+               return None
+       def get_all_sync_sources(self):
+               return self.sources
+               
+def print_version():
+       print __productname__ + "(" + __version__ + ") - " + \
+                 __description__
+       print "Author(s): " + __author__
+
+def print_usage():
+       print white("Usage: ") + turquoise(__productname__) + \
+             yellow(" <options> ") + green("repo-id-1 repo-id-2 ...")
+       print "where " + yellow("<options>") + " is one of"
+       print yellow(" -a, --all-sources") + "  - sync all know sources"
+       print yellow(" -l, --list-sources") + " - list known rsync sources"
+       print yellow(" -C, --no-color") + "      - turn off colours"
+       print yellow(" -h, --help") + "         - this help screen"
+       print yellow(" -V, --version") + "      - display version info"
+
+
+def parse_args(cliargs):
+
+       cmd = "sync-sources"
+       args = []
+       options = []
+       
+       for x in cliargs:
+               if x in ["-V", "--version"]:
+                       cmd = "print-version"
+                       break
+               elif x in ["-h", "--help"]:
+                       cmd = "print-usage"
+                       break
+               elif x in ["-C", "--no-color"]:
+                       options.append("nocolor")
+                       break
+               elif x in ["-l", "--list-sources"]:
+                       cmd = "list-sources"
+               elif x in ["-a", "--all-sources"]:
+                       cmd = "all-sources"
+               else:
+                       args.append(x)
+       return (cmd, args, options)
+
+def main():
+       
+       # Setup colors, as per system defaults
+       if (not sys.stdout.isatty()) or \
+              (portage.settings["NOCOLOR"] in ["yes","true"]):
+               nocolor()
+
+       # Parse arguments
+       (cmd, args, options) = parse_args(sys.argv[1:])
+
+       # Turn off color, if specified by cmdline switch
+       if "nocolor" in options:
+               nocolor()
+               
+       # Initialise sync source manager
+       ssmgr = SyncSourceManager()
+
+       # Perform selected command
+       if cmd == "list-sources":
+               ssmgr.list_sources()
+       elif cmd == "print-version":
+               print_version()
+       elif cmd == "print-usage":
+               print_usage()
+       elif cmd == "sync-sources":
+               for x in args:
+                       repo = ssmgr.get_sync_source(x)
+                       if repo:
+                               print "Syncing '%s' into '%s'" % \
+                                     (repo.id, repo.overlay)
+                               repo.perform_sync()
+       elif cmd == "all-sources":
+               for repo in ssmgr.get_all_sync_sources():
+                       print "Syncing '%s' into '%s'" % \
+                             (repo.id, repo.overlay)
+                       repo.perform_sync()
+       else:
+               print red("Unknown command '" + cmd + "'")
+
+if __name__ == "__main__":
+       try:
+               main()
+       except KeyboardInterrupt:
+               print "Operation aborted by user keypress!"
diff --git a/deprecated/gensync/gensync.1 b/deprecated/gensync/gensync.1
new file mode 100644 (file)
index 0000000..17d85fc
--- /dev/null
@@ -0,0 +1,75 @@
+.TH gensync 1 "0.1.0" "Gentoolkit" "Gentoo Administration"
+.SH "NAME"
+.LP 
+gensync \- Gentoo: Overlay Sync Tool
+.SH "SYNTAX"
+.LP 
+gensync [\fIoption\fP] <\fIrepo-id-1 repo-id-2 ...\fP>
+
+.SH "DESCRIPTION" 
+
+.LP
+
+\fIgensync\fR synchronises your machine against an upstream repository
+of ebuild files, called a sync source. Normally, Portage will only
+synchronise against the main Portage tree maintained by the Gentoo
+project. However, there exist development trees with auxiliary ebuild
+files that may occasionally be of interest to the adventurous power
+user, such as the \fIbreakmygentoo.net\fR sync source.
+
+This tool can be used to synchronise against these alternative sync
+sources.
+
+.SH "OPTIONS"
+.LP 
+\fB\-l\fR
+.br
+\fB--list-sources\fB
+.IP 
+List available sync sources known to \fIgensync\fR.
+
+.LP 
+\fB\-V\fR
+.br
+\fB--version\fB
+.IP 
+Display version information and exit.
+
+.LP 
+\fB\-C\fR
+.br
+\fB--no-color\fB
+.IP 
+Turn off colors.
+
+.LP 
+\fB\-h\fR
+.br 
+\fB\--help\fR
+.IP 
+Display help.
+
+.SH "CONFIGURATION FILES"
+.LP
+\fB/etc/gensync/gensync.conf\fR
+.IP
+The main configuration file, commented and self-explanatory
+
+.LP
+\fB/etc/gensync/*.syncsource\fR
+.IP
+Per-sync source configuration files, describing the sync source to
+\fIgensync\fR.
+
+
+.SH "SEE ALSO"
+.LP 
+The rest of the utilities in \fIapp-portage/gentoolkit-dev\fR, such as
+\fIechangelog\fR, \fIebump\fR and \fIego\fR.
+
+.SH "AUTHORS"
+.LP 
+Matthew Schick <matt@breakmygentoo.net> (original author)
+.br
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
diff --git a/deprecated/gensync/gensync.conf b/deprecated/gensync/gensync.conf
new file mode 100644 (file)
index 0000000..389e020
--- /dev/null
@@ -0,0 +1,8 @@
+
+# By default, we ask Portage about the preferred timeout
+#rsync_timeout = 180
+
+# Normally, each sync source will be placed under 
+# ${base_overlay}/${syncsource-id}, where the 'syncsource-id' is a unique
+# identifier, specified inside the individual .syncsource file
+base_overlay = /usr/local/overlays
diff --git a/deprecated/lintool/AUTHORS b/deprecated/lintool/AUTHORS
new file mode 100644 (file)
index 0000000..fe436cb
--- /dev/null
@@ -0,0 +1 @@
+Karl Trygve Kalleberg <karltk@gentoo.org>
diff --git a/deprecated/lintool/COPYING b/deprecated/lintool/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/deprecated/lintool/ChangeLog b/deprecated/lintool/ChangeLog
new file mode 100644 (file)
index 0000000..0e8c1e9
--- /dev/null
@@ -0,0 +1,71 @@
+2004-02-18 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Moved from separate CVS module to be part of gentoolkit-dev
+       
+2002-10-31 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Fixed \$Header$ comment
+       * Fixed off-by-one in line numbering
+
+2002-10-30 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Now defaults to showing all details
+        * Added --no-details
+        * Better LICENSE parsing
+        * Better references to policy.
+        * Line numbers for whitespace testing.
+       * Released 0.2.4
+       
+2002-08-12 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added more header tests
+       * Added initial tests for SLOT, KEYWORD and LICENSE
+
+2002-08-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Spurious space checker works again.
+       * Added Makefile, COPYING, README, AUTHORS, NEWS
+
+2002-07-22 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Changed A=${P}.tar.gz check to ^A=.*, as per Seemant's suggestion.
+
+2002-07-17 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added TestLicense to ebuild.py
+       * Added --aux-license-dir=<dir>
+       * Added MunchieFormatter
+       * Added --formatter=<formatter>
+
+2002-07-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Refactored; split tests into changelog, digest and ebuild-specific
+       * Added TestSyntax test to digest.py
+       * Added TestHeaders, TestConstructPresence, TestIndentation to 
+       changelog.py
+       
+2002-05-15 Karl Trygve Kalleberg <karltk@gentoo.org>
+
+       * Fixed test for Author: and Maintainer: to warn about their
+       deprecation and recommend the use of ChangeLog instead.
+       * Added (E) and (W) prefixes to messages printed by --show-details
+       * Added --tests=-<particular test> to allow turning off a
+       particular test easily.
+       * Added statistics output at the bottom.
+       * Updated man page to reflect new option feature.
+       * Fixed the --show-details, --show-separate annoyance.
+       * Fixed use flags parsing to disregard some false positives.
+       
+2002-05-13 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Fixed --list-tests which was incorrectly --listTests
+
+2002-04-30 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added <jnelson@gentoo.org>'s cleanups
+       * Improved error reporting; now separates into errors and
+       warnings.
+       * Improved TestSpaces to check for non-conformant
+       line-continuations.
+       * Added TestUseFlags that checks that an ebuild only uses
+       sanctioned use flags (read from /usr/portage/profiles/use.desc)
+       * Added SLOT= to list desired env vars (warns if missing)
+       * Added LICENSE= to list of required env vars (errors if missing)
+       * Improved error messages
+       * Added --tests= option to allow the user to specify which tests
+       to run
+       * Added --list-tests option that lists available tests
+       * Updated man page.
+       
+2002-03-22 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added this ChangeLog
diff --git a/deprecated/lintool/NEWS b/deprecated/lintool/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/lintool/README b/deprecated/lintool/README
new file mode 100644 (file)
index 0000000..1494aad
--- /dev/null
@@ -0,0 +1,23 @@
+
+Package: Lintool
+Version: 0.1.3
+Author : Karl Trygve Kalleberg <karltk@gentoo.org>
+
+
+MOTIVATION
+
+Lintool is a "lint" utility for the Gentoo Linux distribution. It is a 
+tool that tests ebuilds, changelogs and package digests for 
+well-formedness. It's intended to aid Gentoo Linux developers avoid most 
+common mistakes when packaging software for Gentoo.
+
+MECHANICS
+
+To check an ebuild, do lintool --ebuild <path-to-ebuild>
+To check a ChangeLog, do lintool --changelog <path-to-changelog>
+To check a package digest, do lintoo --digest <path-to-digest>
+
+IMPROVEMENTS
+
+Any suggestions for improvements should be sent to karltk@gentoo.org, or
+added as a bug assigned to me.
diff --git a/deprecated/lintool/lintool.1 b/deprecated/lintool/lintool.1
new file mode 100644 (file)
index 0000000..11883e8
--- /dev/null
@@ -0,0 +1,41 @@
+.TH lintool "1" "March 2002" "gentoolkit 0.1.3" 
+.SH NAME
+lintool \- manual page for the lintool program, a program that checks the 
+santity of ebuild scripts.
+.SH SYNOPSIS
+.B lintool
+\fI<switches>\fR \fIebuilds...\fR
+.SH DESCRIPTION
+Lintool checks if a set of
+.I ebuilds
+conforms to the ebuild style guide. This is not
+(yet) an exhaustive test, so your ebuild might be broken even if lintool 
+thinks it's okay.
+.PP
+.SH OPTIONS 
+.TP
+\fB--no-summary\fI
+Turn off total summary for all tests run on all ebuilds.
+.TP
+\fB--show-separate\fI
+Show short summary of tests for each ebuild checked.
+.TP
+\fB--show-details\fI
+Show full details of tests for each ebuild checked
+.TP
+\fB--tests=test1,test2,..\fI
+Run only the tests specified to this option. Default is to run all
+tests. One can turn off a particular test by pretending it with minus (e.g.
+.I --tests=-TestUseFlags,-TestSpaces
+). A list of tests can be obtained from
+.I --list-tests
+.TP
+\fB--list-tests\fI
+List available tests.
+.SH AUTHORS
+Karl Trygve Kalleberg <karltk@gentoo.org>, 2002
+.SH "SEE ALSO"
+ebuild(5) 
+.TP
+The \fI/usr/sbin/lintool\fR script. 
+
diff --git a/deprecated/lintool/lintool.py b/deprecated/lintool/lintool.py
new file mode 100755 (executable)
index 0000000..721c744
--- /dev/null
@@ -0,0 +1,320 @@
+#! /usr/bin/python
+#
+# Copyright 2002 Gentoo Technologies, Inc
+# Distributed under the terms of the GNU General Public License v2.0
+# Author Karl Trygve Kalleberg <karltk@gentoo.org>
+#
+# About:
+# lintool aims to check the stylistic and syntactical correctness for
+# ebuilds, changelogs and digest files for the Gentoo packaging system.
+#
+# TODO
+#
+# - Make HTMLFormatter
+#
+
+VERSION="0.2.4"
+
+import sys
+import getopt
+
+from lintool import ebuild, changelog, digest
+
+class TextFormatter:
+  def section(self, s):
+    print "\n" + "-"*79
+    print " " + s + "\n"
+  def bullet(self, s):
+    print "* " + s
+  def sub(self, s):
+    print "- " + s
+  def subwarn(self, s):
+    print "- (W) " + s
+  def suberr(self, s):
+    print "- (E) " + s
+  def subsub(self, s):
+    print " |" + s
+  def subsubwarn(self, s):
+    print " (W) |" + s
+  def subsuberr(self, s):
+    print " (E) |" + s
+  def line(self,s):
+    print s
+  def div(self, left, right):
+    l = len(left)
+    r = len(right)
+    return left + " " * (78-l-r) + right
+
+class MunchieFormatter:
+  def section(self, s):
+    print "[lintool] " + "-" * (78 - len("[lintool] "))
+    print "[lintool] " + s + "\n"
+  def bullet(self, s):
+    print "[lintool] * " + s
+  def sub(self, s):
+    print "[lintool] - " + s
+  def subwarn(self, s):
+    print "[lintool] - (W) " + s
+  def suberr(self, s):
+    print "[lintool] - (E) " + s
+  def subsub(self, s):
+    print "[lintool] |" + s
+  def subsubwarn(self, s):
+    print "[lintool]  (W) |" + s
+  def subsuberr(self, s):
+    print "[lintool] (E) |" + s
+  def line(self,s):
+    print "[lintool] " + s
+  def div(self, left, right):
+    l = len("[lintool] " + left)
+    r = len(right)
+    return left + " " * (78-l-r) + right
+
+formatters = { "text" : TextFormatter(), "munchie" : MunchieFormatter()  }
+
+def extractFilename(path):
+  return path
+
+def runTests(tests,results,ins):
+  for j in tests:
+    j.reset()
+
+  ln = 1
+  for i in ins.readlines():
+    for j in tests:
+      j.checkLine(i, ln)
+    ln += 1
+
+  hasWarning = 0
+  hasError = 0
+  for j in xrange(len(tests)):
+    if tests[j].hasErrors():
+      results[j][0] += 1
+      hasError = 1
+    if tests[j].hasWarnings():
+      results[j][1] += 1
+      hasWarning = 1
+  return (hasError, hasWarning)
+        
+def showStatus(options,tests,formatter,file):
+  if options['showDetails'] or options['showSeparate']:
+    formatter.section("Status for " + file)
+    for j in tests:
+      if options['showSeparate'] or options['showDetails']:
+        l = len(j.getDesc())
+        formatter.bullet(formatter.div(j.getDesc(), ": " + j.getStatus()))
+        if options['showDetails']:
+          j.report()
+  elif options['showShort']:
+    allOK = 1
+    for j in tests:
+      if j.hasErrors():
+        allOK = 0
+        break
+    if allOK:
+      formatter.div(file, ": OK")
+    else:
+      formatter.div(file, ": Not OK")
+    # else fall through the bottom    
+    
+def usage(opts):
+  print sys.argv[0], "[options] ebuild [ebuild ebuild ... ]"
+  print
+
+  if opts:
+    print "Where [options] include:"
+    for (short,long_,desc) in opts:
+      short_ = ''
+      for s in short:
+        short_ = short_ + '-' + s + ','
+      long_ = '--' + long_
+      opt = short_ + long_
+      opt = opt.rjust(18)
+      print opt + '  ' + desc
+    print
+
+def parse_opts(argv):
+  options = { 'showSeparate': 0,
+              'showTotal': 1,
+              'showDetails': 1,
+              'showShort': 1,
+              'listTests': 0,
+              'desiredTests': 0,
+              'testMode' : "ebuild",
+              'licenseDirs' : [ "/usr/portage/licenses" ],
+              'formatter' : 'text'
+            }
+
+  opts = (('', 'show-separate',
+           'Show short summary of tests for each ebuild checked'),
+
+          ('v', 'version',
+           'Show program version'),
+
+          ('', 'no-summary',
+           'Do not show total summary'),
+
+          ('', 'no-details',
+           'Do not show full details of tests for each ebuild checked'),
+
+          ('', 'ebuild',
+           'Files to check are ebuilds'),
+          
+          ('', 'changelog',
+           'Files to check are changelogs'),
+
+          ('', 'digest',
+           'Files to check are digests'),
+
+          ('', 'tests=',
+           'Comma-separated list of tests to run'),
+
+          ('', 'list-tests',
+           'List available tests'),
+
+          ('', 'from-file=<file>',
+           'Read ebuilds from <file>'),
+
+          ('', 'formatter=<formatter>',
+           "Use 'text' (default) or 'munchie' formatter"),
+
+          ('', 'aux-license-dir=<dir>',
+           'Add <dir> to directories to search for licenses'),
+            
+          ('?h', 'help',
+           'Show this help'),
+         )
+
+  short_options = ''
+  long_options = []
+  for (short,long_,desc) in opts:
+    short_options = short_options + short
+    if '=' in long_:
+      long_ = long_.split('=', 1)[0] + '='
+    long_options.append(long_)
+
+  try:
+    (option_list,args) = getopt.getopt(sys.argv[1:], short_options, long_options)
+  except getopt.GetoptError, details:
+    print 'Error parsing command line:',str(details)
+    sys.exit(1)
+
+  for (option,value) in option_list:
+    if option in [ '--no-details' ]:
+      options['showShort'] = 1
+      options['showDetails'] = 0
+    elif option in [ '--show-separate' ]:
+      options['showShort'] = 0
+      options['showSeparate'] = 1
+    elif option in [ '--no-summary']:
+      options['showTotal'] = 0
+    elif option in [ '--from-file' ]:
+      lines = open(value, 'r').readlines()
+      lines = [o.strip() for o in lines]
+      args = lines + args
+    elif option in [ '--tests' ]:
+      options['desiredTests'] = value.split(",")
+    elif option in [ '--formatter' ]:
+      options['formatter'] = value
+    elif option in [ '--list-tests' ]:
+      options['listTests'] = 1
+    elif option in [ '--ebuild' ]:
+      options['testMode'] = 'ebuild'
+    elif option in [ '--changelog' ]:
+      options['testMode'] = 'changelog'
+    elif option in [ '--digest' ]:
+      options['testMode'] = 'digest'
+    elif option in [ '--aux-license-dir' ]:
+      options['licenseDirs'].append(value)
+    elif option in [ '-v', '--version' ]:
+      print "Lintool " + VERSION
+      sys.exit(0)
+    elif option in [ '-h', '-?', '--help' ]:
+      usage(opts)
+      sys.exit(0)
+    else:
+      # shouldn't ever happen. better to be safe
+      print "Unknown option - '%s'!" % (option)
+      sys.exit(1)
+
+  return (options,args)
+
+def main():
+    (options,args) = parse_opts(sys.argv[1:])
+
+    formatter = formatters[options['formatter']]
+
+    # Get test suite for given mode
+    if options['testMode'] == "ebuild":
+      available_tests = ebuild.getTests(formatter, options)
+    elif options['testMode'] == "changelog":
+      available_tests = changelog.getTests(formatter, options)
+    elif options['testMode'] == "digest":
+      available_tests = digest.getTests(formatter, options)
+
+    # List available tests, if that was the users request
+    if options['listTests']:
+      maxlen = 0
+      for i in available_tests:
+        maxlen = max(len(i.__class__.__name__), maxlen)
+      for i in available_tests:
+        n = i.__class__.__name__
+        print n + " " * (maxlen - len(n)) + " - " + i.getDesc()
+
+    # Quit with short usage string, if no params given
+    if len(args) == 0:
+      usage(None)
+      sys.exit(1)
+
+    # Create final list of tests to run
+    tests = []
+    notTests = []
+    if options['desiredTests']:
+      for i in options['desiredTests']:
+        for j in available_tests:
+          if len(i) and i[0] == "-":
+            notTests.append(i[1:])
+          if j.__class__.__name__ == i:
+            tests.append(j)
+    else:
+      tests = available_tests
+
+    if len(notTests):
+      for i in available_tests:
+        if i.__class__.__name__ not in notTests:
+          tests.append(i)
+      
+    results = [[0, 0] for x in range(len(tests))]
+
+    # Set up for test run
+    numFiles = 0
+    totalErrors = 0
+    totalWarnings = 0
+
+    # Iterate through all files given as arguments, testing each file
+    # against the final list of tests
+    for i in args:
+      fn = extractFilename(i)
+      ins = open(i, "r")
+      numFiles += 1
+      (hasError, hasWarning) = runTests(tests,results,ins)
+      totalErrors += hasError
+      totalWarnings += hasWarning
+      showStatus(options,tests,formatter,fn)
+
+    # Show totals, if options allow it
+    if options['showTotal']:
+      formatter.section(formatter.div("Summary for all " + str(numFiles) + " " + options['testMode'] + "(s) checked", "#errors/warns"))
+      for i in xrange(len(tests)):
+        l = len(tests[i].getDesc())
+        formatter.line(formatter.div(tests[i].getDesc(), ": %3d / %3d" % (results[i][0], results[i][1])))
+      formatter.line(formatter.div("Total number of ebuilds with errors",  \
+                                                   "%3d (%3d%%)" % (totalErrors, totalErrors*100/numFiles)))
+      formatter.line(formatter.div("Total number of ebuilds with warnings", \
+                                                  "%3d (%3d%%)" % (totalWarnings, totalWarnings*100/numFiles)))
+    if totalErrors:
+        sys.exit(1)
+
+if __name__ == "__main__":
+  main()
+
diff --git a/deprecated/lintool/lintool/__init__.py b/deprecated/lintool/lintool/__init__.py
new file mode 100644 (file)
index 0000000..4d2e3a6
--- /dev/null
@@ -0,0 +1,3 @@
+import ebuild
+import changelog
+import digest
diff --git a/deprecated/lintool/lintool/changelog.py b/deprecated/lintool/lintool/changelog.py
new file mode 100644 (file)
index 0000000..1dad779
--- /dev/null
@@ -0,0 +1,105 @@
+# Copyright 2002 Gentoo Technologies, Inc
+# Distributed under the terms of the GNU General Public License v2.0
+# Author Karl Trygve Kalleberg <karltk@gentoo.org>
+
+from test import Test, Regex
+import re
+
+class TestHeaders(Test):
+  def __init__(self, formatter,options):
+    Test.__init__(self,formatter,options)
+    self.desc = "Testing for malformed headers"
+    self.re = [ (1, # append result of regex match
+                 re.compile("^(# Copyright 1999-(2000|2001).*)"),
+                 "Suspect copyright year"), 
+                (1,
+                 re.compile("^(# /home.*)"),
+                 "Suspect path in header"),
+                (0, # don't append result of regex match
+                 re.compile("^(# Author.*)"),
+                 "Use of Author field in the header is deprecated. Put name in ChangeLog"),
+                (0,
+                 re.compile("^(# Maintainer.*)"),
+                 "Use of Maintainer field in the header is deprecated. Put name in ChangeLog"),
+                (1,
+                 re.compile("^(# /space.*)"),
+                 "Suspect path in header")]
+
+  def checkLine(self, s, ln):
+    for i in self.re:
+      k = i[1].match(s)
+      if k and i[0]:
+        self.warnings.append(i[2] + ": " + k.groups()[0] )
+      elif k and not i[0]:
+        self.warnings.append(i[2])
+        
+  def report(self):
+    if len(self.warnings):
+      self.formatter.subwarn("Has illegal or suspect headers:")
+      for i in self.warnings:
+        self.formatter.subsub(i)
+
+class TestConstructPresence(Test):
+
+  def __init__(self, formatter,options):
+    Test.__init__(self,formatter,options)
+    self.desc = "Testing for presence of required constructs"
+    self.required = [ ["# ChangeLog for " + Regex.category + "/" + Regex.PN,
+                       None,
+                       None,
+                       "proper ChangeLog line" 
+                      ],
+
+                      ["\*" + Regex.P + " \([0-9]+ [A-Z][a-z][a-z] 2002\).*",
+                       None,
+                       None,
+                       "proper release entry on the form *package-1.0.0 (01 Apr 2000)"
+                      ],
+
+                      ["  [0-9]+ [A-Z][a-z][a-z] 2002; .* <.*@.*> .*:",
+                       None,
+                       None,
+                       "proper changelog entry"
+                      ]
+                    ]
+
+    for i in self.required:
+      i[1] = re.compile("^(" + i[0] + ")")
+      
+  def checkLine(self, s, ln):
+    for i in self.required:
+      k = i[1].match(s)
+      if k:
+        i[2] = 1
+
+  def report(self):
+    for i in self.required:
+      if not i[2]:
+        self.formatter.suberr("Missing " + i[3])
+
+  def hasErrors(self):
+    for i in self.required:
+      if not i[2]:
+        return 1
+
+class TestIndentation(Test):
+
+  def __init__(self, formatter,options):
+    Test.__init__(self,formatter,options)
+    self.desc = "Testing for proper indentation"
+    self.re = re.compile("^(  |[#*].|\n).*")
+
+  def checkLine(self, s, ln):
+    k = self.re.match(s)
+    if not k:
+      self.errors.append(s)
+
+  def report(self):
+    for i in self.errors:
+      print i.replace(' ','%')
+
+def getTests(formatter,options):
+    return [ TestHeaders(formatter,options),
+             TestConstructPresence(formatter,options),
+             TestIndentation(formatter,options)
+             ]
diff --git a/deprecated/lintool/lintool/digest.py b/deprecated/lintool/lintool/digest.py
new file mode 100644 (file)
index 0000000..5f174ae
--- /dev/null
@@ -0,0 +1,28 @@
+# Copyright 2002 Gentoo Technologies, Inc
+# Distributed under the terms of the GNU General Public License v2.0
+# Author Karl Trygve Kalleberg <karltk@gentoo.org>
+
+from test import Test
+import re
+
+class TestSyntax(Test):
+
+  def __init__(self,formatter,options):
+    Test.__init__(self,formatter,options)
+    self.desc = "Testing for correct syntax"
+    self.re = [ re.compile("^(MD5 [a-z0-9]+ [a-zA-Z0-9_+.-]+ [0-9]+)") ]
+    self.errors = []
+    
+  def checkLine(self, s, ln):
+    for i in self.re:
+      k = i.match(s)
+      if not k:
+        self.errors.append("Invalid line in digest\n |" + s)
+        
+  def report(self):
+    for i in self.errors:
+      self.formatter.suberr(i)
+
+    
+def getTests(formatter,options):
+  return [ TestSyntax(formatter,options) ]
diff --git a/deprecated/lintool/lintool/ebuild.py b/deprecated/lintool/lintool/ebuild.py
new file mode 100644 (file)
index 0000000..c8df0a2
--- /dev/null
@@ -0,0 +1,349 @@
+# Copyright 2002 Gentoo Technologies, Inc
+# Distributed under the terms of the GNU General Public License v2.0
+# Author Karl Trygve Kalleberg <karltk@gentoo.org>
+
+from test import Test
+import re
+import os
+import os.path
+
+class TestSpaces(Test):
+
+  def __init__(self, formatter, options):
+    Test.__init__(self, formatter, options)
+    self.desc = "Testing for correct formatting"
+    self.re_spaces = [ re.compile("^([ ][ ]*)([a-zA-Z\.].*)"),
+                       re.compile("(.*)([ \t]+)\n") ]
+    self.re_backslash = re.compile("([^#]*\S)((\s\s+|\t))\\\\")
+    self.reset()
+    
+  def checkLine(self, s, ln):
+    for r in self.re_spaces:
+      k = r.match(s)
+      if k:
+        spcs = k.groups()[1]
+        rest = k.groups()[0]
+        self.spaces.append((ln, spcs.replace(" ", "%").replace("\t","%") + rest))
+      else:
+        k = self.re_backslash.match(s)
+        if k:
+          head = k.group(1)
+          spcs = k.group(2)
+          tail = "\\"
+          self.backslashes.append((ln, head + len(spcs) * "%" + tail))
+
+  def hasErrors(self):
+    return 0
+  def hasWarnings(self):
+    return len(self.spaces) + len(self.backslashes)
+
+  def reset(self):
+    self.spaces = []
+    self.backslashes = []
+      
+  def report(self):
+    if len(self.spaces):
+      self.formatter.subwarn("Has illegal space characters (marked by %):")
+      for i in self.spaces:
+        self.formatter.subsub("[line " + str(i[0]) + "]:" + i[1])
+    if len(self.backslashes):
+      self.formatter.subwarn("Has illegal white space (marked by %), only one space character allowed:")
+      for i in self.backslashes:
+        self.formatter.subsub("[line " + str(i[0]) + "]:" + i[1])
+
+class TestHeaders(Test):
+
+  def __init__(self, formatter, options):
+    Test.__init__(self,formatter, options)
+    self.desc = "Testing for malformed headers"
+    self.want = [ [ 0, # count
+                    re.compile("^(# Copyright .*2002.*)"), 
+                    "Copyright statement" ],
+                  [ 0, # count
+                    re.compile("^(# " + "\$" + "Header:.*" + "\$)"), # Don't want CVS to fix this
+                    "$" + "Header:" + "$" ], # Don't want CVS to fix this either
+                  [ 0, # count
+                    re.compile("^(# Distributed under the terms of the GNU General Public License.*)"),
+                    "GPL license" ] ]
+    self.dontwant = [ (1, # append result of regex match
+                 re.compile("^(# Copyright 1999-(2000|2001).*)"),
+                 "Suspect copyright year"), 
+                (1,
+                 re.compile("^(# /home.*)"),
+                 "Suspect path in header"),
+                (0, # don't append result of regex match
+                 re.compile("^(# Author.*)"),
+                 "Use of Author field in the header is deprecated. Put name in ChangeLog"),
+                (0,
+                 re.compile("^(# Maintainer.*)"),
+                 "Use of Maintainer field in the header is deprecated. Put name in ChangeLog"),
+                (1,
+                 re.compile("^(# /space.*)"),
+                 "Suspect path in header")]
+
+  def reset(self):
+     for i in self.want:
+        i[0] = 0
+     self.errors = []
+     self.warnings = []
+
+  def hasErrors(self):
+    num_error = 0
+    for i in self.want:
+      if i[0] == 0:
+        num_error += 1
+    return num_error 
+
+  def checkLine(self, s, ln):
+    for i in self.dontwant:
+      k = i[1].match(s)
+      if k and i[0]:
+        self.warnings.append(i[2] + ": " + k.groups()[0] )
+      elif k and not i[0]:
+        self.warnings.append(i[2])
+    for i in self.want:
+      k = i[1].match(s)
+      if k:
+        i[0] += 1
+        
+  def report(self):
+    illegal_headers = len(self.warnings)
+    for i in self.want:
+      if i[0] == 0:
+        illegal_headers += 1 
+    if illegal_headers:
+      self.formatter.subwarn("Has illegal or suspect headers:")
+      for i in self.warnings:
+        self.formatter.subwarn(i)
+      for i in self.want:
+        if i[0] == 0:
+          self.formatter.suberr("Missing " + i[2])
+
+class TestTry(Test):
+
+  def __init__(self,formatter, options):
+    Test.__init__(self,formatter, options)
+    self.desc = "Testing for occurence of deprecated try"
+    self.re = [ re.compile("^([ \t][ \t]*try.*)"),
+                re.compile("(.*=.* try .*)") ]
+
+  def checkLine(self, s, ln):
+    for i in self.re:
+      k = i.match(s)
+      if k:
+        self.errors.append(k.groups()[0])
+
+  def report(self):
+    if len(self.errors):
+      self.formatter.suberr("Uses try, which is deprecated")
+      for i in self.errors:
+        self.formatter.subsub(i)
+
+class TestA(Test):
+
+  def __init__(self, formatter, options):
+    Test.__init__(self,formatter, options)
+    self.desc = "Testing for superfluous A=${P}.tar.gz"
+    self.re = re.compile("(^A=.*)")
+
+  def checkLine(self, s, ln):
+    k = self.re.match(s)
+    if k:
+      self.errors.append(k.groups()[0])
+
+  def report(self):
+    if len(self.errors):
+      self.formatter.suberr("Contains superfluous " + self.errors[0])
+        
+class TestDepend(Test):
+
+  def __init__(self, formatter, options):
+    Test.__init__(self,formatter, options)
+    self.desc = "Testing for empty DEPEND"
+    self.re = re.compile("DEPEND=\"\"")
+
+  def checkLine(self, s, ln):
+    k = self.re.match(s)
+    if k:
+      self.warnings.append("")
+
+  def report(self):
+    if len(self.warnings):
+      self.formatter.subwarn("DEPEND is suspiciously empty")
+
+class TestHomepage(Test):
+
+  def __init__(self, formatter,options):
+    Test.__init__(self,formatter,options)
+    self.desc = "Testing for empty HOMEPAGE"
+    self.re = re.compile("HOMEPAGE=\"\"")
+
+  def checkLine(self, s, ln):
+    k = self.re.match(s)
+    if k:
+      self.warnings.append("")
+
+  def report(self):
+    if len(self.warnings):
+      self.formatter.subwarn("Is HOMEPAGE really supposed to be empty ?")
+
+class TestDescription(Test):
+
+  def __init__(self, formatter, options):
+    Test.__init__(self,formatter, options)
+    self.desc = "Testing for empty DESCRIPTION"
+    self.re = re.compile("DESCRIPTION=\"\"")
+
+  def checkLine(self, s, ln):
+    k = self.re.match(s)
+    if k:
+      self.errors.append("")
+
+  def report(self):
+    if len(self.errors):
+      self.formatter.suberr("DESCRIPTION must not be empty")
+
+class TestEnvVarPresence(Test):
+
+  def __init__(self, formatter, options):
+    Test.__init__(self,formatter, options)
+    self.desc = "Testing for presence of env vars"
+    self.re = []
+    self.found = []
+    self.required = [ ("SRC_URI", "See 2.4"),
+                      ("DESCRIPTION", "See policy, 2.8"),
+                      ("HOMEPAGE", "See policy, 2.8"),
+                      ("DEPEND", "See policy, 2.2"),
+                      ("LICENSE", "See policy, 2.6"),
+                      ("SLOT", "See policy, 2.5"),
+                      ("KEYWORDS", "See policy, 2.3"),
+                      ("IUSE", "See policy, 2.7")
+                      ]
+    self.desired = [ ("RDEPEND", "Is RDEPEND == DEPEND ? See policy, 2.2") ]
+
+
+    for i in self.required:
+      self.re.append(re.compile("^(" + i[0] + ")="))
+    for i in self.desired:
+      self.re.append(re.compile("^(" + i[0] + ")="))
+      
+  def checkLine(self, s, ln):
+    for i in self.re:
+      k = i.match(s)
+      if k:
+        self.found.append(k.group(1))
+
+  def report(self):
+    for i in self.required:
+      if i[0] not in self.found:
+        self.formatter.suberr("Missing " + i[0] + ". " + i[1])
+    for i in self.desired:
+      if i[0] not in self.found:
+        self.formatter.subwarn("Missing " + i[0] + ". " + i[1])
+
+  def hasWarnings(self):
+    for i in self.desired:
+      if i[0] not in self.found:
+        return 1
+
+  def hasErrors(self):
+    for i in self.required:
+      if i[0] not in self.found:
+        return 1
+
+class TestLicense(Test):
+  def __init__(self, formatter, options):
+    Test.__init__(self,formatter, options)
+    self.desc = "Testing for proper LICENSE"
+    self.re = re.compile("^LICENSE=\"(.*)\"")
+    self.license_dirs = options['licenseDirs']
+    self.licenses = self.loadLicenses()
+    
+  def loadLicenses(self):
+    licenses = []
+    for i in self.license_dirs:
+      try:
+        candidates = os.listdir(i)
+      except:
+        self.formatter.line("!!! License directory '" + i + "' does not exist")
+        continue
+      for j in candidates:
+        if os.path.isfile(i + "/" + j):
+          licenses.append(j)
+    return licenses
+
+  def checkLine(self, s, ln):
+    k = self.re.match(s)
+    if k:
+      print k.group(1)
+      licenses = k.group(1).split(" ")
+      for i in licenses:
+       if i not in self.licenses:
+         self.errors.append("License '" + i + "' not known")
+
+  def report(self):
+    for i in self.errors:
+      self.formatter.suberr(i)
+         
+class TestUseFlags(Test):
+  def __init__(self, formatter, options):
+    Test.__init__(self,formatter, options)
+    self.desc = "Testing for sane USE flag usage"
+    self.re = re.compile("[^#]*use ([a-z0-9\+]+).*")
+    self.useflags = self.loadUseFlags()
+
+  def loadUseFlags(self):
+    ins = open("/usr/portage/profiles/use.desc")
+    rex = re.compile("^([a-z0-9]+)[ \t]+-.*");
+    useflags = []
+    for i in ins.readlines():
+      k = rex.match(i)
+      if k:
+        useflags.append(k.group(1))
+    return useflags
+    
+  def checkLine(self, s, ln):
+    k = self.re.match(s)
+    if k:
+      flag = k.group(1)
+      if flag not in self.useflags:
+        l = k.start(1)
+        # We want to try and figure pretty exactly if we've hit a real instnce
+        # of the use command or just some random mumbling inside a string
+        numApostrophes = 0
+        numBackticks = 0
+        numTicks = 0
+        for i in xrange(l,0,-1):
+          if s[i] == '\"' and (i == 0 or (i > 0 and s[i-1] != '\\')):
+            numApostrophes += 1
+          if s[i] == '\'' and (i == 0 or (i > 0 and s[i-1] != '\\')):
+            numTicks += 1
+          if s[i] == '`' and (i == 0 or (i > 0 and s[i-1] != '\\')):
+            numBackticks += 1
+
+        if numApostrophes % 2 == 0:
+          foundError = 1
+        elif numBackticks % 2 and numTicks % 2 == 0:
+          foundError = 1
+        else:
+          foundError = 0
+
+        if foundError:
+          self.errors.append("Unknown USE flag '" + flag + "'")
+
+  def report(self):
+    for i in self.errors:
+      self.formatter.suberr(i)
+
+
+def getTests(formatter,options):
+  return [ TestSpaces(formatter,options),
+           TestHeaders(formatter,options),
+           TestTry(formatter,options),
+           TestA(formatter,options),
+           TestDepend(formatter,options),
+           TestHomepage(formatter,options),
+           TestDescription(formatter,options),
+           TestEnvVarPresence(formatter,options),
+           TestUseFlags(formatter,options),
+           TestLicense(formatter,options) ]
diff --git a/deprecated/lintool/lintool/test.py b/deprecated/lintool/lintool/test.py
new file mode 100644 (file)
index 0000000..0cc56ff
--- /dev/null
@@ -0,0 +1,30 @@
+# Copyright 2002 Gentoo Technologies, Inc
+# Distributed under the terms of the GNU General Public License v2.0
+# Author Karl Trygve Kalleberg <karltk@gentoo.org>
+
+class Test:
+  def __init__(self, formatter,options=None):
+    self.formatter = formatter
+    self.errors = []
+    self.warnings = []
+  def reset(self):
+    self.errors = []
+    self.warnings = []
+  def hasWarnings(self):
+    return len(self.warnings)
+  def hasErrors(self):
+    return len(self.errors)
+  def getDesc(self):
+    return self.desc
+  def getStatus(self):
+    if self.hasErrors():
+      return "failed"
+    else:
+      return "passed"
+
+class Regex:
+  PN       = "[a-zA-Z_.-]+"
+  PV       = "[a-z0-9A-Z_.-]+"
+  P        =  PN + "-" + PV + "(-r[0-9]+)?"
+  category = "[a-z0-9]+-[a-z0-9]+"
+  full     = category + "/" + P
diff --git a/deprecated/moo/AUTHORS b/deprecated/moo/AUTHORS
new file mode 100644 (file)
index 0000000..fe436cb
--- /dev/null
@@ -0,0 +1 @@
+Karl Trygve Kalleberg <karltk@gentoo.org>
diff --git a/deprecated/moo/README b/deprecated/moo/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/moo/TODO b/deprecated/moo/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/moo/moo b/deprecated/moo/moo
new file mode 100755 (executable)
index 0000000..828df08
--- /dev/null
@@ -0,0 +1,244 @@
+#! /usr/bin/env python2.2
+
+import os
+import sys
+import time
+import signal
+
+CONFDIR="/tmp/moo"
+
+class Config:
+    pass
+
+def die(code, msg):
+    sys.stdout.write(msg + "\n")
+    sys.edit(code)
+    
+def printUsage():
+    print "Usage: moo <options> [command] <command-options>\n" + \
+              "Where [command] is one of:\n" + \
+              "scan     - scan for available networks\n" + \
+              "list     - list available profiles\n"+ \
+              "select   - select a particular profile\n"
+
+def scanNetworks():
+    pass
+
+def runCmd(cmd):
+    if Config.verbosity > 5:
+        print "Executing \"" + cmd + "\""
+    v = os.system(cmd)
+    if Config.verbosity > 5:
+        print "Result: " + str(v)
+    return v
+
+class ProfileHandler:
+    def __init__(self):
+        self.profiles = {}
+        self._loadProfiles()
+    def _loadProfiles(self):
+        for x in os.listdir(CONFDIR+"/profiles"):
+            if x[-1] != "~" and x[0] != ".":
+                self.profiles[x] = Profile(CONFDIR+"/profiles/"+x)
+    def listProfiles(self,detailed=0):
+        for x in self.profiles.keys():
+            if detailed:
+                print x + ":"
+                self.profiles[x].dump()
+            else:
+                print x + " [" + self.profiles[x].description + "]"
+    def getProfileNames(self):
+        return self.profiles.keys()
+    def getProfile(self,name):
+        return self.profiles[name]
+
+
+class Profile:
+    def __init__(self, filename):
+        self._loadFromFile(filename)
+    def _loadFromFile(self,filename):
+        self.desc = ""
+        self.ifaceName = ""
+        self.ip = ""
+        self.broadcast = ""
+        self.gateway = ""
+        self.exclusive = "no"
+        self.netmask = ""
+        self.nameserver = ""
+
+        ins = open(filename)
+        for s in ins.readlines():
+            for x in ["description","ifaceName","ip","broadcast",
+                      "gateway","exclusive","nameserver","wepkey"]:
+                if s.find(x+"=") == 0:
+                    val=s.replace(x+"=","").strip()
+                    self.__dict__[x] = val
+    def dump(self):
+        print "description = " + self.description + "\n" + \
+              "iface       = " + self.ifaceName + "\n" + \
+              "ip          = " + self.ip + "\n" + \
+              "broadcast   = " + self.broadcast + "\n" + \
+              "gateway     = " + self.gateway + "\n" + \
+              "gateway     = " + self.nameserver + "\n" + \
+              "exclusive   = " + self.exclusive + "\n" 
+
+class Interface:
+    def __init__(self, name):
+        self.name = name
+        self.netmask = ""
+        self.broadcast = ""
+        self.ip = ""
+        self.gateway = ""
+        self.nameserver = ""
+        self.wepkey = ""
+        self._loadIPV4Info()
+    def _loadIPV4Info(self):
+        pass
+    def getNameserver(self):
+        return self.nameserver
+    def getNetmask(self):
+        return self.netmask
+    def getName(self):
+        return self.name
+    def getBroadcast(self):
+        return self.broadcast
+    def getWEPKey(self):
+        return self.wepkey
+    def getGateway(self):
+        return self.gateway
+    def setNameserver(self,nameserver):
+        self.nameserver = nameserver
+    def setIP(self, ip):
+        self.ip = ip
+    def setWEPKey(self,key):
+        self.wepkey = key
+    def setGateway(self,gw):
+        self.gateway = gw
+    def setBroadcast(self,broadcast):
+        self.broadcast = broadcast
+    def setNetmask(self,netmask):
+        self.netmask = netmask
+    def runDHCP(self):
+        runCmd("dhcpcd " + self.name)
+    def down(self):
+        runCmd("ifconfig " + self.name + " down")
+        pidFile = "/var/run/dhcpcd-" + self.name + ".pid"
+        if os.path.exists(pidFile):
+            ins = open(pidFile)
+            pid = int(ins.readline())
+            os.kill(pid,signal.SIGTERM)
+            time.sleep(1)
+    def up(self):
+        options = ""
+
+        if self.wepkey:
+            if runCmd("iwconfig eth1 key " + self.wepkey):
+              die(4, "Failed to set WEP key for " + self.name)
+              
+        if self.ip:
+            options += self.ip + " "
+        if self.broadcast:
+            options += "broadcast " + self.broadcast + " "
+        if self.netmask:
+            options += "netmask " + self.netmask + " "
+
+        if runCmd("ifconfig " + self.name + " " + options + " up"):
+            die(2, "Failed to bring up " + self.name)
+            
+        if self.gateway:
+            if runCmd("route add default gw " + self.gateway + " " + self.name):
+                die(3, "Failed to set default gateway for " + self.name)
+            
+        if self.nameserver:
+            if Config.verbosity > 5:
+                print("Using nameserver " + self.nameserver)
+            try: 
+                ous = open("/etc/resolv.conf","w")
+                ous.write("nameserver " + self.nameserver)
+                ous.close()
+            except OSError:
+                die("Failed to set nameserver")
+        if Config.verbosity > 3:
+            print "Brough interface " + self.name + " up"
+            
+class InterfaceHandler:
+    def __init__(self):
+        self.ifaces = {}
+        self._loadAllInterfaces()
+    def _loadAllInterfaces(self):
+        ins=open("/proc/net/dev")
+        for line in ins.readlines():
+            tokens = line.split(":")
+            if len(tokens) > 1:
+               ifaceName = tokens[0].strip()
+                iface = Interface(ifaceName)
+                self.ifaces[ifaceName] = iface
+    def getInterface(self,ifaceName):
+        return self.ifaces[ifaceName]
+    def downAll(self):
+        for x in self.ifaces.values():
+            if x.getName() != "lo":
+                x.down()
+
+class Moo:
+    def __init__(self):
+        self.profileHandler = ProfileHandler()
+        self.ifaceHandler = InterfaceHandler()
+    
+    def selectProfile(self,profile):
+        prof = self.profileHandler.getProfile(profile)
+
+        if prof.exclusive == "yes":
+            self.ifaceHandler.downAll()
+
+        iface = self.ifaceHandler.getInterface(prof.ifaceName)
+        
+        if prof.ip == "dhcp":
+            iface.runDHCP()
+        else:
+            iface.setIP(prof.ip)
+            iface.setBroadcast(prof.broadcast)
+            iface.setNetmask(prof.netmask)
+            iface.setGateway(prof.gateway)
+            iface.setNameserver(prof.nameserver)
+            iface.up()
+            
+    def listProfiles(self,detailed=0):
+        self.profileHandler.listProfiles(detailed)
+
+def initConfig():
+    Config.verbosity = 3
+    
+def main():
+
+    initConfig()
+    
+    if len(sys.argv) < 2:
+        printUsage()
+        sys.exit(1)
+
+    moo = Moo()
+
+    for i in xrange(len(sys.argv)):
+        if sys.argv[i] == "list":
+            detailed = 0
+            for x in sys.argv[i:]:
+                if x == "--detailed":
+                    detailed = 1
+            moo.listProfiles(detailed)
+
+        elif sys.argv[i] == "select":
+            moo.selectProfile(sys.argv[2])
+
+if __name__ == "__main__":
+    main()
+
+
+# TODO
+# - automatically create profile
+# - specify wireless network name
+# - specify wep key
+# - specify access point
+# - specify pre_run/post_run commands
+#   - with parameters
+# 
diff --git a/deprecated/moo/moo.1 b/deprecated/moo/moo.1
new file mode 100644 (file)
index 0000000..534aa0c
--- /dev/null
@@ -0,0 +1,12 @@
+.TH moo "1" "Nov 2003" "gentoolkit"
+.SH NAME
+moo \- Gentoo: Configuration Update Utility
+.SH SYNOPSIS
+.B moo
+.SH BUGS
+This tool does not yet have a man page. Feel free to submit a bug about it to
+http://bugs.gentoo.org
+.SH AUTHORS
+This informative man page was written by Karl Trygve Kalleberg 
+<karltk@gentoo.org>.
+
diff --git a/deprecated/old-scripts/Makefile b/deprecated/old-scripts/Makefile
new file mode 100644 (file)
index 0000000..8f61cb5
--- /dev/null
@@ -0,0 +1,32 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+       echo "BOTOLPHS Huge benign tumors which archdeacons and old chemistry teachers affect to wear on the sides of their noses."
+
+dist:
+       mkdir -p ../../$(distdir)/src/old-scripts/
+       cp Makefile ../../$(distdir)/src/old-scripts/
+       cp dep-clean dep-clean.1 ../../$(distdir)/src/old-scripts/
+       cp pkg-clean pkg-clean.1 ../../$(distdir)/src/old-scripts/
+       cp ewhich ewhich.1 ../../$(distdir)/src/old-scripts/
+       cp mkebuild mkebuild.1 ../../$(distdir)/src/old-scripts/
+       cp pkg-size pkg-size.1 ../../$(distdir)/src/old-scripts/
+
+install:
+       install -m 0755 dep-clean $(bindir)/
+       install -m 0755 pkg-clean $(sbindir)/
+       install -m 0755 ewhich $(bindir)/
+       install -m 0755 mkebuild $(sbindir)/
+       install -m 0755 pkg-size $(bindir)/
+
+       install -m 0644 dep-clean.1 $(mandir)/
+       install -m 0644 pkg-clean.1 $(mandir)/
+       install -m 0644 ewhich.1 $(mandir)/
+       install -m 0644 mkebuild.1 $(mandir)/
+       install -m 0644 pkg-size.1 $(mandir)/
diff --git a/deprecated/old-scripts/dep-clean b/deprecated/old-scripts/dep-clean
new file mode 100644 (file)
index 0000000..89c6364
--- /dev/null
@@ -0,0 +1,272 @@
+#!/bin/bash
+#Shows unrequired packages and missing dependencies.
+#Author/Maintainer: Brandon Low <lostlogic@gentoo.org>
+#Author: Jerry Haltom <ssrit@larvalstage.net>
+
+echo
+echo -e "\x1b[31;01m!!! As of Gentoolkit 0.2.0, this tool is deprecated."
+echo -e "!!!\x1b[0;0m Please refer to 'emerge clean' and 'emerge depclean' for replacements."
+echo 
+
+PROG=`basename ${0}`
+
+tmp="/tmp/$$"
+
+#Get PORTDIR and PORTDIR_OVERLAY from portage
+PORTDIR_OVERLAY="$(/usr/lib/portage/bin/portageq portdir_overlay)"
+PORTDIR="$(/usr/lib/portage/bin/portageq portdir)"
+
+rm -rf ${tmp} > /dev/null 2>&1
+mkdir ${tmp} > /dev/null 2>&1
+
+declare -i i
+
+set -- `getopt -n ${PROG} -o N,R,U,I,v,q,C,h -l needed,removed,unneeded,interactive,verbose,quiet,nocolor,help -- ${*/ --/};[ $? != 0 ] && echo "y"`
+
+while [ ${#} -gt 0 ]
+do
+       a=${1}
+       shift
+               case "${a}" in
+
+               -I|--interactive)
+                               interactive=y
+                               ;;
+
+               -N|--needed)
+                               needed=y
+                               ;;
+
+               -U|--unneeded)
+                               unneeded=y
+                               ;;
+
+               -R|--removed)
+                               removed=y
+                               ;;
+
+               -v|--verbose)
+                               verb=y
+                               ;;
+
+               -q|--quiet)
+                               quiet=y
+                               ;;
+
+               -C|--nocolor)
+                               nocolor=y
+                               ;;
+
+               -h|--help)
+                               usage=y
+                               ;;
+
+               --)
+               [ ${1} ] && usage=y && broke=y
+                               break
+                               ;;
+
+               *)
+               usage=y
+               broke=y
+               echo "FIXME - OPTION PARSING - ${a}"
+               break
+               ;;
+
+               esac
+done
+
+if [ ! ${needed} ] && [ ! ${unneeded} ] && [ ! ${removed} ]; then
+  needed=y
+  unneeded=y
+  removed=y
+fi
+
+#Set up colors
+if [ ! "${nocolor}" ]; then
+               NO="\x1b[0;0m"
+               BR="\x1b[0;01m"
+               CY="\x1b[36;01m"
+               GR="\x1b[32;01m"
+               RD="\x1b[31;01m"
+               YL="\x1b[33;01m"
+               BL="\x1b[34;01m"
+elif [ ${quiet} ] && (
+          ( [ ${needed} ] && [ ${unneeded} ] ) ||
+          ( [ ${unneeded} ] && [ ${removed} ] ) ||
+          ( [ ${removed} ] && [ ${needed} ] )
+        ); then
+       NEED=" N"
+       UNNE=" U"
+       REMO=" R"
+fi
+
+if [ ${usage} ]; then
+  echo -e "${BR}GenToolKit's Dependency Checker!
+${NO}Displays packages that are installed but which none 
+of the packages in world or system depend on, and 
+displays packages which are depended on by world or
+system, but are not currently installed.
+
+${BR}USAGE:
+  ${BL}${PROG}${YL}  [${NO}options${YL}]${NO}
+  ${BL}${PROG}${GR}  --help${NO}
+
+${BR}OPTIONS:
+  ${GR}-U, --unneeded${NO}     display unneeded packages that are installed (${GR}green${NO})
+  ${GR}-N, --needed${NO}               display needed packages that are not installed (${RD}red${NO})
+  ${GR}-R, --removed${NO}              display installed packages not in portage (${YL}yellow${NO})
+
+  ${GR}-I, --interactive${NO}  interactively modify world file before proceeding
+  ${GR}-C, --nocolor${NO}              output without color, if necessary, package types are 
+                       noted with ${GR}U, N${NO} and ${GR}R${NO} respectively
+  ${GR}-v, --verbose${NO}              be more verbose
+  ${GR}-q, --quiet${NO}                        be quiet (just output the packages, no extra info)
+
+${BR}NOTES:
+  ${GR}*${NO} If this script is run on a system that is not up-to-date or which hasn't 
+       been cleaned (with '${BL}emerge -c${NO}') recently, the output may be deceptive.
+  ${GR}*${NO} If the same package name appears in all three categories, then it is
+       definitely time to update that package and then run '${BL}emerge -c${NO}'.
+  ${GR}*${NO} The ${GR}-U, -N${NO} and ${GR}-R${NO} options may be combined, defaults to ${GR}-UNR${NO}"
+  rm -rf ${tmp} > /dev/null 2>&1
+  [ ${broke} ] && exit 1 || exit 0
+fi 
+
+X="\([^/]*\)"
+
+#Retrieve currently merged packages.
+if [ ${verb} ];then
+       echo -e "${CY}Retrieving currently merged packages.${NO}"
+fi
+find /var/db/pkg/ -name '*.ebuild' | \
+       sed -e "s:/var/db/pkg/::" \
+               -e "s:${X}/${X}/${X}:\1/\2:" | \
+       sort -u >> ${tmp}/current
+
+if [ ${verb} ]; then
+       echo -e "${CY}"`cat ${tmp}/current | wc -l` "currently merged packages.${NO}"
+       echo -e
+fi
+
+#Retrieve system packages and add to image.
+if [ ${verb} ];then
+       echo -e "${CY}Retrieving system packages.${NO}"
+fi
+emerge system -ep | \
+       sed -e "/ebuild/s:^.*] \([^ ]*\) *:\1:p;d" | \
+       sort -u \
+       > ${tmp}/system
+
+if [ ${verb} ]; then
+       echo -e "${CY}"`cat ${tmp}/system | wc -l 2> /dev/null` "packages contained in system.${NO}"
+       echo -e
+       echo -e "${CY}Preparing world file.${NO}"
+fi
+
+#Create local copy of world and ask user to verify it.
+cp /var/cache/edb/world ${tmp}/world
+
+if [ ${interactive} ]; then
+       ${EDITOR} ${tmp}/world
+fi
+
+#Retrieve world packages and dependencies and add to image.
+if [ ${verb} ]; then
+       echo -e
+       echo -e "${CY}Preparing list of installed world packages.${NO}"
+       echo -e
+fi
+
+cat ${tmp}/current | grep -f ${tmp}/world | sort > ${tmp}/world.inst
+find ${PORTDIR} ${PORTDIR_OVERLAY} -iname '*.ebuild' | \
+       awk -F'/' '{printf("%s/%s\n", $(NF-2), $NF)}' | \
+       sed -e 's:\.ebuild::' > ${tmp}/ebuilds
+grep -xf ${tmp}/world.inst ${tmp}/ebuilds >> ${tmp}/world.new
+
+if [ ${verb} ]; then
+       echo -e "${CY}"`cat ${tmp}/ebuilds | wc -l`"\tebuilds available.${NO}"
+       echo -e "${CY}"`cat ${tmp}/world.new | wc -l`"\tpackages contained in final world file.${NO}"
+       echo -e
+       echo -e "${CY}List prepared, checking dependencies with emerge -ep${NO}"
+fi
+
+sort ${tmp}/world.new |sed -e 's:^:\\\=:' | uniq | xargs emerge -ep | \
+       tee ${tmp}/log | sed -e '/ebuild/s:^.*] \([^ ]*\) *$:\1:p;d' > ${tmp}/image.unsorted
+
+depends=`cat ${tmp}/image.unsorted|wc -l`
+
+if [ ${depends} -lt "2" ]; then
+       echo -e "${RD}There appears to be an unresolved dependency in your world file."
+       echo -e "Please check for masking errors or other world file issues,"
+       echo -e "and then try again."
+       echo -e
+       echo -e "The following is the emerge output for your reference:${NO}"
+       cat ${tmp}/log
+       rm -rf ${tmp} > /dev/null 2>&1
+       exit 1
+fi
+
+cat ${tmp}/system >> ${tmp}/image.unsorted
+
+#Cleanup image
+sort -u ${tmp}/image.unsorted > ${tmp}/image
+
+if [ ${verb} ];then
+       echo -e "${CY}"`cat ${tmp}/image | wc -l` "packages contained in final image.${NO}"
+       echo -e
+fi
+
+#Determine packages that exist in current but not in image.
+#These packages are safe to clean up.
+if [ ${unneeded} ]; then
+       if [ ! ${quiet} ]; then
+               echo -e "${CY}These packages have no other packages depending on them.${NO}"
+       fi
+
+       grep -vxf ${tmp}/image ${tmp}/current > ${tmp}/unneeded
+       for line in `cat ${tmp}/unneeded`;do
+               echo -e "${GR}${line}${CY}${UNNE}${NO}"
+       done
+
+       if [ ! ${quiet} ];then
+               echo -e "${CY}Total of"`cat ${tmp}/unneeded|wc -l` "unneeded packages.${NO}"
+       fi
+fi
+
+#Determine packages that exist in image but not in current.
+#These packages should be added.
+if [ ${needed} ]; then
+       if [ ! ${quiet} ];then
+               echo -e
+               echo -e "${CY}These packages are depended upon but are not present on the system.${NO}"
+       fi
+
+       grep -vxf ${tmp}/current ${tmp}/image > ${tmp}/needed
+       for line in `cat ${tmp}/needed`;do
+               echo -e "${RD}${line}${CY}${NEED}${NO}"
+       done
+
+       if [ ! ${quiet} ];then
+               echo -e "${CY}Total of"`cat ${tmp}/needed|wc -l` "needed packages.${NO}"
+       fi
+fi
+
+#Determine packages that are installed but not currently in portage
+if [ ${removed} ]; then
+       if [ ! ${quiet} ];then
+               echo -e
+               echo -e "${CY}These packages are installed but not in the portage tree.${NO}"
+       fi
+       grep -xf ${tmp}/current ${tmp}/ebuilds > ${tmp}/hascurrent
+       grep -vxf ${tmp}/hascurrent ${tmp}/current > ${tmp}/removed
+       for line in `cat ${tmp}/removed`;do
+               echo -e "${YL}${line}${CY}${REMO}${NO}"
+       done
+
+       if [ ! ${quiet} ];then
+               echo -e "${CY}Total of"`cat ${tmp}/removed|wc -l` "removed packages.${NO}"
+       fi
+fi
+
+rm -rf ${tmp} > /dev/null 2>&1
diff --git a/deprecated/old-scripts/dep-clean.1 b/deprecated/old-scripts/dep-clean.1
new file mode 100644 (file)
index 0000000..9747ce4
--- /dev/null
@@ -0,0 +1,190 @@
+.\" Automatically generated by Pod::Man version 1.15
+.\" Thu Jul 18 15:59:55 2002
+.\"
+.\" Standard preamble:
+.\" ======================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  | will give a
+.\" real vertical bar.  \*(C+ will give a nicer C++.  Capital omega is used
+.\" to do unbreakable dashes and therefore won't be available.  \*(C` and
+.\" \*(C' expand to `' in nroff, nothing in troff, for use with C<>
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr
+.\" for titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and
+.\" index entries marked with X<> in POD.  Of course, you'll have to process
+.\" the output yourself in some meaningful fashion.
+.if \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.\"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it
+.\" makes way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.bd B 3
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ======================================================================
+.\"
+.IX Title "DEP-CLEAN 1"
+.TH DEP-CLEAN 1 "Copyright 2002 Gentoo Technologies, Inc." "2002-07-18" "GenToolKit's Dependency Checker!"
+.UC
+.SH "NAME"
+dep-clean \- Gentoo: Shows unrequired packages and missing dependencies.
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+.Vb 1
+\&  dep-clean [-RUNICv]
+.Ve
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+dep-clean displays extraneous, missing or extra packages.  Extra packages are those in which are not a part of the portage tree (/usr/portage).  It does \s-1NOT\s0 modify the system in any way.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.Ip "\-N, \-\-needed" 4
+.IX Item "-N, --needed"
+Display needed packages that are not installed. (red) (default)
+.Ip "\-R, \-\-removed" 4
+.IX Item "-R, --removed"
+Display installed packages not in portage. (yellow) (default)
+.Ip "\-U, \-\-unneeded" 4
+.IX Item "-U, --unneeded"
+Display unneeded packages that are installed. (green) (default)
+.Ip "\-I, \-\-interactive" 4
+.IX Item "-I, --interactive"
+Interactively modify world file before proceeding.
+.Ip "\-C, \-\-nocolor" 4
+.IX Item "-C, --nocolor"
+Output without color.  Package types will be noted with R, U and N.
+.Ip "\-v, \-\-verbose" 4
+.IX Item "-v, --verbose"
+Be more verbose.
+.Ip "\-q, \-\-quiet" 4
+.IX Item "-q, --quiet"
+Be quiet (display only packages).
+.SH "NOTES"
+.IX Header "NOTES"
+.Ip "" 4
+If this script is run on a system that is not up-to-date or which hasn't been cleaned (with 'emerge \-c') recently, the output may be deceptive.
+.Ip "" 4
+If the same package name appears in all three categories, then it is definitely time to update that package and then run 'emerge \-c'.
+.Ip "" 4
+The \-U, \-N and \-R options may be combined, default is \-UNR
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+Jerry Haltom <ssrit at larvalstage dot net> (dep-clean)
+.br
+Brandon Low <lostlogic at gentoo dot org> (dep-clean)
+.PP
+Paul Belt <gaarde at users dot sourceforge dot net> (man page)
diff --git a/deprecated/old-scripts/ewhich b/deprecated/old-scripts/ewhich
new file mode 100755 (executable)
index 0000000..345ec34
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+# Copyright 1999-2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+# $Header$
+#
+# Author: Marius Mauch <genone@gentoo.org>
+#
+# ewhich is a tool to get the ebuild filename for a given package name
+
+import portage,sys,os
+from output import *
+
+sys.stderr.write("\n" + red("!!!") + " As of Gentoolkit 0.2.0 this tool is deprecated\n")
+sys.stderr.write(red("!!!") + " Refer to 'equery which' for a replacement\n\n") 
+
+def print_help():
+       print
+       print fuscia(os.path.basename(sys.argv[0])), "is a tool to get the filename of the ebuild for a given package"
+       print ' '*len(os.path.basename(sys.argv[0])), "that would be used by portage with the current configuration"
+       print
+       print yellow("Syntax:"), os.path.basename(sys.argv[0]), teal("<package>")
+       print
+       print teal("<package>"), "is either a simple package name (like "+green("xfree")+"),"
+       print "          a package name with category (like "+green("x11-base/xfree")+")"
+       print "          or a DEPEND style atom as defined in ebuild(5) (like "+green(">=x11-base/xfree-4.3.0")+")"
+       print
+
+if len(sys.argv) <= 1 or sys.argv[1] == "-h" or sys.argv[1] == "--help":
+       print_help()
+       sys.exit(0)
+
+p = portage.db[portage.root]["porttree"].dbapi.xmatch("bestmatch-visible", sys.argv[1])
+if len(p) <= 1:
+       print
+       print red("*"), "\""+sys.argv[1]+"\" is not a valid package name or a masked package"
+       print
+       sys.exit(1)
+pv = portage.catpkgsplit(p)
+ebuild = "/"+pv[0]+"/"+pv[1]+"/"+p.split("/")[1]+".ebuild"
+for ov in portage.settings["PORTDIR_OVERLAY"].split():
+       if os.path.exists(ov+ebuild):
+               print os.path.normpath(ov+ebuild)
+               sys.exit(0)
+print os.path.normpath(portage.settings["PORTDIR"]+ebuild)
diff --git a/deprecated/old-scripts/ewhich.1 b/deprecated/old-scripts/ewhich.1
new file mode 100644 (file)
index 0000000..4bc5459
--- /dev/null
@@ -0,0 +1,24 @@
+.TH ewhich 1 "October 19, 2003" "ewhich"
+
+.SH NAME
+ewhich \- Gentoo: program for resolving ebuild names
+
+.SH SYNOPSIS
+.B ewhich
+[ -h | --help ] <package>
+
+.SH DESCRIPTION
+.B ewhich
+is a little tool to find the actual ebuild file for a given package name.
+It can use simple package names (like xfree), package names with category
+(like x11-base/xfree) or DEPEND atoms (like >=x11-base/xfree-4.3.0).
+
+.SH BUGS
+.B ewhich
+No known bugs
+
+.SH SEE ALSO
+emerge(1), make.conf(5), ebuild(5)
+
+.SH AUTHOR
+Marius Mauch <genone@gentoo.org>
diff --git a/deprecated/old-scripts/mkebuild b/deprecated/old-scripts/mkebuild
new file mode 100644 (file)
index 0000000..9ee0ce1
--- /dev/null
@@ -0,0 +1,216 @@
+#!/bin/bash
+
+# Copyright (c) 2002
+# John Stalker
+# Department of Mathematics
+# Princeton University
+
+echo
+echo -e "\x1b[31;01m!!! As of Gentoolkit 0.2.0, this tool is deprecated"
+echo -e "!!!\x1b[0;0m Refer to app-portage/ebuilder for a replacement."
+echo 
+
+CONFIG_FILE=${HOME}/.mkebuild
+if [ -e $CONFIG_FILE ]
+then
+        source $CONFIG_FILE
+else
+        echo This appears to be the first time you have used mkebuild.
+        echo I am going to make some guesses.  If any of these are wrong
+        echo you should edit the file ${CONFIG_FILE}.
+        echo
+        MY_NAME=`awk -F":" '/^'${USER}:'/ { print $5 }' /etc/passwd`
+        echo Your name is ${MY_NAME}.
+        echo 'MY_NAME="'${MY_NAME}'"' >${CONFIG_FILE}
+        COPYRIGHT="Gentoo Technologies, Inc."
+        echo You wish to asign copyright to ${COPYRIGHT}
+        echo 'COPYRIGHT="'${COPYRIGHT}'"' >>${CONFIG_FILE}
+        MY_EMAIL=${USER}"@"${HOSTNAME}
+        echo Your email address is ${MY_EMAIL}.
+        echo 'MY_EMAIL='${MY_EMAIL} >>$CONFIG_FILE
+        LICENSE="the GNU General Public License, v2"
+        echo Your preferred license is ${LICENSE}.
+        echo 'LICENSE="'${LICENSE}'"' >>${CONFIG_FILE}
+        LOCAL_SOURCE=${HOME}
+        echo You download sources to ${LOCAL_SOURCE}.
+        echo LOCAL_SOURCE=${LOCAL_SOURCE} >>${CONFIG_FILE}
+        BUILD_DIRECTORY=${HOME}
+        echo You build packages in ${BUILD_DIRECTORY}.
+        echo 'BUILD_DIRECTORY='${BUILD_DIRECTORY} >>${CONFIG_FILE}
+        echo
+fi
+FILE_NAME=`basename $1`
+SOURCE_LOCATION=`dirname $1`
+PACKAGE_NAME=${FILE_NAME%.*}
+FILE_EXTENSION=${FILE_NAME##*.}
+if [ "${PACKAGE_NAME##*.}" = "tar" ]
+then
+        FILE_EXTENSION=tar.${FILE_EXTENSION}
+        PACKAGE_NAME=${PACKAGE_NAME%.tar}
+fi
+EBUILD_FILE=${PWD}/${PACKAGE_NAME}.ebuild
+echo "# Copyright" `date +"%Y"` ${COPYRIGHT} >${EBUILD_FILE}
+echo "# Distributed under the terms of" ${LICENSE} >>${EBUILD_FILE}
+#echo "# Author" ${MY_NAME} '<'${MY_EMAIL}'>' >>${EBUILD_FILE}
+echo "# \$Header$" >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo 'DESCRIPTION=""' >>${EBUILD_FILE}
+echo 'SRC_URI="'${SOURCE_LOCATION}'/${P}.'${FILE_EXTENSION}'"' >>${EBUILD_FILE}
+echo 'HOMEPAGE="'${SOURCE_LOCATION}'/"' >>${EBUILD_FILE}
+echo 'LICENSE=""' >>${EBUILD_FILE}
+echo 'SLOT="0"' >>${EBUILD_FILE}
+echo 'KEYWORDS="~x86"' >>${EBUILD_FILE}
+echo 'IUSE=""' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo 'DEPEND=""' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo '#RDEPEND=""' >>${EBUILD_FILE}
+echo 'S=${WORKDIR}/${P}' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo 'src_unpack() {' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo -e "\t"'unpack ${A}' >>${EBUILD_FILE}
+echo -e "\t"'cd ${S}' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo '}' >>${EBUILD_FILE}
+if [ -e ${LOCAL_SOURCE}/${FILE_NAME} ]
+then
+        echo I am assuming that $1 really 
+        echo exists and that ${LOCAL_SOURCE}/${FILE_NAME}
+        echo is a faithful copy.
+else
+        echo I didn\'t find ${LOCAL_SOURCE}/${FILE_NAME} so I will fetch
+        echo $1.
+        cd ${LOCAL_SOURCE}
+        wget $1
+fi
+if [ -e ${BUILD_DIRECTORY}/${PACKAGE_NAME} ]
+then
+        echo
+        echo I am assuming that ${BUILD_DIRECTORY}/${PACKAGE_NAME} \
+                is the unpacked
+        echo version of ${LOCAL_SOURCE}/${FILE_NAME}.
+else
+        cd ${BUILD_DIRECTORY}
+        if [ $? -ne 0 ]
+        then
+                echo
+                echo I was unable to enter the directory ${BUILD_DIRECTORY}.
+                exit 1
+        fi
+        case "${FILE_EXTENSION}" in
+                tar.gz|tgz)
+                        tar xzf ${LOCAL_SOURCE}/${FILE_NAME}
+                        ;;
+                tar.bz2|tbz2)
+                        tar xjf ${LOCAL_SOURCE}/${FILE_NAME}
+                        ;;
+                tar.Z|tar.z)
+                        unzip ${LOCAL_SOURCE}/${FILE_NAME}
+                        tar xf ${PACKAGE_NAME}.tar
+                        ;;
+                *)
+                        echo
+                        echo I can\'t figure out how to uncompress
+                        echo ${LOCAL_SOURCE}/${FILE_NAME}
+                        exit 1
+        esac
+        if [ $? -ne 0 ]
+        then
+                echo
+                echo I was unable to uncompress ${LOCAL_SOURCE}/${FILE_NAME}.
+                exit 1
+        fi
+fi
+cd ${BUILD_DIRECTORY}/${PACKAGE_NAME}
+if [ $? -ne 0 ]
+then
+        echo
+        echo I could not change directory to ${BUILD_DIRECTORY}/${PACKAGE_NAME}
+        exit 1
+fi
+echo
+echo You might want to look at the following files for configuration and
+echo dependency information:
+find ${PWD} -name README -print
+find ${PWD} -name README.txt -print
+find ${PWD} -name INSTALL -print
+find ${PWD} -name INSTALL.txt -print
+find ${PWD} -name Changes -print
+find ${PWD} -name CHANGES.txt -print
+find ${PWD} -name FAQ.txt
+find ${PWD} -name ChangeLog -print
+find ${PWD} -name NEWS -print
+echo >>${EBUILD_FILE}
+echo 'src_compile() {' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo
+if [ -e Imakefile ]
+then
+        echo I found an Imakefile.  I am assuming that we should use xmkmf.
+        echo I don\'t really understand xmkmf so you will have to configure
+        echo things on your own.
+        echo
+        echo -e "\t"'xmkmf || die' >>${EBUILD_FILE}
+        echo -e "\tmake Makefiles" >>${EBUILD_FILE}
+        echo -e "\tmake includes" >>${EBUILD_FILE}
+        echo -e "\tmake depend" >>${EBUILD_FILES}
+elif [ -e configure ]
+then
+        echo I found a configure file.  I am assuming that we are using autoconf
+.
+        echo I will take care of setting the most commonly used options.  I am
+        echo including a list of all the available options to confiigure, commen
+ted
+        echo out.  You might want to look it over to see if I have missed anythi
+ng.
+        echo
+        ./configure --help >.config.options
+        echo -e "\t./configure \\" >>${EBUILD_FILE}
+        awk '/--prefix/ { print "\t\t--prefix=/usr \\"}' \
+                .config.options >>${EBUILD_FILE}
+        awk '/--infodir/ { print "\t\t--infodir=/usr/share/info \\"}' \
+                .config.options >>${EBUILD_FILE}
+        awk '/--mandir/ { print "\t\t--mandir=/usr/share/man \\"}' \
+                .config.options >>${EBUILD_FILE}
+        echo -e "\t\t"'|| die "./configure failed"' >>${EBUILD_FILE}
+        echo -e "#\tAvailable options to configure:" >>${EBUILD_FILE}
+        awk '/--/ { print "#" $0 }' .config.options >>${EBUILD_FILE}
+        echo -e "\t"'emake || die' >>${EBUILD_FILE}
+elif [ -e Makefile ]
+then
+        echo I found a Makefile.  You should look at it and possibly prepare
+        echo a patch.
+        echo
+        echo -e "\temake || die" >>${EBUILD_FILE}
+else
+        echo I couldn\'t find a Imakefile, configure script, or Makefile for
+        echo this package.  You will have to figure out what to do on your
+        echo own.
+        echo
+fi
+echo '}' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo 'src_install () {' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo -e "\t"'make DESTDIR=${D} install || die' >>${EBUILD_FILE}
+echo '}' >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo >>${EBUILD_FILE}
+echo I couldn\'t supply a package description for the ebuild file
+echo because I don\'t know what ${PACKAGE_NAME} does.
+echo
+echo I am assume the hompage for this package is ${SOURCE_LOCATION}/.
+echo If that is not correct you will need to edit that portion of
+echo the ebuild file as well.
+echo
+echo I don\'t understand dependencies yet.  You will have to add any
+echo dependencies you know of by hand.  Then try your ebuild script
+echo out to see if there are any dependencies you don\'t know of.
+echo
+echo I am assuming that this package comes with a well-behaved Makefile
+echo which does not install anything outside of '${DESTDIR}'.  You will
+echo need to check this by looking at the portion of the Makefile
+echo beginning with the line '"install:"'.
+
diff --git a/deprecated/old-scripts/mkebuild.1 b/deprecated/old-scripts/mkebuild.1
new file mode 100644 (file)
index 0000000..e800d4d
--- /dev/null
@@ -0,0 +1,20 @@
+.TH mkebuild "1" "Nov 2003" "gentoolkit"
+.SH NAME
+mkebuild \- Gentoo: Interactive ebuild generator
+.SH SYNOPSIS
+.B mkebuild
+.SH BUGS
+This tool is obsolete, as of gentoolkit 0.2.0.  
+Use ebuilder [app\-portage/ebuilder] instead.
+.SH SEE ALSO
+.BR ebuilder(1)
+.br
+.BR /usr/sbin/ebuilder
+.br
+.BR /usr/sbin/mkebuild
+
+.SH AUTHORS
+This informative man page was written by Karl Trygve Kalleberg 
+<karltk@gentoo.org> and expanded by Katerina Barone\-Adesi 
+<katerinab@gmail.com>.
+
diff --git a/deprecated/old-scripts/pkg-clean b/deprecated/old-scripts/pkg-clean
new file mode 100644 (file)
index 0000000..9b07337
--- /dev/null
@@ -0,0 +1,107 @@
+#!/usr/bin/python
+# Copyright 1999-2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+# $Header$
+# Author: Leo Lipelis <aeoo@gentoo.org>
+# Author: Karl Trygve Kalleberg <karltk@gentoo.org>
+
+import commands
+import re
+import sys
+import time
+import os
+from output import *
+
+print
+print red("!!! As of Gentoolkit 0.2.0, this tool is deprecated")
+print red("!!!") + " Refer to 'emerge clean' and 'emerge depclean' for replacements."
+print
+
+time.sleep(4)
+
+# constants for package tuples that are stored in pkg_hash
+PKG_TIME = 0 # number of seconds for ctime function
+PKG = 1 # package full path as accepted by ebuild
+PKG_NAME = 2 # package name as accepted by emerge
+
+(status, pkg_files) = commands.getstatusoutput(
+               "find /var/db/pkg -iname '*.ebuild' -printf '%T@ %p\n' | sort -n")
+
+pkg_file_list = pkg_files.splitlines()
+
+pkg_hash = {}
+for time_pkg_pair in pkg_file_list:
+       (pkg_time, pkg) = time_pkg_pair.split()
+       pkg_time = int(pkg_time)
+       # This covers developer trees with not-accepted categories
+       tmp_name = re.match(r'/var/db/pkg/(.*/.*)/.*', pkg)
+       if not tmp_name: continue
+       pkg_name = tmp_name.group(1)
+       tmp_core = re.match(r'(.*)-\d.*', pkg_name)
+       if not tmp_core: continue
+       pkg_core = tmp_core.group(1)
+       if pkg_hash.has_key(pkg_core):
+               pkg_hash[pkg_core].append((pkg_time, pkg, pkg_name))
+       else:
+               pkg_hash[pkg_core] = [(pkg_time, pkg, pkg_name)]
+
+total_len = len(pkg_hash.keys())
+curpkg = 0
+tmpname = os.tmpnam()
+assume_yes = 0
+
+if len(sys.argv) > 1:
+       if sys.argv[1] in ["-y", "--yes"]:
+               assume_yes = 1
+       elif sys.argv[1] in ["-h", "--help"]:
+               print """pkg-clean [options]
+
+-y, --yes       Don't ask for individual confirmation before unmerging; assume yes.
+"""
+               sys.exit(0)
+
+for pkg_core in pkg_hash.keys():
+       print "Examining %s:" % (pkg_core)
+       if len(pkg_hash[pkg_core]) < 2:
+               continue
+       unmerged_indexes = []
+       
+       curpkg += 1
+       choices = ""
+       idx = 1
+       for pkg_tuple in pkg_hash[pkg_core]:
+               choices += " %d \"%s %s\" 0" % \
+                          (idx, time.ctime(pkg_tuple[PKG_TIME]),
+                           pkg_tuple[PKG_NAME])
+               idx += 1
+
+       params = "dialog --separate-output --backtitle \"pkg-clean processing package %d of %d\" " % ( curpkg, total_len)
+       params += "--checklist \"Select which package(s) to unmerge\" 20 70 12" + choices
+       res = os.system(params + " 2> " + tmpname)
+       if res:
+               sys.exit(0)
+       
+       ins = open(tmpname)
+       for j in ins.readlines():
+               idx = int(j)
+               if idx == 0:
+                       break
+
+               full_path = pkg_hash[pkg_core][idx-1][PKG]
+               ebuild = full_path.replace('/var/db/pkg','')
+
+               if not assume_yes: 
+                       params = "dialog --backtitle \"" + ebuild + "\" " + \
+                                "--yesno \"Are you sure you want to unmerge " + ebuild + " ?\" 20 70"
+                       res = os.system(params)
+               else:
+                       res = 0
+
+               if res == 0:
+                       (status, unmerge_out) = commands.getstatusoutput(
+                               "ebuild %s unmerge" % (full_path))
+                       print unmerge_out
+                       time.sleep(2)
+                       if status != 0:
+                               sys.exit(status)
+       ins.close()
diff --git a/deprecated/old-scripts/pkg-clean.1 b/deprecated/old-scripts/pkg-clean.1
new file mode 100644 (file)
index 0000000..7a295f3
--- /dev/null
@@ -0,0 +1,20 @@
+.TH pkg\-clean "1" "Nov 2003" "gentoolkit"
+.SH NAME
+pkg\-clean \- Gentoo: Clean obsolete packages
+.SH SYNOPSIS
+.B pkg\-clean
+.SH BUGS
+This tool is obsolete, as of gentoolkit 0.2.0.
+Use 'emerge clean' or 'emerge depclean' (with caution; read the man page) 
+instead.
+
+.SH SEE ALSO
+.BR emerge(1)
+.br
+.BR /usr/sbin/pkg\-clean
+
+.SH AUTHORS
+This informative man page was written by Karl Trygve Kalleberg 
+<karltk@gentoo.org> and expanded by Katerina Barone\-Adesi 
+<katerinab@gmail.com>.
+
diff --git a/deprecated/old-scripts/pkg-size b/deprecated/old-scripts/pkg-size
new file mode 100644 (file)
index 0000000..8770db7
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/sh
+# Copyright 1999-2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+# $Header$
+# Author: Karl Trygve Kalleberg <karltk@gentoo.org>
+
+echo
+echo -e "\x1b[31;01m!!! As of Gentoolkit 0.2.0, this tool is deprecated."
+echo -e "!!!\x1b[0;0m Refer to 'equery size' for a replacement."
+echo
+
+spec=$1
+
+if [ -z "$spec" ] ; then
+  echo "Usage: pkg-size package"
+  exit 1
+fi
+
+name=`echo $1 | sed "s/\([^/]*\)\///"`
+category=`echo $1 | sed "s/\/.*//"`
+
+if [ "$category" == "$name" ] ; then
+       category=
+fi
+
+function tryfile() {
+       local foo
+       foo=/var/db/pkg/$1/CONTENTS
+       bar=`ls $foo 2> /dev/null`
+       for i in $bar ; do
+               if [ -f "$i" ] ; then
+                       echo $i
+                       break
+               fi
+       done
+}
+
+file=`tryfile "${category}/${name}"`
+if [ -z $file ] ; then
+ file=`tryfile "${category}/${name}*"`
+ if [ -z $file ] ; then
+  file=`tryfile "${category}*/${name}"`
+  if [ -z $file ] ; then
+   file=`tryfile "${category}*/${name}*"`
+   if [ -z $file ] ; then
+    echo "!!! Package resembling ${category}/${name} not found"
+    exit 1
+   fi
+  fi
+ fi
+fi
+
+pkgname=`echo $file | sed -e "s:\/var\/db\/pkg\/::" -e "s:\/CONTENTS::"`
+
+totals=`cat $file|grep "obj"|awk '{ print $2 }' | sed "s/ /\\ /" | xargs du -scb | grep total | cut -f 1`
+
+size=0
+for i in $totals ; do 
+       size=$[size+i]
+done
+
+echo "$pkgname $size ($[size/1024]KB)"
+
diff --git a/deprecated/old-scripts/pkg-size.1 b/deprecated/old-scripts/pkg-size.1
new file mode 100644 (file)
index 0000000..b195412
--- /dev/null
@@ -0,0 +1,11 @@
+.TH pkg-size "1" "Nov 2003" "gentoolkit"
+.SH NAME
+pkg-size \- Gentoo: Package size calculator
+.SH SYNOPSIS
+.B pkg-size 
+\fIpackage\fR
+.SH DESCRIPTION
+Calculate size of \fIpackage\fR.
+.SH AUTHORS
+\fBpkg-size\fR was written by Karl Trygve Kalleberg <karltk@gentoo.org>.
+
diff --git a/deprecated/pkg-clean/AUTHORS b/deprecated/pkg-clean/AUTHORS
new file mode 100644 (file)
index 0000000..f126a36
--- /dev/null
@@ -0,0 +1,5 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Authors:
+Leo Lipelis <aeoo@gentoo.org> (original author)
diff --git a/deprecated/pkg-clean/ChangeLog b/deprecated/pkg-clean/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/pkg-clean/README b/deprecated/pkg-clean/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/pkg-clean/pkg-clean b/deprecated/pkg-clean/pkg-clean
new file mode 100644 (file)
index 0000000..abe0159
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+# Copyright 1999-2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+# $Header$
+# Author: Leo Lipelis <aeoo@gentoo.org>
+# Author: Karl Trygve Kalleberg <karltk@gentoo.org>
+
+import commands
+import re
+import sys
+import time
+import os
+
+# constants for package tuples that are stored in pkg_hash
+PKG_TIME = 0 # number of seconds for ctime function
+PKG = 1 # package full path as accepted by ebuild
+PKG_NAME = 2 # package name as accepted by emerge
+
+(status, pkg_files) = commands.getstatusoutput(
+               "find /var/db/pkg -iname '*.ebuild' -printf '%T@ %p\n' | sort -n")
+
+pkg_file_list = pkg_files.splitlines()
+
+pkg_hash = {}
+for time_pkg_pair in pkg_file_list:
+       (pkg_time, pkg) = time_pkg_pair.split()
+       pkg_time = int(pkg_time)
+       # This covers developer trees with not-accepted categories
+       tmp_name = re.match(r'/var/db/pkg/(.*/.*)/.*', pkg)
+       if not tmp_name: continue
+       pkg_name = tmp_name.group(1)
+       tmp_core = re.match(r'(.*)-\d.*', pkg_name)
+       if not tmp_core: continue
+       pkg_core = tmp_core.group(1)
+       if pkg_hash.has_key(pkg_core):
+               pkg_hash[pkg_core].append((pkg_time, pkg, pkg_name))
+       else:
+               pkg_hash[pkg_core] = [(pkg_time, pkg, pkg_name)]
+
+total_len = len(pkg_hash.keys())
+curpkg = 0
+tmpname = os.tmpnam()
+assume_yes = 0
+
+if len(sys.argv) > 1:
+       if sys.argv[1] in ["-y", "--yes"]:
+               assume_yes = 1
+       elif sys.argv[1] in ["-h", "--help"]:
+               print """pkg-clean [options]
+
+-y, --yes       Don't ask for individual confirmation before unmerging; assume yes.
+"""
+               sys.exit(0)
+
+for pkg_core in pkg_hash.keys():
+       print "Examining %s:" % (pkg_core)
+       if len(pkg_hash[pkg_core]) < 2:
+               continue
+       unmerged_indexes = []
+       
+       curpkg += 1
+       choices = ""
+       idx = 1
+       for pkg_tuple in pkg_hash[pkg_core]:
+               choices += " %d \"%s %s\" 0" % \
+                          (idx, time.ctime(pkg_tuple[PKG_TIME]),
+                           pkg_tuple[PKG_NAME])
+               idx += 1
+
+       params = "dialog --separate-output --backtitle \"pkg-clean processing package %d of %d\" " % ( curpkg, total_len)
+       params += "--checklist \"Select which package(s) to unmerge\" 20 70 12" + choices
+       res = os.system(params + " 2> " + tmpname)
+       if res:
+               sys.exit(0)
+       
+       ins = open(tmpname)
+       for j in ins.readlines():
+               idx = int(j)
+               if idx == 0:
+                       break
+
+               full_path = pkg_hash[pkg_core][idx-1][PKG]
+               ebuild = full_path.replace("/var/db/pkg/", "")
+
+               if not assume_yes: 
+                       params = "dialog --backtitle \"" + ebuild + "\" " + \
+                                "--yesno \"Are you sure you want to unmerge " + ebuild + " ?\" 20 70"
+                       res = os.system(params)
+               else:
+                       res = 0
+
+               if res == 0:
+                       (status, unmerge_out) = commands.getstatusoutput(
+                               "ebuild %s unmerge" % (full_path))
+                       print unmerge_out
+                       time.sleep(2)
+                       if status != 0:
+                               sys.exit(status)
+       ins.close()
diff --git a/deprecated/pkg-clean/pkg-clean.1 b/deprecated/pkg-clean/pkg-clean.1
new file mode 100644 (file)
index 0000000..7a295f3
--- /dev/null
@@ -0,0 +1,20 @@
+.TH pkg\-clean "1" "Nov 2003" "gentoolkit"
+.SH NAME
+pkg\-clean \- Gentoo: Clean obsolete packages
+.SH SYNOPSIS
+.B pkg\-clean
+.SH BUGS
+This tool is obsolete, as of gentoolkit 0.2.0.
+Use 'emerge clean' or 'emerge depclean' (with caution; read the man page) 
+instead.
+
+.SH SEE ALSO
+.BR emerge(1)
+.br
+.BR /usr/sbin/pkg\-clean
+
+.SH AUTHORS
+This informative man page was written by Karl Trygve Kalleberg 
+<karltk@gentoo.org> and expanded by Katerina Barone\-Adesi 
+<katerinab@gmail.com>.
+
diff --git a/deprecated/pkg-size/pkg-size b/deprecated/pkg-size/pkg-size
new file mode 100644 (file)
index 0000000..84dd7df
--- /dev/null
@@ -0,0 +1,66 @@
+#! /usr/bin/python
+#
+# $Header$
+#
+#  Distributed under the terms of the GNU General Public License v2
+#  Copyright (c) 2003 Karl Trygve Kalleberg
+
+import portage
+import pprint
+import sys
+import os
+
+__author__ = "Karl Trygve Kalleberg"
+__email__ = "karltk@gentoo.org"
+__version__ = "0.1.0"
+__productname__ = "pkg-size"
+__description__ = "Portage package size calculator"
+
+def find(name):
+    return portage.portdb.match(name)
+
+def print_size(cpv):
+    scpv=portage.catpkgsplit(cpv)
+    cat = scpv[0]
+    pnv = scpv[1]+"-"+scpv[2]
+    if scpv[3] != "r0":
+        pnv +="-"+scpv[3]
+    db=portage.dblink(cat,pnv,"")
+    size=0
+    uncounted = 0
+    if not os.path.exists(db.getpath()):
+        return
+    k=db.getcontents()
+    if not k:
+        return
+    for i in k:
+        try:
+            size += os.stat(i).st_size
+        except OSError:
+            uncounted += 1
+    s = cpv + ": " + str(size) + " bytes (" + str((size+512)/1024) + "KB)"
+    if uncounted > 0:
+        s += " (" + str(uncounted) + " file(s) not accessible)"
+    print s
+
+    
+def main():
+    # parse parameters
+    if len(sys.argv) < 2:
+        print "No arguments!"
+        return 
+    name = sys.argv[1]
+    candidates = find(name)
+    if len(candidates) == 0:
+        print "No candidate packages found!"
+        return
+
+    for i in candidates:
+            print_size(i)
+
+if __name__ == "__main__":
+    try:
+        main()
+    except KeyboardInterrupt:
+        print "Operation Aborted!"
+        
diff --git a/deprecated/qpkg/AUTHORS b/deprecated/qpkg/AUTHORS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/qpkg/ChangeLog b/deprecated/qpkg/ChangeLog
new file mode 100644 (file)
index 0000000..b25fd25
--- /dev/null
@@ -0,0 +1,5 @@
+2004-02-18 Brandon Low <lostlogic@gentoo.org>
+       * Fix a reported security issue, have a TMP location that is process specific
+
+2004-01-07 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added Makefile
diff --git a/deprecated/qpkg/Makefile b/deprecated/qpkg/Makefile
new file mode 100644 (file)
index 0000000..cccdee0
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+       echo "HATHERSAGE (n.) The tiny snippets of beard which coat the inside of a washbasin after shaving in it."
+
+dist:
+       mkdir -p ../../${distdir}/src/qpkg
+       cp Makefile README AUTHORS ChangeLog TODO qpkg qpkg.1 ../../${distdir}/src/qpkg/
+
+install:
+       install -d $(docdir)/deprecated/qpkg
+       install -m 0755 qpkg $(docdir)/deprecated/qpkg/
+       install -m 0644 qpkg.1 README AUTHORS ChangeLog $(docdir)/deprecated/qpkg/
diff --git a/deprecated/qpkg/README b/deprecated/qpkg/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/qpkg/TODO b/deprecated/qpkg/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/qpkg/qpkg b/deprecated/qpkg/qpkg
new file mode 100644 (file)
index 0000000..dd8344b
--- /dev/null
@@ -0,0 +1,581 @@
+#!/bin/bash
+#
+# qpkg - query portage package system for various information
+#
+# Copyright (c) Vitaly Kushneriuk <vitaly_kushneriuk@yahoo.com>
+# This program is distributed under the terms of GPL version 2.
+#
+# Maintainer: Brandon Low <lostlogic@gentoo.org>
+# Additional code thanks to:
+#            Josh Goebel <dreamer@firesedge.org>
+#
+# $Header$
+ID='$Id$'
+VERSION=0.`echo ${ID} | cut -d\  -f3`
+
+umask 0077
+
+TMP="$(mktemp -d -t qpkg-XXXXXX)"
+rm -rf ${TMP}
+mkdir -p ${TMP}
+
+PROG=`basename ${0}`
+
+# Parse args
+verb=0
+group="*"
+params=${#}
+while [ ${#} -gt 0 ]
+do
+       a=${1}
+       shift
+       case "${a}" in
+
+       -h|--help)
+               usage=y
+               break
+               ;;
+
+       -i|--info)
+               info=y
+               ;;
+
+       -d|--dups)
+               dups=y
+               inst=y
+               ;;
+
+       -q|--query-deps)
+               query=y
+               ;;
+
+       -s|--slot)
+               slot=y
+               ;;
+
+       -f|--find-file)
+               ffind=y
+               inst=y
+               ;;
+
+       -fp|--find-pattern)
+               ffind=y
+               fpat=y
+               inst=y
+               ;;
+
+       -I|--installed)
+               inst=y
+               ;;
+
+       -m|--masked)
+               grepmask="-L"
+               ;;
+
+       -n|--non-masked)
+               grepmask="-l"
+               ;;
+
+       -U|--uninstalled)
+               uninst=y
+               ;;
+
+       -g|--group)
+               group=$1
+               shift
+               ;;
+
+       -l|--list)
+               list=y
+               inst=y
+               ;;
+
+       -ct|--check-time|-tc|--time-check)
+               tcheck=y
+               inst=y
+               ;;
+
+       -cm|--check-md5|-mc|--md5-check)
+               mcheck=y
+               inst=y
+               ;;
+
+       -c|--check)
+               mcheck=y
+               tcheck=y
+               inst=y
+               ;;
+
+       -v|--verbose)
+               let $((verb++))
+               ;;
+
+       -vv)
+               let $((verb++))
+               let $((verb++))
+               ;;
+
+       -nc|--no-color|--nocolor|--no-colors|--nocolors)
+               nocolor=y
+               ;;
+
+       -*)
+               echo -e ${CY}${PROG}${NO}:${YL} Invalid option ${RD}$a 1>&2
+               usage=y
+               break
+               ;;
+       *)
+               if [ -n "${arg}" ]; then
+                       echo -e ${CY}${PROG}: ${YL}Only one argument supported
+                       usage=y
+                       break
+               fi
+               arg=$a
+               ;;
+               
+       esac
+done
+
+#This is a dumb way to handle things, take it out next time
+T="\t"
+
+#Set up colors
+if [ ! "${nocolor}" ]; then
+       NO="\x1b[0;0m"
+       BR="\x1b[0;01m"
+       CY="\x1b[36;01m"
+       RD="\x1b[31;01m"
+       GR="\x1b[32;01m"
+       YL="\x1b[33;01m"
+       BL="\x1b[34;01m"
+       STAR=" *"
+elif [ ! "${inst}" ] && [ ! "${uninst}" ]; then
+       STAR=" *"
+fi
+
+
+# check for option conflicts
+if [ "${inst}" -a "${uninst}" \
+       -o \( "${ffind}" -o "${list}" -o "${tcheck}" -o "${mcheck}" \) \
+       -a "${uninst}"  ]; then
+       echo -e ${CY}${PROG}${NO}:${YL} conflicting options/modes${NO}
+       usage=y
+fi
+
+if [ "${usage}" ]; then
+       echo -e "${CY}${PROG} v. ${VERSION}${NO}
+
+${CY}${PROG}${NO} is GenToolKit's \"query package\" tool, using it, you can
+find packages owning files on your filesystem, check the integrity
+of installed packages, and do other queries against installed or
+uninstalled packages.
+
+NOTICE: This tool will be phased out at some point in the 
+        future, please use equery instead.
+        Bugs are still fixed but new features won't be added.
+
+${BR}Usage:
+${T}${CY}${PROG}${NO} [${BR}options${NO}] [${YL}pkgname${NO}] [${BL}-g${YL} group${NO}] [${BL}-f${YL} <file>${NO}|${BL}-fp${YL} <pattern>${NO}]
+${T}${CY}${PROG}${NO} ${BL}--dups${NO} [${BL}--slot${NO}]
+${T}${CY}${PROG}${NO} ${BL}--help${NO}
+
+${BR}Duplicate Locating:
+  ${BL}-d,  --dups${NO}${T}${T}print packages that have multiple versions installed
+  ${BL}-s,  --slot${NO}${T}${T}make ${BL}-d${NO} SLOT only print dups of the same SLOT
+
+${BR}Package Selection:
+  ${BL}-f,  --find-file${NO}${T}finds package that owns file <file>
+  ${BL}-fp, --find-pattern${NO}${T}finds to package that owns file matching *<pattern>*
+  ${BL}-m,  --masked${NO}${T}${T}Include${YL} only${NO} masked packages
+  ${BL}-n,  --non-masked${NO}${T}Include${YL} only${NO} non-masked packages
+  ${BL}-I,  --installed${NO}${T}Include${YL} only${NO} installed packages
+  ${BL}-U,  --uninstalled${NO}${T}Include${YL} only${NO} uninstalled packages
+  ${BL}-g,  --group${NO}${T}${T}Find by group (can be combined with other searches)
+
+${BR}Information Selection:
+  ${BL}-l,  --list${NO}${T}${T}List package content
+  ${BL}-i,  --info${NO}${T}${T}Get package description and home page.
+  ${BL}-ct, --check-time${NO}
+  ${BL}-tc, --time-check${NO}${T}Verify package files timestamps
+  ${BL}-cm, --check-md5${NO}
+  ${BL}-mc, --md5-check${NO}${T}Verify package files md5
+  ${BL}-c,  --check${NO}${T}${T}Verify mtimes${YL} and${NO} md5.
+  ${BL}-q,  --query-deps${NO}${T}display all installed packages 
+${T}${T}${T}depending on selected packages
+
+${BR}Operation Modifiers:
+  ${BL}-nc, --no-color${NO}${T}don't use colors
+  ${BL}-v,  --verbose${NO}${T}Be more verbose [ can be repeated twice ]
+  ${BL}-vv${NO}${T}${T}${T}Same as ${BL}-v -v${NO}
+
+${YL}Notes${NO}: 
+${YL}*${NO} ${BL}-f${NO}, ${BL}-fp, ${BL}-d${NO}, ${BL}-l${NO}, ${BL}-ct${NO}, ${BL}-cm${NO}, and ${BL}-c${NO} apply only to installed packages.
+${YL}*${NO} Short options may not be combined on the command-line, yet.
+${YL}*${NO} The operation of some flags has been changed by the
+  stripping of version numbers from some output to see
+  the version numbers play with ${BL}-v${NO} and ${BL}-vv${NO}.
+${YL}*${NO} When using${BL} -f${NO} with ${BL}-l${NO} or ${BL}--check.. -v${NO} options, only
+  matching files will be displayed, unless ${BL}-v${NO} is doubled, 
+  (yet more verbose) or ${BL}-vv${NO} is used.
+
+
+${YL}Examples${NO}:
+  ${PROG} --dups               print duplicates oldest first
+  ${PROG} --dups -v    .. with versions
+  ${PROG}                      print list of installed packages
+  ${PROG} porta -I             print versions of installed portage
+  ${PROG} porta -i             .. + versions in portage tree + descriptions 
+                       and homepages
+  ${PROG} gawk -c -v   check integrity of all installed versions of gawk
+                       the older ones will have \"damaged\" files.
+  ${PROG} -f /bin/ls   print package(s) that own /bin/ls
+"
+       rm -rf ${TMP}
+       exit
+fi
+
+#For the --dups switch only
+if [ "${dups}" ]; then
+if [ "${grepmask}" ]; then
+       mask=`python -c 'import portage; print portage.settings["ACCEPT_KEYWORDS"];' 2> /dev/null`
+       echo -e "Currently accepted keywords: ${BL}${mask}${NO}"
+       echo -e
+       mask=`echo ${mask} | perl -pe 's/\s+/|/'`
+fi
+
+       #First dig out the list of packages with duplicates
+       find /var/db/pkg/ -iname "*${arg}*.ebuild" 2> /dev/null > ${TMP}qpkg.lst
+       dups=`cat ${TMP}qpkg.lst | cut -f7 -d/ |
+               sed -e 's:\.ebuild$::; s:-r[0-9]*$::; s:-[^-]*$::; /^$/d' |
+               sort | 
+               uniq -d`
+
+       #Next get all the exact versions
+       duppak=`cat ${TMP}qpkg.lst | fgrep "${dups}"`
+
+       #Now cut that down to the directory name so we can be smart
+       dirs=`sed -e 's:/[^/]*$::' ${TMP}qpkg.lst`
+
+       #Go through each package's DB and create a sortable file
+       #to play with
+       declare -i defcount=`cat /var/cache/edb/counter`
+       for DIR in ${dirs}
+       do      #Package COUNTER
+               NUM=`cat "${DIR}/COUNTER" 2> /dev/null`
+               [ -z "${NUM}" ] && NUM=defcount
+               #Package slot if requested
+               [ ${slot} ] && SLOT=`cat "${DIR}/SLOT"`
+               #Package fullname
+               PKG=`ls --color=no -1 ${DIR}/*.ebuild|cut -f5,7 -d"/"`
+               #Package basename
+               NAME=`echo "${PKG}"|sed -e 's:\.ebuild$::; s:-r[0-9]\+$::; s:-[0-9].*$::'`
+               echo "${NUM} ${PKG} ${NAME}${SLOT}"
+       #Finish loop, and sort that nice sortable file based on 
+       #installation order, and then based on package basename
+       #bash hates me so I decided to use a temp file
+       done |sort -t" " -k3 -k1g,2|uniq -D -f2 > ${TMP}qpkg.lst
+       duppak=`cat ${TMP}qpkg.lst`
+       rm ${TMP}qpkg.lst
+
+       #If max verbosity is set output with full path to each ebuild
+       if [ "${verb}" -gt 1 ]; then
+               echo -n "${duppak}"|cut -f2 -d" "| \
+                                   sed -e "s:^:${BL}/var/db/pkg/${BR}:" \
+                                       -e "s:\(/\)\([^/]*\)\(.ebuild\):\1${CY}\2${NO}\1\2\3:"
+
+       #If normal verbosity output package group, package name and package version
+       elif [ "${verb}" -gt 0 ]; then
+               echo -n "${duppak}"|cut -f2 -d" "| \
+                                   sed -e "s:\(^[^/]*/\)\(.*\)\(\.ebuild\):${BR}\1${CY}\2${NO}:"
+
+       #Otherwise just output package group and package name
+       else
+               echo -n "${duppak}"|cut -f2 -d" "| \
+                                   sed -e "s:-r[0-9]\+$::" \
+                                       -e "s:-[0-9].*$::" \
+                                       -e "s:\(^[^/]*/\)\(.*\):${BR}\1${CY}\2${NO}:"|uniq
+       fi
+       rm -rf ${TMP}
+       exit
+fi
+
+# get list of ebuilds to work on
+if [ "${ffind}" ]; then
+       # file find mode - list all ebuilds for 
+       # package/CONTENTS containing <arg>
+       if [ "${fpat}" ]; then
+               dirs=`ls /var/db/pkg/${group}/*/CONTENTS \
+               | xargs grep -l "${arg} " \
+               | xargs --no-run-if-empty -n 1 dirname`
+       else
+               # if the user didnt specify a full path assume they
+               # want to check in the working dir #17331
+               [ "${arg:0:1}" != "/" ] && arg="${PWD}/${arg}"
+
+               dirs=`ls /var/db/pkg/${group}/*/CONTENTS \
+               | xargs grep -Fl " ${arg} " \
+               | xargs --no-run-if-empty -n 1 dirname`
+       fi
+       ipak=`(
+       for d in ${dirs} -;do
+               [ "-" = "$d" ] && break
+               ls ${d}/*.ebuild
+       done)`
+else
+       # normal mode - list ebuilds for ebuild name containing <arg>
+
+       # installed packages
+       if [ ! "${uninst}" ]; then
+               ipak=`find /var/db/pkg/ -iname "*.ebuild" 2>/dev/null`
+               if [[ ${group} != "*" ]]; then
+                   ipak=`echo ${ipak}|sed -e "s: :\n:g"|grep ${group}`
+               fi
+               if [ ${arg} ]; then
+                       # avoid ${arg}="db" from pulling in every installed package
+                       temp="/var/db/pkg/.*${arg}"     
+                       ipak=`echo ${ipak}|sed -e "s: :\n:g"|grep -i ${temp}`
+               fi
+               if [ -n "${mask}" ]; then
+                   ipak=`echo ${ipak}|xargs -r egrep ${grepmask} "^KEYWORDS=.*[[:space:]\"\'](${mask})[[:space:]\"\']"`
+               fi
+       fi
+       # not installed packages (yet:-)
+       if [ ! "${inst}" ]; then
+               upak=`find /usr/portage/ -iname "*.ebuild" 2>/dev/null|grep -v --regex="/usr/portage/[^/]*\.ebuild"`
+               if [[ ${group} != "*" ]]; then
+                   upak=`echo ${upak}|sed -e "s: :\n:g"|grep ${group}`
+               fi
+               if [ ${arg} ]; then
+                   upak=`echo ${upak}|sed -e "s: :\n:g"|grep -i ${arg}`
+               fi
+               if [ -n "${mask}" ]; then
+                   upak=`echo ${upak}|xargs -r egrep ${grepmask} "^KEYWORDS=.*[[:space:]\"\'](${mask})[[:space:]\"\']"`
+               fi
+       fi
+fi
+
+X="\([^/]*\)"
+
+for p in ${ipak} ${upak} -;do
+       [ "${p}" = "-" ] && break
+
+       # cut common prefix from ebuild name and mark installed/uninstalled packages
+       # Note: iii/uuu will be replaced by the pipe at the end
+       n=${p%.ebuild}
+       var_db_pkg="/var/db/pkg/"
+       n=${n/${var_db_pkg}/iii }
+       usr_portage="/usr/portage/"
+       n=${n/${usr_portage}/uuu }
+       n=${n/\/*\//\/}
+
+       d=${p%\/*.ebuild} # faster d=`dirname ${p}`
+       echo ${n}
+       
+       # if we have no passed parameters then 
+       # we can skip the extra conditional checks
+       [[ ${params} == 0 ]] && continue;
+
+       if [ "${mask}" ]; then
+               keywords=`grep KEYWORDS ${p}| cut -d\" -f2`
+               echo -e "${T}Keywords: ${BL}${keywords}${NO}"
+       fi
+       
+       if [ ${verb} -gt 1 ];then
+               echo "vvv    ${p}"
+       fi
+       
+       if [ "${info}" ]; then
+               source ${p} 2> /dev/null
+               home="${HOMEPAGE}"
+               desc="${DESCRIPTION}"
+               #home=`grep HOMEPAGE ${p}| cut -d\" -f2`
+               #desc=`grep DESCRIPTION ${p}|cut -d= -f2-|cat`
+               echo -e "${T}${BL}${desc}${NO} [ ${YL}${home}${NO} ]"
+               if [ ${verb} -gt 0 ]; then
+                       pdir=${p/$(basename ${p})/}
+                       if [[ -r ${pdir}/USE && -r ${pdir}/IUSE ]]; then
+                               echo -n "Compiled with USE Flags: "
+                               for flag in $(<${pdir}/IUSE)
+                               do
+                                       use=$(grep -o ${flag} ${pdir}/USE | tr -d '\n')
+                                       if [[ "${use}" == "" ]]; then
+                                               echo -n "-"
+                                       fi
+                                       echo -n "${flag} "
+                               done
+                               echo
+                       fi
+               fi
+       fi
+       
+       if [ "${query}" ]; then
+               echo -e "${BL}DEPENDED ON BY:${NO}"
+               package="`echo ${n}|sed -e 's:-r[0-9]\+$::' \
+                                       -e 's:-[0-9].*$::' \
+                                       -e 's:^iii ::' \
+                                       -e 's:^uuu ::'`"
+               place="`echo ${n}|cut -f1 -d' '`"
+               [[ "${place}" == "iii" ]] && color="${GR}" || color="${RD}"
+
+               if [[ ${place} == "iii" ]]; then
+                       for deppkg in $(grep -R "${package}" /var/db/pkg/*/*/RDEPEND | sed 's/RDEPEND.*$//')
+                       do
+                               rdepend=$(< ${deppkg}/RDEPEND)
+
+                               for flag in $(< ${deppkg}/USE)
+                               do
+                                       if [[ "${flag:0:1}" == "-" ]]; then
+                                               rdepend="$(echo ${rdepend} | sed 's/${flag:1}? ( [[:alnum:][:punct:]]* )//')"
+                                       fi
+                               done
+
+                               if [[ $(echo ${rdepend} | grep -o ${package}) == ${package} ]]; then
+                                       echo $'\t'$(< ${deppkg}/PF)
+                               fi
+                       done
+               else
+                       grep -R "${package}" /var/db/pkg/*/*/RDEPEND | \
+                       cut -f5,6 -d"/" | sed -e "s:^:\t${color}:;s:$:${NO}:" | sort -u
+               fi
+               if [[ $(grep -R "*${package}" /etc/make.profile/packages) != "" ]]; then
+                       echo -e "\t${color}SYSTEM PROFILE${NO}"
+               fi
+       fi
+
+       # cat package content, remove obj/sym/dir, md5 and mtime when not verbose
+       # display only match in file-find mode unless extra verbose
+       if [ "${list}" ]; then
+               echo -e ${BL}CONTENTS:${NO}
+
+               if [ ${verb} -gt 1 ]; then
+                       cat ${d}/CONTENTS
+               else
+                       if [ "${ffind}" ]; then
+                               if [ "${fpat}" ]; then
+                                       grep "${arg}[:blank:]" $d/CONTENTS
+                               else
+                                       grep " ${arg}\( .*\)*$" $d/CONTENTS
+                               fi
+                       else
+                               cat $d/CONTENTS
+                       fi |
+                       if [ ${verb} -gt 0 ]; then
+                               cat
+                       else
+                               sed -e "s:\(^obj \)\(.*\)\( .*\)\{2\}$:\1${BR}\2${NO}:;
+                                       s:\(^sym \)\(.*\)\( -> \)\(.*\)\( .*\)\{2\}$:\1${CY}\2${NO}\3\4:;
+                                       s:\(^dir \)\(.*\)$:\1${YL}\2${NO}:"
+                       fi
+               fi
+
+               echo
+               
+       # check files mtime and md5, display summary at the end
+       elif [ "${tcheck}" -o "${mcheck}" ]; then
+               # counters
+               fe=0
+               fs=0
+               # read the CONTENTS file and check md5 and mtime if needed
+               # process only matching files in find-file mode unless extra verbose
+               cat ${d}/CONTENTS | 
+                       if [ "${ffind}" -a ${verb} -lt 2 ];then 
+                               if [ "${fpat}" ]; then
+                                       grep "${arg}"
+                               else
+                                       grep " ${arg} "
+                               fi
+                       else
+                               cat
+                       fi |
+               (
+               while read -a line
+               do
+                       fs=$((fs + 1))
+                       
+                       unset md5
+                       unset _md5
+                       unset mtime
+                       unset _mtime
+                       unset err
+                       unset len
+                       
+                       len="${#line[*]}"
+                       if [ "${line[0]}" = "obj" ]; then
+                               name=
+                               for i in `seq 1 $((${len}-3))`; do
+                                       [ "${name}" ] && name="${name} ${line[${i}]}" || name="${line[${i}]}"
+                               done
+                       else
+                               name="${line[1]}"
+                       fi
+                       
+                       missing=
+                       [ ! -e "${name}" ] && missing=1
+                       
+                       # colorize name and compute mtime/md5
+                       if [ "obj" = "${line[0]}" ]; then
+                               [ -e "${name}" ] && {
+                                       [ "${tcheck}" ] && mtime="${line[$((${len}-1))]}"
+                                       [ "${tcheck}" ] && _mtime=`date -r "${name}" +%s`
+
+                                       [ "${mcheck}" ] && md5=${line[$((${len}-2))]}
+                                       [ "${mcheck}" ] && _md5=`md5sum "${name}"|cut -f1 -d" "`
+                               }
+
+                               name="${BR}${name}${NO}"
+
+                       elif [ "sym" = "${line[0]}" ]; then
+                               name="${CY}${name}${NO}"
+
+                       elif [ "dir" = "${line[0]}" ]; then
+                               name="${YL}${name}${NO}"
+                       fi
+                       
+                       # compare
+                       if [ "${missing}" ]; then
+                               err=1
+                               name="${name} ${RD}!not exist!${NO}"
+                       fi
+                       if [ "${md5}" != "${_md5}" ]; then
+                               #If the md5 fails the first time check it with
+                               #everything changed to lowercase :-D
+                               md5="$(echo ${md5}|tr A-Z a-z)"
+                               if [ "${md5}" != "${_md5}" ]; then
+                                       err=1
+                                       name="${name} ${RD}!md5!${NO}"
+                               fi
+                       fi
+                       if [ "${mtime}" != "${_mtime}" ]; then
+                               err=1
+                               name="${name} ${RD}!mtime!${NO}"
+                       fi
+
+                       [ "${verb}" -gt 1 ] && echo -e "${name}"
+                       [[ "${verb}" -eq 1 ]] && [[ "${err}" -eq 1 ]] && echo -e "${name}"
+
+                       fe=$((fe + err))
+               done
+               if [ "${fe}" = "0" ]; then
+                       echo -e "${YL}${fe}${CY}/${fs}${NO}"
+               else
+                       echo -e "${RD}${fe}${CY}/${fs}${NO}"
+               fi
+               echo
+               )
+       fi
+       
+done | (
+       if [ ! \( "${tcheck}" -o "${mcheck}" -o "${info}" -o "${list}" -o "${query}" -o "${mask}" -o ${verb} -gt 0 \) ]; then
+               sed -e "s:-r[0-9]\+$::" -e "s:-[0-9][^-]*$::"|sort -k2|uniq -f1
+       elif [ ! \( "${tcheck}" -o "${mcheck}" -o "${info}" -o "${list}" -o "${query}" -o "${mask}" -o ${verb} -lt 2 \) ]; then
+               sort -k2|uniq -f1
+       else
+               cat
+       fi | sed \
+               -e "s:^iii ${X}/${X}:${BR}\1/${CY}\2${STAR}${NO}:" \
+               -e "s:^uuu ${X}/${X}:${BR}\1/${YL}\2${NO}:" \
+               -e "s:^vvv \(.*\)$:${BL}\1${NO}:" \
+               -e "s:^obj ::;s:^sym ::;s:^dir ::"
+
+)
+rm -rf ${TMP}
diff --git a/deprecated/qpkg/qpkg.1 b/deprecated/qpkg/qpkg.1
new file mode 100644 (file)
index 0000000..6d45a93
--- /dev/null
@@ -0,0 +1,112 @@
+.TH "qpkg" "1" "1.6" "gentoolkit 0.1.11-r1" ""
+.SH "NAME"
+qpkg \- Gentoo: query package tool
+.SH "SYNOPSIS"
+.LP 
+.B qpkg\fR     [\fIoptions\fR] [\fIpkgname\fR] [\fI\-g group\fR]
+.br 
+       [\fI\-f <file>\fR|\fI\-fp <pattern>\fR]
+.TP 
+.B qpkg        \fI\-\-dups\fR  [\fI\-\-slot\fR]
+.TP 
+.B qpkg        \fI\-\-help\fR
+.SH "DESCRIPTION"
+qpkg is GenToolKit's "query package" tool, using it, you can find packages owning files on your filesystem, check the integrity of installed packages, and do other queries against installed or uninstalled packages.
+.SH "OPTIONS "
+.LP 
+.I Duplicate Locating:
+.LP 
+.B \-d, \-\-dups\fR            print packages that have multiple
+.br 
+                               versions installed
+.br 
+.B \-s, \-\-slot\fR            make \-d SLOT only print dups of the
+.br 
+                               same SLOT
+.LP 
+.I Package Selection:
+.LP 
+.B \-f, \-\-find\-file\fR      Finds package that owns file <file>
+.br 
+.B \-fp, \-\-find\-pattern\fR  Finds to package that owns file
+.br 
+                               matching *<pattern>*
+.br 
+.B \-I, \-\-installed\fR       Include only installed packages
+.br 
+.B \-U, \-\-uninstalled\fR     Include only uninstalled packages
+.br 
+.B \-g, \-\-group\fR           Find by group (can be combined with
+.br 
+                               other searches)
+.LP 
+.I Information Selection:
+.LP 
+.B \-l, \-\-list\fR            List package content
+.br 
+.B \-i, \-\-info\fR            Get package description and home page.
+.br 
+.B \-ct, \-\-check\-time
+.br 
+.B \-tc, \-\-time\-check\fR    Verify package files timestamps
+.br 
+.B \-cm, \-\-check\-md5
+.br 
+.B \-mc, \-\-md5\-check\fR     Verify package files md5
+.br 
+.B \-c, \-\-check\fR           Verify mtimes and md5.
+.br 
+.B \-q, \-\-query\-deps\fR     display all installed packages 
+.br 
+\fR                            depending on selected packages
+.LP 
+.I Operation Modifiers:
+.LP 
+.B \-nc, \-\-no\-color\fR      Don't use colors
+.br 
+.B \-v, \-\-verbose\fR         Be more verbose [2 levels]
+.br 
+.B \-vv\fR                             Same as \-v \-v
+.SH "NOTES"
+\fI\-f, \-fp, \-d, \-l, \-ct, \-cm, \fRand \fI\-c\fR apply only to installed packages.
+.br 
+.TP 
+Short options may not be combined on the command\-line, yet.
+.TP 
+The operation of some flags has been changed in version 1.6 by the stripping of version numbers from some output to see the version numbers play with \fI\-v\fR and \fI\-vv\fR.
+.TP 
+When using \fI\-f\fR with \fI\-l\fR or \fI\-\-check.. \-v\fR options, only matching files will be displayed, unless \fI\-v\fR is doubled, (yet more verbose), equivalent to \fI\-vv\fR.
+.TP 
+When using \fI\-q\fR, it is important to note that the querying of deps checks package names only, because qpkg is not advanced enough (nor can it reasonably made so) to check complete deps with versions.  Please use \fBdepclean\fR or \fBemerge --dep-clean\fR to more completely check the dependency sanity of your system.
+.SH "EXAMPLES"
+.LP 
+.B qpkg \fI\-\-dups\fR         print duplicates oldest first
+.br 
+.B qpkg \fI\-\-dups \-v\fR..   with versions
+.br 
+.B qpkg\fR                             print list of packages
+.br 
+.B qpkg\fR porta \fI\-I\fR             print versions of installed portage
+.br 
+.B qpkg porta \fI\-i\fR                .. + versions in portage tree + 
+.br 
+                               descriptions and homepages
+.br 
+.B qpkg gawk \fI\-c \-v\fR     check integrity all installed versions 
+.br 
+                               of gawk the older versions will have
+.br 
+                               "damaged" files.
+.br 
+.B qpkg \fI\-f\fR /bin/ls      print package(s) that own /bin/ls
+.SH "AUTHORS"
+Vitaly Kushneriuk <vitaly@gentoo.org>, 2002: qpkg
+.br 
+Karl Trygve Kalleberg <karltk@gentoo.org>, 2002: man page
+.br 
+Brandon Low <lostlogic@gentoo.org>, 2002: maintainance
+.SH "SEE ALSO"
+ebuild(5) 
+.TP 
+The \fI/usr/sbin/qpkg\fR script. 
+.TP 
diff --git a/deprecated/qpkg/qpkg.sh b/deprecated/qpkg/qpkg.sh
new file mode 100644 (file)
index 0000000..cdbe3c7
--- /dev/null
@@ -0,0 +1,520 @@
+#!/bin/bash
+#
+# qpkg - query portage package system for various information
+#
+# Copyright (c) Vitaly Kushneriuk <vitaly_kushneriuk@yahoo.com>
+# This program is distributed under the terms of GPL version 2.
+#
+# Maintainer: Brandon Low <lostlogic@gentoo.org>
+# Additional code thanks to:
+#            Josh Goebel <dreamer@firesedge.org>
+#
+# $Header$
+ID='$Id$'
+VERSION=0.`echo ${ID} | cut -d\  -f3`
+
+PROG=`basename ${0}`
+
+# Parse args
+verb=0
+group="*"
+params=${#}
+while [ ${#} -gt 0 ]
+do
+       a=${1}
+       shift
+       case "${a}" in
+
+       -h|--help)
+               usage=y
+               break
+               ;;
+
+       -i|--info)
+               info=y
+               ;;
+
+       -d|--dups)
+               dups=y
+               inst=y
+               ;;
+
+       -q|--query-deps)
+               query=y
+               ;;
+
+       -s|--slot)
+               slot=y
+               ;;
+
+       -f|--find-file)
+               ffind=y
+               inst=y
+               ;;
+
+       -fp|--find-pattern)
+               ffind=y
+               fpat=y
+               inst=y
+               ;;
+
+       -I|--installed)
+               inst=y
+               ;;
+
+       -m|--masked)
+               grepmask="-L"
+               ;;
+
+       -n|--non-masked)
+               grepmask="-l"
+               ;;
+
+       -U|--uninstalled)
+               uninst=y
+               ;;
+
+       -g|--group)
+               group=$1
+               shift
+               ;;
+
+       -l|--list)
+               list=y
+               inst=y
+               ;;
+
+       -ct|--check-time|-tc|--time-check)
+               tcheck=y
+               inst=y
+               ;;
+
+       -cm|--check-md5|-mc|--md5-check)
+               mcheck=y
+               inst=y
+               ;;
+
+       -c|--check)
+               mcheck=y
+               tcheck=y
+               inst=y
+               ;;
+
+       -v|--verbose)
+               let $((verb++))
+               ;;
+
+       -vv)
+               let $((verb++))
+               let $((verb++))
+               ;;
+
+       -nc|--no-color|--nocolor|--no-colors|--nocolors)
+               nocolor=y
+               ;;
+
+       -*)
+               echo -e ${CY}${PROG}${NO}:${YL} Invalid option ${RD}$a 1>&2
+               usage=y
+               break
+               ;;
+       *)
+               if [ -n "${arg}" ]; then
+                       echo -e ${CY}${PROG}: ${YL}Only one argument supported
+                       usage=y
+                       break
+               fi
+               arg=$a
+               ;;
+               
+       esac
+done
+
+#This is a dumb way to handle things, take it out next time
+T="\t"
+
+#Set up colors
+if [ ! "${nocolor}" ]; then
+       NO="\x1b[0;0m"
+       BR="\x1b[0;01m"
+       CY="\x1b[36;01m"
+       RD="\x1b[31;01m"
+       GR="\x1b[32;01m"
+       YL="\x1b[33;01m"
+       BL="\x1b[34;01m"
+       STAR=" *"
+elif [ ! "${inst}" ] && [ ! "${uninst}" ]; then
+       STAR=" *"
+fi
+
+
+# check for option conflicts
+if [ "${inst}" -a "${uninst}" \
+       -o \( "${ffind}" -o "${list}" -o "${tcheck}" -o "${mcheck}" \) \
+       -a "${uninst}"  ]; then
+       echo -e ${CY}${PROG}${NO}:${YL} conflicting options/modes${NO}
+       usage=y
+fi
+
+if [ "${usage}" ]; then
+       echo -e "${CY}${PROG} v. ${VERSION}${NO}
+
+${CY}${PROG}${NO} is GenToolKit's \"query package\" tool, using it, you can
+find packages owning files on your filesystem, check the integrity
+of installed packages, and do other queries against installed or
+uninstalled packages.
+
+${BR}Usage:
+${T}${CY}${PROG}${NO} [${BR}options${NO}] [${YL}pkgname${NO}] [${BL}-g${YL} group${NO}] [${BL}-f${YL} <file>${NO}|${BL}-fp${YL} <patern>${NO}]
+${T}${CY}${PROG}${NO} ${BL}--dups${NO} [${BL}--slot${NO}]
+${T}${CY}${PROG}${NO} ${BL}--help${NO}
+
+${BR}Duplicate Locating:
+  ${BL}-d,  --dups${NO}${T}${T}print packages that have multiple versions installed
+  ${BL}-s,  --slot${NO}${T}${T}make ${BL}-d${NO} SLOT only print dups of the same SLOT
+
+${BR}Package Selection:
+  ${BL}-f,  --find-file${NO}${T}finds package that owns file <file>
+  ${BL}-fp, --find-pattern${NO}${T}finds to package that owns file matching *<pattern>*
+  ${BL}-m,  --masked${NO}${T}Include${YL} only${NO} masked packages
+  ${BL}-n,  --non-masked${NO}${T}Include${YL} only${NO} non-masked packages
+  ${BL}-I,  --installed${NO}${T}Include${YL} only${NO} installed packages
+  ${BL}-U,  --uninstalled${NO}${T}Include${YL} only${NO} uninstalled packages
+  ${BL}-g,  --group${NO}${T}${T}Find by goup (can be combined with other searches)
+
+${BR}Information Selection:
+  ${BL}-l,  --list${NO}${T}${T}List package content
+  ${BL}-i,  --info${NO}${T}${T}Get package description and home page.
+  ${BL}-ct, --check-time${NO}
+  ${BL}-tc, --time-check${NO}${T}Verify package files timestamps
+  ${BL}-cm, --check-md5${NO}
+  ${BL}-mc, --md5-check${NO}${T}Verify package files md5
+  ${BL}-c,  --check${NO}${T}${T}Verify mtimes${YL} and${NO} md5.
+  ${BL}-q,  --query-deps${NO}${T}display all installed packages 
+${T}${T}${T}depending on selected packages
+
+${BR}Operation Modifiers:
+  ${BL}-nc, --no-color${NO}${T}don't use colors
+  ${BL}-v,  --verbose${NO}${T}Be more verbose [ can be repeated twise ]
+  ${BL}-vv${NO}${T}${T}${T}Same as ${BL}-v -v${NO}
+
+${YL}Notes${NO}: 
+${YL}*${NO} ${BL}-f${NO}, ${BL}-fp, ${BL}-d${NO}, ${BL}-l${NO}, ${BL}-ct${NO}, ${BL}-cm${NO}, and ${BL}-c${NO} apply only to installed packages.
+${YL}*${NO} Short options may not be combined on the command-line, yet.
+${YL}*${NO} The operation of some flags has been changed by the
+  stripping of version numbers from some output to see
+  the version numbers play with ${BL}-v${NO} and ${BL}-vv${NO}.
+${YL}*${NO} When using${BL} -f${NO} with ${BL}-l${NO} or ${BL}--check.. -v${NO} options, only
+  matching files will be displayed, unless ${BL}-v${NO} is doubled, 
+  (yet more verbose) or ${BL}-vv${NO} is used.
+
+
+${YL}Examples${NO}:
+  ${PROG} --dups               print duplicates oldest first
+  ${PROG} --dups -v    .. with versions
+  ${PROG}                      print list of installed packages
+  ${PROG} porta -I             print versions of installed portage
+  ${PROG} porta -i             .. + versions in portage tree + descriptions 
+                       and homepages
+  ${PROG} gawk -c -v   check integrity all installed versions of gawk
+                       the older will have \"damaged\" files.
+  ${PROG} -f /bin/ls   print package(s) that own /bin/ls
+"
+       exit
+fi
+
+#For the --dups switch only
+if [ "${dups}" ]; then
+if [ "${grepmask}" ]; then
+       mask=`python -c 'import portage; print portage.settings["ACCEPT_KEYWORDS"];' 2> /dev/null`
+       echo -e "Currently accepted keywords: ${BL}${mask}${NO}"
+       echo -e
+       mask=`echo ${mask} | perl -pe 's/\s+/|/'`
+fi
+
+       #First dig out the list of packages with duplicates
+       find /var/db/pkg -iname "*${arg}*.ebuild" 2> /dev/null > /tmp/qpkg.lst
+       dups=`cat /tmp/qpkg.lst | cut -f7 -d/ |
+               sed -e 's:\.ebuild$::; s:-r[0-9]*$::; s:-[^-]*$::; /^$/d' |
+               sort | 
+               uniq -d`
+
+       #Next get all the exact versions
+       duppak=`cat /tmp/qpkg.lst | fgrep "${dups}"`
+
+       #Now cut that down to the directory name so we can be smart
+       dirs=`sed -e 's:/[^/]*$::' /tmp/qpkg.lst`
+
+       #Go through each package's DB and create a sortable file
+       #to play with
+       declare -i defcount=`cat /var/cache/edb/counter`
+       for DIR in ${dirs}
+       do      #Package COUNTER
+               NUM=`cat "${DIR}/COUNTER" 2> /dev/null`
+               [ -z "${NUM}" ] && NUM=defcount
+               #Package slot if requested
+               [ ${slot} ] && SLOT=`cat "${DIR}/SLOT"`
+               #Package fullname
+               PKG=`ls --color=no -1 ${DIR}/*.ebuild|cut -f5,7 -d"/"`
+               #Package basename
+               NAME=`echo "${PKG}"|sed -e 's:\.ebuild$::; s:-r[0-9]\+$::; s:-[0-9].*$::'`
+               echo "${NUM} ${PKG} ${NAME}${SLOT}"
+       #Finish loop, and sort that nice sortable file based on 
+       #installation order, and then based on package basename
+       #bash hates me so I decided to use a temp file
+       done |sort -t" " -k3 -k1g,2|uniq -D -f2 > /tmp/qpkg.lst
+       duppak=`cat /tmp/qpkg.lst`
+       rm /tmp/qpkg.lst
+
+       #If max verbosity is set output with full path to each ebuild
+       if [ "${verb}" -gt 1 ]; then
+               echo -n "${duppak}"|cut -f2 -d" "| \
+                                   sed -e "s:^:${BL}/var/db/pkg/${BR}:" \
+                                       -e "s:\(/\)\([^/]*\)\(.ebuild\):\1${CY}\2${NO}\1\2\3:"
+
+       #If normal verbosity output package group, package name and package version
+       elif [ "${verb}" -gt 0 ]; then
+               echo -n "${duppak}"|cut -f2 -d" "| \
+                                   sed -e "s:\(^[^/]*/\)\(.*\)\(\.ebuild\):${BR}\1${CY}\2${NO}:"
+
+       #Otherwise just output package group and package name
+       else
+               echo -n "${duppak}"|cut -f2 -d" "| \
+                                   sed -e "s:-r[0-9]\+$::" \
+                                       -e "s:-[0-9].*$::" \
+                                       -e "s:\(^[^/]*/\)\(.*\):${BR}\1${CY}\2${NO}:"|uniq
+       fi
+       exit
+fi
+
+# get list of ebuilds to work on
+if [ "${ffind}" ]; then
+       # file find mode - list all ebuilds for 
+       # package/CONTENTS containing <arg>
+       if [ "${fpat}" ]; then
+               dirs=`ls /var/db/pkg/${group}/*/CONTENTS \
+               | xargs grep -l "${arg}" \
+               | xargs --no-run-if-empty -n 1 dirname`
+       else
+               # if the user didnt specify a full path assume they
+               # want to check in the working dir #17331
+               [ "${arg:0:1}" != "/" ] && arg="${PWD}/${arg}"
+
+               dirs=`ls /var/db/pkg/${group}/*/CONTENTS \
+               | xargs grep -l " ${arg}\( .*\)*$" \
+               | xargs --no-run-if-empty -n 1 dirname`
+       fi
+       ipak=`(
+       for d in ${dirs} -;do
+               [ "-" = "$d" ] && break
+               ls ${d}/*.ebuild
+       done)`
+else
+       # normal mode - list ebuilds for ebuild name containing <arg>
+
+       # installed packages
+       if [ ! "${uninst}" ]; then
+               ipak=`find /var/db/pkg/ -iname "*.ebuild" 2>/dev/null`
+               if [[ ${group} != "*" ]]; then
+                   ipak=`echo ${ipak}|sed -e "s: :\n:g"|grep ${group}`
+               fi
+               if [ ${arg} ]; then
+                       # avoid ${arg}="db" from pulling in every installed package
+                       temp="/var/db/pkg/.*${arg}"     
+                       ipak=`echo ${ipak}|sed -e "s: :\n:g"|grep ${temp}`
+               fi
+               if [ -n "${mask}" ]; then
+                   ipak=`echo ${ipak}|xargs -r egrep ${grepmask} "^KEYWORDS=.*[[:space:]\"\'](${mask})[[:space:]\"\']"`
+               fi
+       fi
+       # not installed packages (yet:-)
+       if [ ! "${inst}" ]; then
+               upak=`find /usr/portage/ -iname "*.ebuild" 2>/dev/null|grep -v --regex="/usr/portage/[^/]*\.ebuild"`
+               if [[ ${group} != "*" ]]; then
+                   upak=`echo ${upak}|sed -e "s: :\n:g"|grep ${group}`
+               fi
+               if [ ${arg} ]; then
+                   upak=`echo ${upak}|sed -e "s: :\n:g"|grep ${arg}`
+               fi
+               if [ -n "${mask}" ]; then
+                   upak=`echo ${upak}|xargs -r egrep ${grepmask} "^KEYWORDS=.*[[:space:]\"\'](${mask})[[:space:]\"\']"`
+               fi
+       fi
+fi
+
+X="\([^/]*\)"
+
+for p in ${ipak} ${upak} -;do
+       [ "${p}" = "-" ] && break
+
+       # cut common prefix from ebuild name and mark installed/uninstalled packages
+       # Note: iii/uuu will be replaced by the pipe at the end
+       n=${p%.ebuild}
+       var_db_pkg="/var/db/pkg/"
+       n=${n/${var_db_pkg}/iii }
+       usr_portage="/usr/portage/"
+       n=${n/${usr_portage}/uuu }
+       n=${n/\/*\//\/}
+
+       d=${p%\/*.ebuild} # faster d=`dirname ${p}`
+       echo ${n}
+       
+       # if we have no passed parameters then 
+       # we can skip the extra conditional checks
+       [[ ${params} == 0 ]] && continue;
+
+       if [ "${mask}" ]; then
+               keywords=`grep KEYWORDS ${p}| cut -d\" -f2`
+               echo -e "${T}Keywords: ${BL}${keywords}${NO}"
+       fi
+       
+       if [ ${verb} -gt 1 ];then
+               echo "vvv    ${p}"
+       fi
+       
+       if [ "${info}" ]; then
+               home=`grep HOMEPAGE ${p}| cut -d\" -f2`
+               desc=`grep DESCRIPTION ${p}| cut -d\" -f2`
+               echo -e "${T}${BL}${desc}${NO} [ ${YL}${home}${NO} ]"
+       fi
+       
+       if [ "${query}" ]; then
+               echo -e "${BL}DEPENDED ON BY:${NO}"
+               package="`echo ${n}|sed -e 's:-r[0-9]\+$::' \
+                                       -e 's:-[0-9].*$::' \
+                                       -e 's:^iii ::' \
+                                       -e 's:^uuu ::'`"
+               place="`echo ${n}|cut -f1 -d' '`"
+               [[ "${place}" == "iii" ]] && color="${GR}" || color="${RD}"
+               grep -R "${package}" /var/db/pkg/*/*/RDEPEND | \
+               cut -f5,6 -d"/" | sed -e "s:^:\t${color}:;s:$:${NO}:" | sort | uniq
+#              gawk -F "/" '{printf("${place}\n\t%s/%s${NO}",$5,$6)}' | sort | uniq
+       fi
+
+       # cat package content, remove obj/sym/dir, md5 and mtime when not verbose
+       # display only match in file-find mode unless extra verbose
+       if [ "${list}" ]; then
+               echo -e ${BL}CONTENTS:${NO}
+
+               if [ ${verb} -gt 1 ]; then
+                       cat ${d}/CONTENTS
+               else
+                       if [ "${ffind}" ]; then
+                               if [ "${fpat}" ]; then
+                                       grep "${arg}" $d/CONTENTS
+                               else
+                                       grep " ${arg}\( .*\)*$" $d/CONTENTS
+                               fi
+                       else
+                               cat $d/CONTENTS
+                       fi |
+                       if [ ${verb} -gt 0 ]; then
+                               cat
+                       else
+                               sed -e "s:\(^obj \)\([^ ]*\)\(.*$\):\1${BR}\2${NO}:;
+                                       s:\(^sym \)\([^ ]*\)\( -> \)\([^ ]*\)\(.*$\):\1${CY}\2${NO}\3\4:;
+                                       s:\(^dir \)\([^ ]*\)\(.*$\):\1${YL}\2${NO}:"
+                       fi
+               fi
+
+               echo
+               
+       # check files mtime and md5, display summary at the end
+       elif [ "${tcheck}" -o "${mcheck}" ]; then
+               # counters
+               fe=0
+               fs=0
+               # read the CONTENTS file and check md5 and mtime if needed
+               # process only matching files in find-file mode unless extra verbose
+               cat ${d}/CONTENTS | 
+                       if [ "${ffind}" -a ${verb} -lt 2 ];then 
+                               if [ "${fpat}" ]; then
+                                       grep "${arg}"
+                               else
+                                       grep " ${arg} "
+                               fi
+                       else
+                               cat
+                       fi |
+               (
+               while read -a line
+               do
+                       fs=$((fs + 1))
+                       
+                       unset md5
+                       unset _md5
+                       unset mtime
+                       unset _mtime
+                       unset err
+
+                       name=${line[1]}
+                       
+                       missing=
+                       [ ! -e ${name} ] && missing=1
+                       
+                       # colorize name and compute mtime/md5
+                       if [ "obj" = ${line[0]} ]; then
+                               [ -e ${name} ] && {
+                                       [ "${tcheck}" ] && mtime=${line[3]}
+                                       [ "${tcheck}" ] && _mtime=`date -r ${name} +%s`
+
+                                       [ "${mcheck}" ] && md5=${line[2]}
+                                       [ "${mcheck}" ] && _md5=`md5sum ${name}|cut -f1 -d" "`
+                               }
+
+                               name=${BR}${name}${NO}
+
+                       elif [ "sym" = ${line[0]} ]; then
+                               name=${CY}${name}${NO}
+
+                       elif [ "dir" = ${line[0]} ]; then
+                               name=${YL}${name}${NO}
+                       fi
+                       
+                       # compare
+                       if [ "$missing" ]; then
+                               err=1
+                               name="${name} ${RD}!not exist!${NO}"
+                       fi
+                       if [ "${md5}" != "${_md5}" ]; then
+                               #If the md5 fails the first time check it with
+                               #everything changed to lowercase :-D
+                               md5=`echo "${md5}"|tr A-Z a-z`
+                               if [ "${md5}" != "${_md5}" ]; then
+                                       err=1
+                                       name="${name} ${RD}!md5!${NO}"
+                               fi
+                       fi
+                       if [ "${mtime}" != "${_mtime}" ]; then
+                               err=1
+                               name="${name} ${RD}!mtime!${NO}"
+                       fi
+
+                       [ ${verb} -gt 1 ] && echo -e ${name}
+                       [[ ${verb} -eq 1 ]] && [[ $err -eq 1 ]] && echo -e ${name}
+
+                       fe=$((fe + err))
+               done
+               if [ "$fe" = "0" ]; then
+                       echo -e ${YL}$fe${CY}/$fs${NO}
+               else
+                       echo -e ${RD}$fe${CY}/$fs${NO}
+               fi
+               echo
+               )
+       fi
+       
+done | (
+       if [ ! \( "${tcheck}" -o "${mcheck}" -o "${info}" -o "${list}" -o "${query}" -o "${mask}" -o ${verb} -gt 0 \) ]; then
+               sed -e "s:-r[0-9]\+$::" -e "s:-[0-9][^-]*$::"|sort -k2|uniq -f1
+       elif [ ! \( "${tcheck}" -o "${mcheck}" -o "${info}" -o "${list}" -o "${query}" -o "${mask}" -o ${verb} -lt 2 \) ]; then
+               sort -k2|uniq -f1
+       else
+               cat
+       fi | sed \
+               -e "s:^iii ${X}/${X}:${BR}\1/${CY}\2${STAR}${NO}:" \
+               -e "s:^uuu ${X}/${X}:${BR}\1/${YL}\2${NO}:" \
+               -e "s:^vvv \(.*\)$:${BL}\1${NO}:" \
+               -e "s:^obj ::;s:^sym ::;s:^dir ::"
+
+)
diff --git a/deprecated/useflag/AUTHORS b/deprecated/useflag/AUTHORS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/useflag/ChangeLog b/deprecated/useflag/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/useflag/README b/deprecated/useflag/README
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/deprecated/useflag/useflag b/deprecated/useflag/useflag
new file mode 100644 (file)
index 0000000..fd4cc08
--- /dev/null
@@ -0,0 +1,610 @@
+#!/bin/bash
+# useflag v0.3.1
+# Script to help users manage USE flags in Gentoo Linux
+#
+# Distributed under the terms of the GNU General Public License, v2 or later
+# Author: Michael Thompson <psionix@grandecom.net>, (c) 2002
+
+run_name=`basename $0`
+use_desc="/usr/portage/profiles/use.desc"
+make_conf_dir="/etc"
+make_conf="${make_conf_dir}/make.conf"
+# Home directory was chosen as the use of /tmp allows for symlink attacks
+make_temp="${HOME}/use.make.conf.tmp"
+use_cache_parent="/var/cache"
+use_cache_dir="${use_cache_parent}/use_desc"
+use_cache="${use_cache_dir}/use.cache"
+lock_cache="${use_cache_dir}/lock.cache"
+changes="0"
+
+
+# Get flag description
+# parm1 = Use flag to get description of
+do_get_desc() {
+       local parm1=$1
+       # Strip the comments and find the flag.
+       local out_get_desc=`grep -v "#" ${use_desc} | grep -w -e "${parm1} -"`
+       if [ "${out_get_desc}" = "" ]; then
+               local lcl_avail=`grep -v "#" ${use_desc} | cut -d ' ' -f1 | \
+                       grep -w -e "${parm1}"`
+               if [ "${lcl_avail}" != "" ]; then
+                       echo "${parm1} - No description available."
+               else
+                       echo "!!! ${parm1} does not exist."
+               fi
+       else
+               echo "${out_get_desc}"
+       fi
+}
+
+# Get the contents of the USE variable
+# parm1 controls whether or not to include the '-' with each flag
+do_get_make() {
+       local parm1=$1
+       # Get the USE flags from make.conf
+       # using `source` now instead of brain-damaged grepping
+       source ${make_conf}
+       local get_make_out=${USE}
+       # If called with "nodashes", then strip the leading dashes
+       if [ "${parm1}" = "nodashes" ]; then
+               for tr_sucks in ${get_make_out}; do
+                       if [ "${tr_sucks:0:1}" = "-" ]; then
+                               local tr_out="${tr_out} ${tr_sucks:1}"
+                       else
+                               local tr_out="${tr_out} ${tr_sucks}"
+                       fi
+               done
+               get_make_out="${tr_out}"
+       fi
+       echo "${get_make_out}"
+}
+
+# Yes, it's pointless.  But it could be used more than once in the future
+# so it's a function.
+# Gets the master list of available USE flags from use.desc
+do_get_avail() {
+       grep -v "#" ${use_desc} | cut -d " " -f1 | tr '\n' ' '
+}
+
+# Get deprecated flags.
+# parm1 = flag to check for deprecation
+# parm2 = list of available flags
+do_get_depr() {
+       local parm1=$1
+       local parm2=${@:2}
+       # This next var can't be local
+       get_depr_tmp=`echo "${parm2}" | tr ' ' '\n' | grep -x -e "${parm1}"`
+       local ret_code=$?
+       if [ "${ret_code}" != "0" ]; then
+               echo "${parm1}" | tr '\n' ' '
+       fi
+}
+
+# Removes a USE flag from make.conf
+# parm1 = flag to remove
+# use_rm_out = list of available flags
+do_use_rm() {
+       local parm1=$1
+       local use_rm_out=${@:2}
+       # Strip matching USE flags.  Yes, this is ugly, I know.
+       use_rm_out=`echo "${use_rm_out}" | tr ' ' '\n' | \
+               grep -x -v -e "${parm1}" | tr '\n' ' '`
+       # Also strip the inverse. Even uglier...
+       if [ "${parm1:0:1}" = "-" ]; then
+               use_rm_out=`echo "${use_rm_out}" | tr ' ' '\n' | \
+                       grep -x -v -e "${parm1:1}" | tr '\n' ' '`
+       else
+               use_rm_out=`echo "${use_rm_out}" | tr ' ' '\n' | \
+                       grep -x -v -e "-${parm1}" | tr '\n' ' '`
+       fi
+       echo "${use_rm_out}"
+}
+
+# Adds a USE flag to make.conf
+# parm1 = flag to add
+# use_add_out = list of available flags
+do_use_add() {
+       local parm1=$1
+       local use_add_out=${@:2}
+       # First strip existing flags (matching or inverse), then add.
+       # This is not the best way to do this.  Better would be to replace a
+       #  flag if it already exists.  That turned out to be a real PITA.
+       #  Maybe in a later version...
+       use_add_out=`do_use_rm ${parm1} ${use_add_out}`
+       use_add_out="${use_add_out} ${parm1}"
+       echo "${use_add_out}"
+}
+
+# Adds a flag to the locked flag cache
+# Pass list of flags to lock as parameter.
+do_lock_flags() {
+       local flag_list=$@
+       # Merge the new list of flags flags that are already locked.
+       if [ -r ${lock_cache} ]; then
+               local lock_old=`cat ${lock_cache}`
+       fi
+       flag_list="${lock_old} ${flag_list}"
+       # Remove duplicates.
+       echo "${flag_list}" | tr ' ' '\n' | sort | uniq | tr '\n' ' '
+}
+
+# Writes the list of locked flags to the cache file
+# Pass list of flags to write as parameter.
+do_write_lock() {
+       local write_flags=$@
+       if [ -r ${make_conf} ]; then
+               local make_prune=`do_get_make nodashes`
+       else
+               do_report_err ${make_conf} read
+       fi
+       # Be sure and remove any locked flags that no longer exist in USE.
+       for prune in ${write_flags}; do
+               local prune_test=`do_get_depr ${prune} ${make_prune}`
+               if [ "$prune_test" = "" ]; then
+                       local new_cache="${prune} ${new_cache}"
+               fi
+       done
+       if [ -w ${use_cache_parent} ]; then
+               mkdir -p ${use_cache_dir}
+               chmod 700 ${use_cache_dir}
+               echo "${new_cache}" > ${lock_cache}
+               chmod 600 ${lock_cache}
+       else
+               do_report_err ${lock_cache} write
+       fi
+}
+
+# Writes new USE variable to make.conf
+# Pass new list of USE flags as parameter.
+do_write_make() {
+       local use_write="USE=\"$@\""
+       local old_use="USE=\"`do_get_make dashes`\""
+       if [ -w ${make_conf} ] && [ -w ${make_conf_dir} ]; then
+               local use_write="USE=\"$@\""
+               local old_use=`grep "USE=\"" ${make_conf} | grep -v "#"`
+               local start_line=`grep -n "USE=\"" ${make_conf} | \
+                       grep -v "#" | cut -d ":" -f1`
+               if [ "${old_use:0-1}" != "\\" ]; then
+                       sed -e "s/${old_use}/${use_write}/" ${make_conf} > \
+                               ${make_temp}
+               else
+                       sed -e "s/${old_use}\\/${use_write}/" ${make_conf} > \
+                               ${make_temp}
+               fi
+               let start_line="${start_line} + 1"
+               if [ "${old_use:0-1}" != "\"" ]; then
+                       del_line=`head -n ${start_line} ${make_temp} | \
+                               tail -n 1`
+                       until [ "${del_line:0-1}" != "\\" ]; do
+                               let del_length="${#del_line} - 1"
+                               del_line="${del_line:0:${del_length}}"
+                               grep -v -w "${del_line}" ${make_temp} > \
+                                       ${make_temp}.2
+                               mv ${make_temp}.2 ${make_temp}
+                               del_line=`head -n ${start_line} \
+                                       ${make_temp} | tail -n 1`
+                       done
+                       let del_length="${#del_line} - 1"
+                       del_line="${del_line:0:${del_length}}"
+                       grep -v -x "${del_line}\"" ${make_temp} > \
+                               ${make_temp}.2
+                       mv ${make_temp}.2 ${make_temp}
+               fi
+               mv ${make_temp} ${make_conf}
+       else
+               do_report_err ${make_conf} write
+       fi
+}
+
+# Reports a read/write error and exits
+# parm1 = File to report on
+# parm2 = read, write, etc
+do_report_err() {
+       local parm1=$1
+       local parm2=$2
+       if [ "${parm2}" = "read" ]; then
+               echo "!!! Could not read ${parm1}"
+               echo -n "!!! Verify that file exists and that you have "
+               echo "appropriate permissions."
+       elif [ "${parm2}" = "write" ]; then
+               echo "!!! Could not write ${parm1}"
+               echo "!!! Got root?"
+       elif [ "${parm2}" = "nolock" ]; then
+               echo "!!! Could not read ${parm1}"
+               echo -n "!!! You have no locked flags or you have "
+               echo "insufficient permissions."
+       fi
+       exit 1
+}
+
+
+# The main section of the script
+# desc:
+# This is the feature for getting USE descriptions.
+if [ "$1" = "desc" ] || [ "$1" = "-i" ]; then
+       if [ -r ${use_desc} ]; then
+               for flag in ${@:2}; do
+                       do_get_desc ${flag}
+               done
+       else
+               do_report_err ${use_desc} read
+       fi
+
+# show:
+# This is the feature for showing the contents of the USE variable.
+elif [ "$1" = "show" ] || [ "$1" = "-s" ]; then
+       if [ -r ${make_conf} ]; then
+               do_get_make dashes
+       else
+               do_report_err ${make_conf} read
+       fi
+
+# del:
+# This is the feature for removing a USE flag.
+elif [ "$1" = "del" ] || [ "$1" = "-d" ]; then
+       if [ -r ${make_conf} ]; then
+               make_use=`do_get_make dashes`
+       else
+               do_report_err ${make_conf} read
+       fi
+       for flag in ${@:2}; do
+               # Strip leading dashes.
+               if [ "${flag:0:1}" = "-" ]; then
+                       flag="${flag:1}"
+               fi
+               del_test1=`do_get_depr ${flag} ${make_use}`
+               del_test2=`do_get_depr -${flag} ${make_use}`
+               if [ "${del_test1}" = "" ] || [ "${del_test2}" = "" ]; then
+                       changes="1"
+                       make_use=`do_use_rm ${flag} ${make_use}`
+               else
+                       echo "!!! ${flag} is not in your USE variable."
+               fi
+       done
+       if [ "${changes}" != "0" ]; then
+               do_write_make ${make_use}
+               # Prune deleted USE flags from lock cache
+               lock_flags=`do_lock_flags $2`
+               do_write_lock ${lock_flags}
+       fi
+
+# add:
+# This is the feature for explicitly enabling or disabling a USE flag.
+elif [ "$1" = "add" ] || [ "$1" = "-a" ]; then
+       if [ -r ${make_conf} ]; then
+               make_use=`do_get_make dashes`
+       else
+               do_report_err ${make_conf} read
+       fi
+       for flag in ${@:2}; do
+               changes="1"
+               make_use=`do_use_add ${flag} ${make_use}`
+       done
+       if [ "${changes}" != "0" ]; then
+               do_write_make ${make_use}
+       fi
+
+# lock:
+# This is the feature to lock a deprecated USE flag to prevent removal
+elif [ "$1" = "lock" ] || [ "$1" = "-l" ]; then
+       if [ -r ${make_conf} ]; then
+               make_use=`do_get_make nodashes`
+       else
+               do_report_err ${make_conf} read
+       fi
+       for flag in ${@:2}; do
+               # Strip leading dashes.
+               if [ "${flag:0:1}" = "-" ]; then
+                       flag="${flag:1}"
+               fi
+               lock_test=`do_get_depr ${flag} ${make_use}`
+               if [ "${lock_test}" = "" ]; then
+                       lock_flags="${lock_flags} ${flag}"
+               else
+                       echo "!!! ${flag} is not in your USE variable."
+               fi
+       done
+       lock_out=`do_lock_flags ${lock_flags}`
+       do_write_lock ${lock_out}
+
+# unlock:
+# This is the feature to unlock a deprecated USE flag to allow removal
+elif [ "$1" = "unlock" ] || [ "$1" = "-k" ]; then
+       if [ -r ${lock_cache} ]; then
+               lock_out=`cat ${lock_cache}`
+       else
+               do_report_err nolock
+       fi
+       for flag in ${@:2}; do
+               # Strip leading dashes.
+               if [ "${flag:0:1}" = "-" ]; then
+                       flag="${flag:1}"
+               fi
+               lock_flag_check=`do_get_depr ${flag} ${lock_out}`
+               if [ "${lock_flag_check}" = "" ]; then
+                       lock_out=`do_use_rm ${flag} ${lock_out}`
+               else
+                       echo "!!! ${flag} is not a locked flag."
+               fi
+       done
+       do_write_lock ${lock_out}
+
+# showlock:
+# This feature prints a list of USE flags that have been locked
+elif [ "$1" = "showlock" ] || [ "$1" = "-w" ]; then
+       if [ -r ${lock_cache} ]; then
+               cat ${lock_cache}
+       else
+               do_report_err nolock
+       fi
+
+# update:
+# This is the feature to update your USE by removing deprecated flags and
+#  handling new flags.
+elif [ "$1" = "update" ] || [ "$1" = "-u" ]; then
+       if [ -r ${make_conf} ]; then
+               # Get our USE but strip leading dashes
+               make_use=`do_get_make nodashes`
+       else
+               do_report_err ${make_conf} read
+       fi
+       # Get available USE flags from use.desc
+       if [ -r ${use_desc} ]; then
+               use_avail=`do_get_avail`
+       else
+               do_report_err ${use_desc} read
+       fi
+       # First we check for deprecated flags.
+       echo "Your USE variable currently looks like this:"
+       echo
+       echo `do_get_make dashes`
+       echo
+       # Print the list of locked flags if any exist.
+       if [ -r ${lock_cache} ]; then
+               lock_test=`cat ${lock_cache} | tr -d ' '`
+               if [ "${lock_test}" != "" ]; then
+                       echo "The following flags are locked:"
+                       cat ${lock_cache}
+                       echo
+               fi
+       fi
+       echo
+       echo "*** Checking for deprecated USE flags ..."
+       echo
+       for check_flag in ${make_use}; do
+               depr_ret=`do_get_depr ${check_flag} ${use_avail}`
+               flag_depr="${flag_depr}${depr_ret}"
+       done
+       # Filter out locked flags
+       if [ -r ${lock_cache} ] && [ "${lock_test}" != "" ]; then
+               lock_list=`cat ${lock_cache}`
+               for check_locks in ${flag_depr}; do
+                       lock_ret=`do_get_depr ${check_locks} ${lock_list}`
+                       lock_out="${lock_out}${lock_ret}"
+               done
+               flag_depr="${lock_out}"
+       fi
+       make_use=`do_get_make dashes`
+       if [ "${flag_depr}" = "" ]; then
+               echo "!!! No deprecated flags were found."
+       else
+               echo "The following USE flags appear to be deprecated:"
+               echo "${flag_depr}"
+               echo
+               echo "How would you like to handle them?"
+               echo "1) Handle them individually"
+               echo "2) Remove all deprecated flags"
+               echo "3) Don't remove any deprecated flags"
+               echo "4) Lock all deprecated flags"
+               echo
+               echo -n "Type (1, 2, 3, or 4): "
+               while [ "${luser_input}" = "" ]; do
+                       read luser_input
+                       case ${luser_input} in
+                       "2")
+                               changes="1"
+                               for flag in ${flag_depr}; do
+                                       make_use=`do_use_rm \
+                                               ${flag} ${make_use}`
+                               done
+                               echo
+                               echo -n "*** All deprecated flags were "
+                               echo "removed."
+                               ;;
+                       "3")
+                               echo
+                               echo "*** No flags were removed."
+                               ;;
+                       "1")
+                               for flag in ${flag_depr}; do
+                                       echo -n "${flag} appears to be "
+                                       echo -n "deprecated.  Remove it? "
+                                       echo -n "[Y]es/[N]o/[L]ock : "
+                                       luser_yn=""
+                                       while [ "${luser_yn}" = "" ]; do
+                                               read luser_yn
+                                               case ${luser_yn} in
+                                               "y" | "Y")
+                                                       changes="1"
+                                                       make_use=`do_use_rm \
+                                                               ${flag} \
+                                                               ${make_use}`
+                                                       ;;
+                                               "n" | "N")
+                                                       ;;
+                                               "l" | "L")
+                                                       wlk="${flag} ${wlk}"
+                                                       ;;
+                                               *)
+                                                       luser_yn=""
+                                                       ;;
+                                               esac
+                                       done
+                               done
+                               echo
+                               echo -n "*** All deprecated flags "
+                               echo "processed."
+                               ;;
+                       "4")
+                               wlk="${flag_depr}"
+                               echo
+                               echo "*** All deprecated flags were locked."
+                               ;;
+                       *)
+                               luser_input=""
+                               ;;
+                       esac
+               done
+       fi
+       if [ "${wlk}" != "" ]; then
+               do_write_lock ${wlk}
+       fi
+       # Now we check for new flags.
+       echo
+       echo
+       echo "*** Checking for new USE flags ..."
+       echo
+       # Load up our cached USE flags for comparison with use.desc
+       # Create the cache if it does not exist
+       if [ -w ${use_cache} ]; then
+               use_old=`cat ${use_cache}`
+               echo "${use_avail}" > ${use_cache}
+               chmod 600 ${use_cache}
+       elif [ -w ${use_cache_parent} ]; then
+               mkdir -p ${use_cache_dir}
+               chmod 700 ${use_cache_dir}
+               echo "${use_avail}" > ${use_cache}
+               chmod 600 ${use_cache}
+               use_old=""
+       else
+               do_report_err ${use_cache} write
+       fi
+       # Grab the contents of the USE variable.
+       make_cand=`do_get_make nodashes`
+       # Build a list of flags that do not exist in the USE variable.
+       for flag in ${use_avail}; do
+               new_cand="${new_cand}`do_get_depr ${flag} ${make_cand}`"
+       done
+       # Filter that list through the cached master list of flags.
+       for flag in ${new_cand}; do
+               new_flags="${new_flags}`do_get_depr ${flag} ${use_old}`"
+       done
+       if [ "${new_flags}" = "" ]; then
+               echo "!!! No new USE flags are available."
+       else
+               echo "The following new USE flags are available:"
+               echo "${new_flags}"
+               echo
+               echo "How would you like to handle them?"
+               echo "1) Handle them individually"
+               echo "2) Use Portage defaults (do not add to USE)"
+               echo "3) Explicitly enable all new flags"
+               echo "4) Explicitly disable all new flags"
+               echo
+               echo -n "Type (1, 2, 3, or 4): "
+               luser_input=""
+               while [ "${luser_input}" = "" ]; do
+                       read luser_input
+                       case ${luser_input} in
+                       "1")
+                               for h_flag in ${new_flags}; do
+                                       do_get_desc ${h_flag}
+                                       echo -n "How would you like to handle "
+                                       echo -n "${h_flag}? [e]nable, "
+                                       echo -n "[d]isable, [u]se default : "
+                                       luser_handle=""
+                                       while [ "${luser_handle}" = "" ]; do
+                                               read luser_handle
+                                               case ${luser_handle} in
+                                               "e" | "E")
+                                                       changes="1"
+                                                       make_use=`do_use_add \
+                                                               ${h_flag} \
+                                                               ${make_use}`
+                                                       echo
+                                                       ;;
+                                               "d" | "D")
+                                                       changes="1"
+                                                       make_use=`do_use_add \
+                                                               "-${h_flag}" \
+                                                               ${make_use}`
+                                                       echo
+                                                       ;;
+                                               "u" | "U")
+                                                       echo
+                                                       ;;
+                                               *)
+                                                       luser_handle=""
+                                                       ;;
+                                               esac
+                                       done
+                               done
+                               echo -n "*** All new flags have been "
+                               echo "processed."
+                               ;;
+                       "2")
+                               echo
+                               echo -n "*** No new flags were added to "
+                               echo "your USE."
+                               ;;
+                       "3")
+                               changes="1"
+                               for h_flag in ${new_flags}; do
+                                       make_use=`do_use_add ${h_flag} \
+                                               ${make_use}`
+                               done
+                               echo
+                               echo -n "*** All new flags were enabled in "
+                               echo "your USE."
+                               ;;
+                       "4")
+                               changes="1"
+                               for h_flag in ${new_flags}; do
+                                       make_use=`do_use_add \
+                                               "-${h_flag}" ${make_use}`
+                               done
+                               echo
+                               echo -n "*** All new flags were disabled in "
+                               echo "your USE."
+                               ;;
+                       *)
+                               luser_input=""
+                               ;;
+                       esac
+               done
+       fi
+       # Write the changes if necessary.
+       if [ "${changes}" != "0" ]; then
+               do_write_make ${make_use}
+               # Prune any locked flags that do not exist in the USE variable
+               lock_prot=`do_lock_flags fakeflag`
+               do_write_lock ${lock_prot}
+       fi
+       echo
+       echo
+       echo "*** Script finished ..."
+
+# Display USAGE statement for unhandled parameters
+else
+       echo "Usage:"
+       echo "  ${run_name} action [flag] [...]"
+       echo
+       echo "Actions:"
+       echo "-s, show  Displays the contents of the USE variable."
+       echo "-i, desc  Displays a description of one or more USE flags."
+       echo -n "-a, add                Adds the specified flag(s) to the USE "
+       echo "variable."
+       echo -n "-d, del                Deletes the specified flag(s) from "
+       echo "the USE variable."
+       echo "-l, lock  Locks the specified flag(s) to prevent deprecation."
+       echo -n "-k, unlock     Unlocks the specified flags to allow "
+       echo "deprecation."
+       echo "-w, showlock      Displays a list of locked flags."
+       echo -n "-u, update     Interactively updates the USE variable to "
+       echo "reflect changes"
+       echo "          to use.desc."
+       echo
+       exit 1
+fi
+exit 0
+
diff --git a/deprecated/useflag/useflag.1 b/deprecated/useflag/useflag.1
new file mode 100644 (file)
index 0000000..d321861
--- /dev/null
@@ -0,0 +1,69 @@
+.TH useflag "1" "May 2002" "gentoolkit"
+.SH NAME
+useflag \- manage and update Gentoo Linux USE flags
+.SH SYNOPSIS
+.B useflag
+\fIaction\fR [\fIflag\fR] [\fI...\fR]
+.SH DESCRIPTION
+The \fBuseflag\fR utility allows the user to manage Gentoo Linux USE flags through a simple command-line interface.  It allows quick and easy, single-command manipulation of the USE variable defined in \fI/etc/make.conf\fR.  It also simplifies the process of handling changes to the master list of USE flags defined in \fI/usr/portage/profile/use.desc\fR.
+.br
+
+It is important to note that a USE variable must exist in \fImake.conf\fR for this utility to work.  Be sure that the USE variable is uncommented.  It is OK for the USE variable to be empty.  Please be sure to back up \fImake.conf\fR before using this utility for the first time.
+.PP
+.SH ACTIONS
+.TP
+\fBshow, -s\fR
+Displays the raw contents of the USE variable as defined in \fImake.conf\fR.  The output contains only the flags themselves.
+.TP
+\fBdesc, -i [flag] ...\fR
+Displays a description of one or more USE flags specified on the command line.  The flags should be seperated by spaces and should not contain leading dashes.  Specifying a flag that does not exist returns a non-fatal error.
+.TP
+\fBadd, -a [[\-]flag] ...\fR
+Adds one or more specified flags to the USE variable defined in \fImake.conf\fR.  The flags are appended to the USE variable exactly as they appear on the command line.  If a specified flag already exists in the USE variable, it is removed before the new set of flags is appended.  The utility removes existing flags regardless of whether they are in an enabled or disabled state, allowing the user to enable or disable a flag with a single command.  The user may add flags that are not defined in the \fIuse.desc\fR master list.
+.TP
+\fBdel, -d [flag] ...\fR
+Deletes one or more specified flags from the USE variable defined in \fImake.conf\fR.  The enabled/disabled state of a flag in the USE variable as well as any dashes prepended to flags on the command line is ignored.  Attempting to delete a flag that is not in the USE variable returns a non-fatal error.  When a flag is deleted from the USE variable using this utility, it is automatically unlocked.
+.TP
+\fBlock, -l [flag] ...\fR
+Locks one or more specified flags that exist in the USE variable defined in \fImake.conf\fR.  Locked flags are not considered to be deprecated by the update function of this utility.  This allows the user to avoid being queried by the utility about deprecated, undocumented, or custom flags that the user wishes to preserve when performing an update.  A flag must exist in the USE variable in order to be locked.  The enabled/disabled state of a flag in the USE variable as well as any dashes prepended to flags on the command line is ignored.
+.TP
+\fBunlock, -k [flag] ...\fR
+Unlocks one or more specified USE flags.  This allows the update function to consider a flag deprecated if it no longer exists in the master list defined in \fIuse.desc\fR.  Any dashes prepended to flags on the command line are ignored.  Attempting to unlock flags that are not locked returns a non-fatal error.
+.TP
+\fBshowlock, -w\fR
+Displays the raw list of locked flags, seperated by spaces.
+.TP
+\fBupdate, -u\fR
+Interactively updates the USE variable defined in \fImake.conf\fR to reflect changes to the master list of USE flags defined in \fI use.desc\fR.
+.br
+
+First, the user is presented with the current raw contents of the USE variable.  The user is also shown the list of locked flags if any exist.
+.br
+
+Next, the USE variable is searched for flags that do not appear in the master list.  If any are found and they are not locked, then they are considered to be deprecated and are displayed to the user along with a list of options for handling them.  The user may choose to remove all of the flags, remove none of the flags, lock all of the flags, or handle each flag individually.
+.br
+
+Last, the master list is searched for any new flags that have become available since the last time the update function was run, and these are displayed to the user.  If this is the first time, then all flags not currently defined in the USE variable will be displayed.  The user will then be presented with a list of options for handling these flags.  The user may choose to add all of the new flags to the USE variable as enabled, add all of the new flags as disabled, use Portage defaults for all of the flags, or handle each flag individually.
+.SH FILES
+.TP
+\fI/etc/make.conf\fR
+Contains the USE variable that Portage uses to control build-time functionality.
+.TP
+\fI/usr/portage/profile/use.desc\fR
+Contains a master list of all documented USE flags along with their descriptions.
+.TP
+\fI/var/cache/use_desc/use.cache\fR
+Contains a cached list of flags from \fIuse.desc\fR.  This prevents the user from being repeatedly queried about flags that exist in \fIuse.desc\fR but not in the USE variable.  DO NOT EDIT THIS FILE MANUALLY.
+.TP
+\fI/var/cache/use_desc/lock.cache\fR
+Contains a list of USE flags that have been locked.  DO NOT EDIT THIS FILE MANUALLY.
+.SH AUTHOR
+Michael Thompson <psionix@grandecom.net>, 2002
+.SH SEE ALSO
+ebuild(1), ebuild(5), emerge(1), make.conf(5).
+.TP
+See \fI/usr/share/doc/gentoolkit-<version>/\fR for documentation on other gentoolkit utilities.
+.SH TIPS
+.TP
+Deleting \fI/var/cache/use_desc/use.cache\fR will allow the utility to query about all flags not currently defined in the USE variable.
+
diff --git a/gentoolkit-dev/AUTHORS b/gentoolkit-dev/AUTHORS
new file mode 100644 (file)
index 0000000..fdfccf3
--- /dev/null
@@ -0,0 +1,6 @@
+Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Maintenance
+
+See the AUTHOR file in the various src/<foo> subdirectories for a full
+log of who's done what with whome and when.
+
diff --git a/gentoolkit-dev/COPYING b/gentoolkit-dev/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gentoolkit-dev/ChangeLog b/gentoolkit-dev/ChangeLog
new file mode 100644 (file)
index 0000000..58a2b45
--- /dev/null
@@ -0,0 +1,902 @@
+2009-12-11: Christian Ruppert <idl0r@gentoo.org>
+       src/echangelog/test/test.sh, src/echangelog/Makefile:
+               Fix bug 292932, thanks to Alexis Ballier <aballier@gentoo.org>.
+
+2009-22-10: Christian Ruppert <idl0r@gentoo.org>
+       src/imlate/imlate: Some cleanup.
+               Disable metadata warnings.
+
+2009-22-10: Christian Ruppert <idl0r@gentoo.org>
+       src/imlate/imlate.1: Update man-page.
+
+2009-11-10: Christian Ruppert <idl0r@gentoo.org>
+       * src/echangelog/echangelog: Fix bug 288589, thanks to Kent Fredric     <kentfredric@gmail.com>.
+
+2009-28-09: Christian Ruppert <idl0r@gentoo.org>
+       * src/eshowkw/eshowkw: Make eshowkw a bit more POSIX compliant when using
+               /bin/sh, thanks to Daniel Pielmeier <billie@gentoo.org>.
+               Ignore comments when parsing arch.list, thanks to Fabian Groffen <grobian@gentoo.org>.
+
+2009-12-09: Christian Ruppert <idl0r@gentoo.org>
+       * src/echangelog/echangelog: Fix bug 284657, thanks to Andrew Gaffney <agaffney@gentoo.org>.
+               Cleanup VCS detection.
+               Cleanup.
+               Make --strict default, add --no-strict option.
+
+2009-09-09: Christian Ruppert <idl0r@gentoo.org>
+       * src/imlate/imlate: Bump to 0.0.4.
+               Add SLOT support.
+               Add option to specify the minimum mtime for the mtime check.
+               Add search by herd or maintainer.
+               Add single package mode.
+               Automatic detection of TARGET/MAIN-_ARCH.
+               Remove EXPERIMENTAL option, make it default.
+
+2009-09-06: Christian Ruppert <idl0r@gentoo.org>
+       * src/echangelog/echangelog: Fix regex, thanks to Magnus Granberg (zorry) <zorry@ume.nu>.
+
+2009-07-20: Christian Ruppert <idl0r@gentoo.org>
+       * README.Developer, README: Some clean-up.
+       * Makefile, Makefile.skel, makedefs.mak: Some minor improvements. Fix
+         docdir to use gentoolkit-dev instead of gentoolkit. Add all and clean
+         targets (skeletons) to makedefs.mak. Fix test target to be
+         parallel-build compatible. Add Makefile.skel as example for new tools.
+       * src/eshowkw/eshowkw.1, src/eshowkw/eshowkw,
+         src/eshowkw/Makefile: Add new tool eshowkw, thanks to Fabian Groffen
+         <grobian@gentoo.org>
+       * release.sh: Removed, I think it is not needed anymore.
+
+2009-07-19: Christian Ruppert <idl0r@gentoo.org>
+       * src/ebump/ebump: Use svn cp instead of cp if vcs == svn, thanks to Justin
+         Lecher (jlec) <jlec@j-schmitz.net>.
+       * src/ekeyword/Makefile src/eviewcvs/Makefile: Add a clean target to remove
+         generated manpages.
+
+2009-06-03: Christian Ruppert <idl0r@gentoo.org>
+       Cleanup, update ChangeLog, README, TODO.
+       * src/echangelog/echangelog: Add support for bzr. Cleanup. Removed git related if statement in check
+         for unknown/untracked files since it was useless.
+
+2009-05-26: Christian Ruppert <idl0r@gentoo.org>
+       * src/ebump/ebump: Fixed bug 271322, thanks to Justin Lecher (jlec)
+         <jlec@j-schmitz.net>.
+
+2009-05-15: Christian Ruppert <idl0r@gentoo.org>
+       * src/ekeyword/ekeyword: Fix keywording if the KEYWORDS line starts with
+         whitespace, thanks to Markus Meier <maekke@gentoo.org>.
+
+2009-05-12: Christian Ruppert <idl0r@gentoo.org>
+       * src/echangelog/echangelog: Fix regex, bug 269557, thanks to Samuli
+         Suominen <ssuominen@gentoo.org>.
+
+2009-05-09: Christian Ruppert <idl0r@gentoo.org>
+       * src/echangelog/echangelog: Ignore .git dir when running echangelog in
+         the repository root, fixes bug 199805 c8,c13. Improved
+         git_unknown_objects(), git status parsing improved. Thanks to Andrew
+         Gaffney <agaffney@gentoo.org> for testing etc.
+       * src/echangelog/echangelog: Update category/package when creating the 
+         initial ChangeLog, thanks to Serkan Kaba <serkan@gentoo.org>. Cleanup.
+
+2009-05-07: Christian Ruppert <idl0r@gentoo.org>
+       * echangelog: Re-add files (git) if the copyright has been updated, thanks
+       to Justin Lecher (jlec) <jlec@j-schmitz.net>.
+
+2009-05-06: Christian Ruppert <idl0r@gentoo.org>
+       * ekeyword: Improved die message, bug 257853.
+       * echangelog: Fixed git detection. Add support for project-wide variables
+       as suggested in bug 213374. Improved environment handling. Respect $PATH
+       while looking for git. Fixed stty call. Fix 'Use of uninitialized value in
+       concatenation' when calling echangelog on untouched ebuild directories
+       (Add an action for ChangeLog). Removed duplicate sort call. Don't eat
+       newlines, bug 264146, added new function text_fill      (modified 
+       Text::Wrap::fill). Add some useful information when using $EDITOR.
+       * ebump: Add support for git/svn, bug 256398. Use echangelog instead of
+       creating own ChangeLog entries. ebump calls ekeyword from now on (default).
+       Old option opt_add_cvs (now opt_add_vcs) and variables
+       AUTHORNAME/AUTHOREMAIL are deprecated, see bug 213374 and/or echangelog(1).
+
+2009-04-30: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add patch from loki_val to check -l dependencies in 
+       .la files (Bug #267898)
+
+2009-04-24: Paul Varner <fuzzyray@gentoo.org>
+       * ekeyword: Fix to handle multiline KEYWORDS (Bug #267250)
+
+2009-01-08: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix package.py to account for PORTDIR being a symbolic link
+       when checking if a package is in an overlay. (Bug #253968)
+
+2008-11-25: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fixes for non-linux Gentoo systems. Add patch from
+       igli to fix find command to comply with POSIX. Change awk calls to
+       gawk.
+
+2008-11-11: Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Add --strict option (Bug 246242).
+       * echangelog: Fix processing of virtual category (Bug 179530)
+
+2008-09-17: Paul Varner <fuzzyray@gentoo.org>
+       * euse: Fix check_sanity function to use get_all_make_defaults
+       function when checking for the make.defaults files in the profile.
+       (Bug #233651)
+
+2008-09-03: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix depgraph function to print out dependencies that don't
+       resolve to a package (Bug #236492)
+
+2008-08-26: Paul Varner <fuzzyray@gentoo.org>
+       * glsa-check: Fix has_key() deprecation message. (Bug #232797)
+       * revdep-rebuild: Update fix for Bug 232270 to utilize better patch
+       from Ian Abbott.
+
+2008-08-22: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix find_packages and find_installed_packages to print
+       a warning instead of a traceback when an InvalidAtom exception occurs.
+       (Bug #234358)
+       * equery: Fix equery belongs to strip multiple slashes from path
+       names. (Bug #234584)
+
+2008-07-24: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix equery check to convert mtime to an integer so that
+       comparisions always work. Thanks to Alexey Parshin for discovering the
+       problem and providing a patch. (Bug 232803)
+
+2008-07-22: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix gentoolkit.split_package_name to work with
+       newer portage.catpkgsplit that now returns a tuple instead of a
+       list. (Bug 232599)
+
+2008-07-21: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix filtering of masked paths from SEARCH_DIRS
+       variable. (Bug 232270)
+
+2008-07-18: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Remove prefixed '+/-' signs from IUSE for equery uses
+       command. (Bug 232019)
+
+2008-07-09: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to use TMPDIR instead of HOME for
+       temporary files. (Bug 203414)
+       * revdep-rebuild: Fix revdep-rebuild to not evaluate broken objects
+       multiple times. (Bug 220761)
+
+2008-07-09: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix gentoolkit to work without thread support in 
+       python. (Bug 223255)
+
+2008-06-16: Marius Mauch <genone@gentoo.org>
+       * euse: Add support for multi-parent profiles, account for missing 
+       final newline in make.conf
+
+2008-03-19: Paul Varner <fuzzyray@gentoo.org>
+       * glsa-check: Fix imports so mail functionality in glsa-check works
+       with python versions less than 2.5 (Bug 211706)
+
+2008-03-13: Paul Varner <fuzzyray@gentoo.org>
+       * euse: Add --info-installed option from patch provided by Andreas
+       Waidler. (Bug 212573)
+
+2008-03-13: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix trying to emerge an empty list of packages. (Bug
+       213294)
+
+2008-02-28: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix traceback when accessing the portage
+       db. (Bug #211716)
+
+2008-02-21: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: "Use /etc/init.d/functions.sh instead of
+       /sbin/functions.sh. (Bug 210940)
+
+2008-02-18: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Apply patch to allow combined short options.
+       (Bug 188343)
+       * revdep-rebuild: Don't duplicate broken file output. (Bug 201319)
+       * revdep-rebuild: unset GREP_OPTIONS to prevent problems with grep.
+       (Bug 189257)
+       * revdep-rebuild: Apply patch to prevent false matches of object names.
+       (Bug 196460)
+       * revdep-rebuild: Apply patch to better handle masked and removed
+       packages. (Bug 205227)
+       * revdep-rebuild: Filter SEARCH_DIRS_MASK paths from SEARCH_DIRS.
+       (Bug 194993)
+       * revdep-rebuild: Apply patch for revdep-rebuild portable find function.
+       (Bug 194234)
+       * equery: Fix equery list to not generate an internal portage error when
+       fed input with too many slashes. (Bug 119806)
+       * equery: Assume the -p flag when equery list -I is used by itself.
+       (Bug 106278)
+
+
+2007-10-09: Marius Mauch <genone@gentoo.org>
+       * glsa-check: Change "affected" target so it's based on "new" instead of 
+       "all" (IOW: exclude already applied/injected GLSAs).
+
+2007-10-05: Marius Mauch <genone@gentoo.org>
+       * glsa-check: Use UTF-8 strings to avoid EncodeErrors if a GLSA contains
+       non-ascii characters (bug #162493)
+
+2007-09-19: Paul Varner <fuzzyray@gentoo.org>
+       * epkginfo: Fix handling of KEYWORDS="" in an ebuild. (Bug #193108)
+       * revdep-rebuild: Fix handling of /var/db/pkg when it is a symbolic
+       link. (Bug #179392)
+
+2007-09-18: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Apply patch from Carlo Marcelo Arenas Belon to fix incorrect
+       display of masking status in list command. (Bug #188678)
+       * revdep-rebuild: Correctly handle LD_LIBRARY_MASK when checking for
+       "no version information" errors/ (Bug #182882)
+
+2007-09-12: Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Fix processing of the long arguments to work correctly. (Bug
+       #192345)
+       * revdep-rebuild: Correctly handle the case where an ebuild no longer
+       exists for a package (Bug #188918)
+       * eread: Fix eread to not accept invalid input for file selection.
+       (Bug #189994)
+
+2007-08-08: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix progress bar to only update when there is a
+       change (Bug #186945)
+       * revdep-rebuild: Ensure that we source functions.sh before calling
+       ewarn, etc.
+
+2007-08-06: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix processing of .la files (Bug #187453)
+       * revdep-rebuild: Add -X option back for backwards compatibilty (Bug
+       #187366)
+
+2007-07-30: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix grepping for non-existant package-owners file
+       (Bug #187141)
+
+2007-07-05: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Added refactored revdep-rebuild from Michael A.
+       Smith (Bug #184042)
+
+2007-05-30: Marius Mauch <genone@gentoo.org>
+       * glsa-check: check SLOT when selecting and displaying upgrades
+       * glsa-check: new --emergelike option to use the best version 
+       within the same SLOT instead of the one with the smallest delta.
+       * glsa-check: prefer visible upgrades to masked upgrades
+       * equery: check for and warn about unknown options (bug 119674)
+       * equery,eclean,glsa-check,epkginfo: Only add /usr/lib/portage/pym 
+       to python search path when necessary
+
+2007-05-21: Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Add patch from genstef to fix issues from Bug 176337
+
+2007-05-11: Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Changed permission check to see if you are either root or
+       belong to the portage group (Bug #177385)
+
+2007-05-11: Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Updated eclean to not delete metadata.dtd by default (Bug
+       #176951)
+
+2007-05-10: Marius Mauch <genone@gentoo.org>
+       * euse: Fix incorrect flag status display when a flag appears multiple
+       times in a single location
+
+2007-04-25: Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Re-added subversion/git support with fixes from genstef.
+       (Bug #136048)
+
+2007-04-24: Paul Varner <fuzzyray@gentoo.org>
+       * etcat: Removed from Makefile (deprecated since 04-25-2005)
+       * qpkg: Removed from Makefile (deprecated since 04-25-2005)
+
+2007-04-23: Paul Varner <fuzzyray@gentoo.org>
+       * genpkgindex, epkginfo: Move to /usr/bin from
+       /usr/lib/gentoolkit/bin (Bug #175759)
+
+2007-04-10: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Change equery uses to command to display the best matching
+       uninstalled package version if an uninstalled package is specified.
+       Changed the meaning of -a to mean display all versions. (Bug #152325)
+
+2007-04-01: Alec Warner <antarus@gentoo.org>
+       * eread: Fix path and fully qualified paths (Bug #172969)
+
+2007-03-31: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix traceback in equery which (Bug #134053)
+
+2007-03-29: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Change package.get_???_deps() methods to try the portage
+       tree first, since emerge always uses the portage tree for dependencies.
+       (Bug #164678)
+
+2007-03-29: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Convert deprecated strings functions to str methods (Bug
+       #172694)
+
+2007-03-25: Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Remove subversion/git patch due to many bugs.
+
+2007-03-18 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Change --no-color to --nocolor for consistency
+       within gentoolkit. (Bug #165165)
+
+2007-03-16 Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix typo in package.py (Bug #168347)
+
+2007-03-15 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix equery check to not fail for symlinks prefixed with ./
+       (Bug #170702)
+
+2007-03-14 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Trim trailing slash from query for equery belongs command
+       (Bug #170981)
+
+2007-03-13 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix bug with --package-names option not rebuilding
+       packages (Bug #169761)
+
+2007-03-10 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Add --depth option to equery depgraph to limit the depth of
+       the dependency graph. (Bug #115807)
+
+2007-03-09 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add support to detect "no version information
+       available" message from ldd (Bug #169973)
+
+2007-03-08 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Improved handling of KeyError in equery depends command
+       (Bug #169929)
+
+2007-03-07 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Change ordering algorithm to use --deep instead of
+       --emptytree on the advice of zmedico
+
+2007-02-26 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Display access information in verbose list mode (bug 168482)
+
+2007-02-19 Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Updated to support git and subversion (Bug #136048)
+
+2007-01-10 Paul Varner <fuzzyray@gentoo.org>
+       * epkgmove: removed epkgmove command due to popular demand. (Bug
+       161360)
+       * gensync: Deprecated gensync in favor of app-portage/layman (multiple
+       bugs)
+
+2007-01-02 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix equery depends --indirect command. (Bug #124552)
+
+2006-12-31 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Reworked equery depends command to be more accurate.
+
+2006-12-13 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix handling of /etc/portage/package.mask (Bug
+       #158025) Thanks to Wolfram Schlich for the patch.
+
+2006-12-12 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Add --tree option to equery files command. (Bug #62898)
+       Thanks to scope for the patch.
+
+2006-12-06 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Modify equery size command to work like the equery list
+       command for pkgspec arguments
+
+2006-11-27 Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Fix typographical error in help and man page. (Bug #156243)
+
+2006-10-11 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix fileAsStr to understand device files.
+       (http://forums.gentoo.org/viewtopic-p-3639575.html)
+
+2006-10-07 Paul Varner <fuzzyray@gentoo.org>
+       * euse: Fix quoting bug in get_real_path() (Bug #150335).
+
+2006-09-21 Paul Varner <fuzzyray@gentoo.org>
+       * eread: Add eread script for reading and managing portage ELOG files.
+       Thanks to Donnie Berkholz for writing this.
+
+2006-09-03 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Remove unused environment variables before calling
+       emerge (Bug #142074). Check for permissions to write temporary files
+       (Bug #142308)
+
+2006-08-12 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Add new --mail option to send out vulnerability reports
+       (output of --list plus --dump for each matched glsa as attachment),
+       using elog configuration.
+
+2006-07-31 Paul Varner <fuzzyray@gentoo.org>
+       * euse: Replace calls to readlink with bash function for Gentoo/ALT
+       compatibility. (Bugs #140477, #128960)
+
+2006-07-28 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to correctly handle --ask being
+       passed to emerge. Thanks to Sal Gonzalez <ghostx@optonline.net> for
+       the patch. (Bug #37485)
+
+2006-07-07 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Rename --no-path to --no-ld-path and change
+       functionality to not set LD_LIBRARY_PATH.  This fixes bug #96946 as
+       well as bug #137313
+       * revdep-rebuild: Apply patch from truedfx to fix bug #38751
+
+2006-07-05 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add --no-path option to revdep-rebuild for bug
+       #137313
+
+2006-06-25 Marius Mauch <genone@gentoo.org>
+       * glsa-check: update cve code for bug 128115
+
+2006-06-14 Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix package.py to honor $ROOT. (bug #136811)
+
+2006-05-22 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Use qfile to locate packages if portage-utils is
+       installed (Bug #128374). Be even more paranoid about extra slashes in
+       path names (Bug #128108). Remove unused code. Update configuration
+       section of manpage (Bug #126038).
+
+2006-04-02 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Remove double-slashes from path names (Bug #128108)
+
+2006-04-01 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add fix so that packages no longer in the tree cause
+       errors (Bug #128174). Fix case where masked packages cause
+       revdep-rebuild to not rebuild any packages (Bug #128085)
+
+2006-03-29 Marius Mauch <genone@gentoo.org>
+       * euse: Add support for special %active argument as placeholder for
+       active use flags
+
+2006-03-26 Aron Griffis <agriffis@gentoo.org>
+       * echangelog: Don't warn about missing ebuilds when updating 
+         copyrights #120061
+
+2006-03-25 Aron Griffis <agriffis@gentoo.org>
+       * eviewcvs: Update for sources.gentoo.org, add subversion support
+
+2006-03-21 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix to clear environment before portageq call. (Bug
+       #126038)
+
+2006-03-08 Paul Varner <fuzzyray@gentoo.org>
+       * genpkgindex: Add binary package indexing utility. (Bug 82132)
+
+2006-03-01 Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix depends parsing to properly handle conjunction. (bug
+       #123725). Thanks to tgl for the patch.
+       * gentoolkit: Added function to get post-merge dependencies (PDEPEND)
+       (bug #99191)
+       * gentoolkit: Change get_dependency functions to always use the
+       portage tree
+       * equery: Added post-merge dependencies to depends and depgraph
+       actions. (bug #99191)
+       * equery: Removed requirement for package to be installed to use
+       depgraph action.
+
+2006-02-16 Marius Mauch <genone@gentoo.org>
+       * euse: add/remove use flags even if there is no USE= statement in make.conf
+       (bug #95432)
+
+2006-02-16 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Fix bug causing the wrong summary to be displayed 
+       for --test --verbose (bug #123084)
+
+2006-02-06 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Reset PORTAGE_NICENESS, so that emerge is not niced
+       twice. Thanks to Lukas Reck for the patch. (Bug 121482)
+
+2006-01-24 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Use vbd information in verbose list mode (patch by solar)
+
+2006-01-18 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add capability to check libtool .la files for
+       non-existant references.
+
+2006-01-06 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to play nicely with portage-2.1
+       (Bug 118124)
+
+2005-12-28 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix to automatically determine how to call find (Bug 111203)
+
+2005-12-19 Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Add regular expression matching for exclude files (Bug 114365)
+
+2005-12-13 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix USE flag parsing. (Bug 115294)
+
+2005-12-07 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to work with findutils-4.2.27.
+       (bug 111203)
+       * equery: Added note to error message about quoting redirection
+       characters. (Bug 113423)
+       * gentoolkit: Removed python-config call from make file. (Bug 113386)
+
+2005-11-23 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Changed default behavior for equery list to search for
+       partial name matches. Added equery list examples to the equery man
+       page. (Bugs 113032, 113134)
+
+2005-11-15 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Changed several messages to stderr instead of stdout
+       * glsa-check: Added new --cve option as requested by solar (bug 98589)
+       * glsa-check: Added support for a EMERGE_OPTS env variable to modify the emerge call of glsa-check --fix
+       * glsa-check: Added a new target "affected"
+       * glsa-check: Removed the warning message as it is now pretty much tested
+       * glsa-check: Show GLSA title on --test if --verbose is also used
+       
+2005-11-11 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Added sanity check to equery files (Bug 75983)
+       * equery: Fix string matching for equery depends (Bug 85653)
+       * gentoolkit: Fix package.size() to report correct size for symbolic
+       links (Bug 90384)
+       * equery: Fix equery depgraph to show all dependencies (Bug 99191)
+       * equery: Fix traceback with invalid regular expression for equery
+       list (Bug 109392)
+
+2005-11-04 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix equery belongs to correctly work when passed an argument
+       list of multiple files (Bug 111501)
+
+2005-11-02 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix to work with findutils-4.2.25 (Bug 111203)
+
+2005-10-18 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Make equery look at both DEPEND and RDEPEND for dependencies
+       * gentoolkit: Fix _parse_deps to understand || syntax (Bug 101377)
+
+2005-10-14 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Add qpkg --dups functionality to equery list command (bug
+       109156)
+
+2005-10-13 Paul Varner <fuzzyray@gentoo.org>
+       * equery: equery depgraph shows USE flags (Bug 74554)
+       * equery: equery should properly parse use.local.desc (Bug 74569)
+       * equery: equery list escapes regular expressions (Bug 77113)
+       * equery: equery uses displays flags correctly (Bug 86633)
+       * equery: equery -N option to disable pipe detection (Bug 90046)
+       * equery: equery list properly detects version string (Bug 91286)
+       * equery: equery belongs now requires a filename (Bug 94618)
+       * equery: equery files over a pipe only prints file names (Bug 100148)
+       * revdep-rebuild: Fix typo in man page (Bug 109147)
+
+2005-09-25 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Update to read configuration files from
+       /etc/revdep-rebuild
+
+2005-09-23 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Sort output from equery list (bug 67152)
+       * equery: Update man page (Bugs 73893, 74944)
+       * equery: equery which returns best-visible ebuild (bug 78687)
+       * equery: equery --quiet is actually quiet (bug 78921)
+       * equery: Fixed typo in equery -h (bug 82352)
+       * gentoolkit: gentoolkit now uses a single portage.config object (bug
+       90680)
+       * equery: equery uses returns unique, sorted list (bug 91623)
+       * equery: equery always honors nocolor flag and settings (bug 98634)
+
+2005-09-08 Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Inital commit of eclean 0.4.1 from Thomas de Grenier de
+       Latour (tgl) <degrenier@easyconnect.fr> (bug 33877)
+
+2005-06-28 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Revert fix for bug 93574 as it can cause packages to
+       be missed. (bug 97171)
+
+2005-06-07 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Delete temporary files if the environment does not 
+       match the previous environment (bug 95274)
+
+2005-06-05 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Imported revdep-rebuild release from bug 62644
+       * revdep-rebuild: Major changes to the functionality when using 
+       --package-names/-X The script should now update slotted packages 
+       correctly. (bug 22161)
+       * revdep-rebuild: Customizable searching controlled through environment
+       variables.  This removes the need for end users to directly modify the
+       script. (bugs 32276, 38011, 59803)
+       * revdep-rebuild: The directories to search are no longer hard coded
+       into the script.  revdep-rebuild now determines the directories to
+       search based upon /etc/profile.env and /etc/ld.so.conf. (bugs 32276,
+       38011, 89781)
+       * revdep-rebuild: --ignore option to ignore temporary files left from
+       previous runs. Automatically ignore temporary files older than 24 hours.
+       (bug 34052)
+       * revdep-rebuild: Always return an exit status based upon success or
+       failure. (bug 38472)
+       * revdep-rebuild: Fixed to only emerge packages with direct missing
+       dependencies. (bug 38487)
+       * revdep-rebuild: New man page. (bug 40042)
+       * revdep-rebuild: emerge is no longer called with --nodeps. This allows
+       for needed dependencies to be pulled in. (bug 62893)
+       * revdep-rebuild: Cleaned up grammatical errors (bug 85278)
+       * revdep-rebuild: Added support for revdep-rebuild --soname 
+       /path/to/library.so (bug 91503)
+       * revdep-rebuild: Removed symbolically linked directories from search
+       (bug 93574)
+       * revdep-rebuild: --nocolor option to turn off colored output, the
+       script also obeys the NOCOLOR setting from /etc/make.conf.
+       * revdep-rebuild: Removed dependency on qpkg
+       * revdep-rebuild: Script uses PORTAGE_NICENESS from /etc/make.conf
+       * revdep-rebuild: Undocumented --keep-temp option.  This is primarily
+       for debugging/testing. This option prevents temporary files from being
+       deleted.
+       * revdep-rebuild: Changed --soname --soname-regexp options to --library
+       and treat all arguments as basic regular expressions. --soname and
+       --soname-regexp can still be used as options for backwards
+       compatability.
+       * revdep-rebuild: Removed requirement to keep revdep-rebuild and emerge
+       options distinct. Options that are unrecognized by revdep-rebuild are
+       passed directly to emerge.
+
+2005-04-30 Marius Mauch <genone@gentoo.org>
+       * glsa-check: add V to short option list so it actually works
+       * equery: added new option --name-only to belongs command to make it
+       "emerge-compatible"
+       
+2005-04-26 Marius Mauch <genone@gentoo.org>
+       * gentoolkit: fix broken Makefile
+       * gentoolkit: add some sticky tape to get the stupid thing working again
+       * equery: fix a few minor problems
+
+2005-04-25 Marius Mauch <genone@gentoo.org>
+       * qpkg: moving to /usr/share/doc/gentoolkit-*/deprecated
+       * etcat: moving to /usr/share/doc/gentoolkit-*/deprecated
+       * revdep-rebuild: replacing qpkg call with equivalent grep/sed call
+
+2005-04-07 Marius Mauch <genone@gentoo.org>
+       * euse: fixed bugs 74344, 75525 and 84521
+       * euse: add better support for cascaded profiles
+       * glsa-check: use --oneshot (bug 79819)
+       * glsa.py: fix stupid revision comparison bug (bug 75233)
+
+2005-03-12 Aron Griffis <agriffis@gentoo.org>
+       * Added eviewcvs to -dev, utility for generating viewcvs URLs
+
+2005-03-01 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Dropped epkgmove from the -dev
+       * Released gentoolkit-dev-0.2.3
+
+2005-03-01 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Released gentookit-dev-0.2.2
+
+2004-12-09 Marius Mauch <genone@gentoo.org>
+       * glsa.py: Another stupid bug, this time revisionMatch() broke as
+       ~foobar-rN isn't valid anymore
+
+2004-12-08 Marius Mauch <genone@gentoo.org>
+       * equery: implemented the --category option
+       * glsa-check: fixed the bug where it wanted to unnecessary merge masked
+       packages
+       * glsa-check: added a check to verify that all non-option arguments are
+       valid GLSAs
+       * glsa.py: changed the outfile parameter in Glsa.dump() to outstream so
+       we don't have to open/close a file which breaks pipes
+       * glsa.py: checks now for python versions below 2.3 and throws an
+       exception
+
+2004-11-29 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * branched v0-3-0: major rework in equery is in progess. the main
+       branch is reserved for minor and incremental fixups.
+
+2004-10-20 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * release.sh: New script that automates the relase of a new gentoolkit
+       relase. Only works for gentoolkit-dev at the moment.
+       * src/echangelog/Makefile: Fixed spurious '}'
+       * Released gentoolkit-dev-0.2.1
+
+2004-10-31 Marius Mauch <genone@gentoo.org>
+       * qpkg: security fix for bug #68846
+
+2004-10-20 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * etcat: fixed get_use_vars to get_use_flags, fixes #67349.
+
+2004-10-18 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: collapsed ChangeLog into base ChangeLog
+       * gentoolkit: reverted indenting back to tabs, due to loud protests
+       from Marius;)
+       * equery: collapsed ChangeLog into base ChangeLog
+       * equery: reverted indenting back to tabs, due to loud protests from
+       Marius;)
+       * equery: minor syntactical cleanups.
+       * equery: minor documentation improvements
+       * equery: added errors module that will hold various types of internal
+       errors raised.
+       * equery: added try block around on md5sum check, which fails on various
+       conditions like insufficient permission or stale temporary checksum
+       files.
+
+2004-10-17 Marius Mauch <genone@gentoo.org>
+       * equery: fix for bug #67473 (checking md5sums of prelinked binaries)
+       * equery: fix for bug #67275 (--nocolor didn't work as configure was
+       called before parsing the arguments
+       * equery: changed defaults for `equery depends` as making a depgraph for
+       the full portage tree isn't a good idea and find_all_packages() uses way
+       to much memory currently
+       * euse: replaced the old perl version with a newly written bash version.
+
+2004-10-12 Marius Mauch <genone@gentoo.org>
+       * equery: fix for bug #67210
+
+2004-10-10 Marius Mauch <genone@gentoo.org>
+       * Removed old-scripts directory from gentoolkit
+       * euse: added a errormessage that it doesn't support cascading profiles
+       * equery: small bugfixes
+       * equery: performance speedup for `equery belongs` by using portage
+       directly
+       * equery: added MD5 verification to `equery check`
+       * equery: renamed 'hasuses' to 'hasuse'
+       * equery: added filter patch for `equery files` from bug 43422, thanks
+       to degrenier@easyconnect.fr
+       * Released gentoolkit-0.2.0_pre10
+
+2004-10-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added unit tests for all supported commands
+       * equery: Fixed printing order and recognition of overlay, #53432.
+
+2004-10-11 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Split gentoolkit.py into helpers.py and package.py
+
+2004-10-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Fixed Makefiles to work with posix-compatible shells
+       * gentoolkit: Fixed is_overlay() to report properly, #53432.
+
+2004-10-06 Marius Mauch <genone@gentoo.org>
+       * glsa.py: Convert Unicode strings to ascii before passing them to
+       portage
+       * glsa.py: Some formatting fixes for dump()
+       * glsa.py: changed the matching routines so the reports are hopefully
+       more accurate
+       * glsa-check: added color support 
+       * glsa-check: added a --verbose option to show the warnings about
+       invalid GLSAs
+
+2004-09-30 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added unit tests for --help
+       * equery: Added unit tests for 'files'
+
+2004-09-27 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Added find_installed_packages
+       * equery: Added short commands
+       * equery: Fixed copyright dates
+       * equery: Fixed belongs to search only installed packages
+       * equery: Fixed reporting bug in uses command
+       * equery: Fixed reference to cppv
+       * equery: Added import of die
+       * equery: Added searching header to uses
+       * equery: Fixed hasuses to report properly
+       * Released gentoolkit-0.2.0_pre9
+       * Released gentoolkit-dev-0.2.0_pre4
+
+2004-09-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added man page rewrites by Katerina Barone-Adesi
+       <katerinab@gmail.com>, fixes #63045
+       * equery: Fixed spacing issues with files, fixes #63036.
+       * equery: Added depends command by Olivier Crete <tester@gentoo.org>,
+       fixes #40830.
+       * equery: Reworked output yet again.
+       * equery: Belongs handles multiple files on the command line, partially
+       fixes #62361.
+       * gentoolkit: Reworked printing functions
+
+2004-08-29 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Added printing functions
+       * equery: Added check for bad regexp in belongs, fixes #58494
+       * equery: Added proper error reporting to stderr, fixes #57580
+
+2004-08-22 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Fixed Package.get_env_var to use the correct tree db.
+       * gentoolkit: Renamed Package.get_use_vars to Package.get_use_flags
+       * equery: Searches now include masked packages, when installed.
+       * equery: Fixed output to be piping-friendly
+       * equery: Added -N option to force non-piping output
+       * equery: Added hasuses command
+
+2004-08-01 Marius Mauch <genone@gentoo.org>
+       * Fixed grep expression for `qpkg -f`
+
+2004-05-04 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added a -f/--full-regex option to belongs and some logic so
+       users can do belongs ant, belongs /usr/bin/ant and belongs -f ".*ant.*"
+       while getting sensible results. Fixes #37637.
+
+2004-04-14 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Released gentoolkit-dev-0.2.0_pre3
+
+2004-03-31 Marius Mauch <genone@gentoo.org>
+       * glsa-check: updates, fixing #45528 and #45522, adding support for rXX
+       operators and passing filenames as arguments to Glsa()
+
+2004-03-13 Marius Mauch <genone@gentoo.org>
+       * Added glsa-check and glsa.py, please note:
+               - they are only temporary for testing, so no manpage/ChangeLog
+               - their CVS home is in gentoo-projects
+       * fixed bugs #42160, #40935, #43389
+       * equery: fixing descriptions for local USE flags
+       * equery: more checking on exceptions
+
+2004-02-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added ebump
+       * Added gensync
+       * Added epkgmove, closes #36663.
+
+2004-02-06 Marius Mauch <genone@gentoo.org>
+       * fixed bugs #40159, #39798, #39652, #39596, #39293
+       * changed etcat and equery behavior for ambigous package names,
+               they now return values for all matching packages rather
+               than erroring out
+       * added "will be phased out" messages to etcat and qpkg
+
+2004-01-23 Marius Mauch <genone@gentoo.org>
+       * lots of bugfixes
+       * equery: now catches Exceptions thrown by portage
+       * equery: minor bugfixes
+
+2004-01-14 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added src/ego to gentoolkit-dev
+
+2004-01-12 Marius Mauch <genone@gentoo.org>
+       * equery: added mask, keyword and slot information to list command
+       * equery: fixed traceback in equery
+       * equery: added more information on "equery list"
+
+2004-01-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added src/ego
+
+2004-01-07 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added Makefile
+       * Added new build system
+       * Added src/old-scripts, the scripts from app-portage/gentoolkit
+       * Renamed gentool to equery
+       * Released 0.2.0_pre1
+
+2003-12-31 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added which command
+       * equery: Added check command (not finished)
+
+2003-12-12 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added size command
+       * equery: Added depgraph command
+
+2003-12-11 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added list command
+       * equery: Added uses command
+
+2003-12-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Removed emerge-rsync, emerge-webrsync
+       * Added moo
+       * Added skeleton man pages to all packages
+       * Added Makefile
+
+2003-10-05 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added files command
+       * equery: Added belongs command
+
+2003-10-04 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Rewrote dep-clean to python
+
+2003-06-31 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Rewrote more of etcat; many of the functions now employ
+               gentoolkit.
+       * Replaced qpkg with stubs of a reimplementation in python.
+
+2003-06-27 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added patch to echangelog to consider PORTDIR, fixes #23881.
+
+2003-06-26 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added echangelog, by agenkin
+       * Added ekeyword, by agenkin
+       * Added gentoolkit, the common python library for all Gentoolkit tools.
+       * Revived pkg-size as a testbed for the gentoolkit library
+       * Fixed some minor issues in qpkg
+       * Added revdep-rebuild-1
+       * Added revdep-rebuild-2
+       * Restructuring etcat
+       * Fixed some minor issues in dep-clean
+
+2002-11-21 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Renamed pkg-size to gentool-package-size
+       * Renamed pst-package-count to gentool-package-count
+       * Retired rest of pst-* stuff
+
+2002-08-06 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Created separate CVS module for Gentoolkit
+       * Restructured directory hierarchy
diff --git a/gentoolkit-dev/Makefile b/gentoolkit-dev/Makefile
new file mode 100644 (file)
index 0000000..fdd48a7
--- /dev/null
@@ -0,0 +1,54 @@
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+include makedefs.mak
+
+TOOLS=ebump echangelog ego ekeyword eshowkw eviewcvs imlate
+RELEASE="gentoolkit-dev-$(VERSION)$(RELEASE_TAG)"
+
+all:
+       @echo "YARMOUTH (vb.) To shout at foreigners in the belief that the louder you speak, the better they'll understand you." 
+       @echo "PYVERSION=$(PYVERSION)"
+       @echo "VERSION=$(VERSION)"
+       @echo "docdir=$(docdir)"
+       @echo "bindir=$(bindir)"
+       @echo "sbindir=$(sbindir)"
+       @echo "mandir=$(mandir)"
+
+# use $(TOOLS) if we have more than one test
+test:
+       $(MAKE) -C src/echangelog test
+
+clean:
+       rm -rf release/
+       @for tool in $(TOOLS); do \
+               ( $(MAKE) -C src/$${tool} clean ) \
+       done
+
+dist:
+       mkdir -p release/gentoolkit-dev-$(VERSION)$(RELEASE_TAG)
+       @for tool in $(TOOLS); do \
+               ( $(MAKE) -C src/$${tool} distdir=release/$(RELEASE) dist ) \
+       done
+
+       cp Makefile AUTHORS README README.Developer TODO COPYING NEWS ChangeLog release/$(RELEASE)/
+
+       @sed -e "s/^VERSION=.*/VERSION=$(VERSION)/" \
+               -e "s/^RELEASE_TAG=.*/RELEASE_TAG=$(RELEASE_TAG)/" \
+               makedefs.mak > release/$(RELEASE)/makedefs.mak
+
+       ( cd release ; tar zcf $(RELEASE).tar.gz $(RELEASE)/ )
+
+install: install-gentoolkit-dev
+
+install-gentoolkit-dev:
+       install -d $(docdir)
+       install -d $(bindir)
+       install -d $(mandir)
+
+       install -m 0644 AUTHORS ChangeLog COPYING NEWS README README.Developer TODO $(docdir)/
+
+       @for tool in $(TOOLS); do \
+               ( $(MAKE) -C src/$${tool} DESTDIR=$(DESTDIR) install ) \
+       done
diff --git a/gentoolkit-dev/Makefile.skel b/gentoolkit-dev/Makefile.skel
new file mode 100644 (file)
index 0000000..aa03f75
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+include ../../makedefs.mak
+
+.PHONY: all
+
+all:
+
+dist:
+       mkdir -p ../../$(distdir)/src/<TOOL>/
+       cp Makefile <TOOL> <TOOL>.1 ../../$(distdir)/src/<TOOL>/
+
+install: all
+       install -m 0755 <TOOL> $(bindir)/
+       install -m 0644 <TOOL>.1 $(mandir)/
+
diff --git a/gentoolkit-dev/NEWS b/gentoolkit-dev/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/gentoolkit-dev/README b/gentoolkit-dev/README
new file mode 100644 (file)
index 0000000..992e86d
--- /dev/null
@@ -0,0 +1,37 @@
+Package: gentoolkit-dev
+Authors: Aron Griffis <agriffis@gentoo.org>
+         Brandon Low <lostlogic@gentoo.org>
+         Ian Leitch <port001@gentoo.org>
+         Karl Trygve Kalleberg <karltk@gentoo.org>
+         Marius Mauch <genone@gentoo.org>
+         Paul Varner <fuzzyray@gentoo.org>
+         Christian Ruppert <idl0r@gentoo.org>
+         See src/<tool>/AUTHORS for tool-specific authors
+
+MOTIVATION
+
+The gentoolkit and gentoolkit-dev packages contain a collection of useful
+administration scripts particular to the Gentoo Linux distribution. It contains
+rough drafts and implementations of features that may in time make it into
+Portage, or into full-fledged tools in their own right.
+
+The gentoolkit-dev package is intended primarily for Gentoo developers.
+
+CONTENTS
+
+gentoolkit-dev
+==============
+ebump          - Ebuild revision bumper
+echangelog     - update portage ChangeLogs
+ego            - 
+ekeyword       - modify package KEYWORDS
+eshowkw        - Display ebuild keywords in a graphical form
+eviewcvs       - generate viewcvs URLs
+imlate         - Displays candidates for keywords for an architecture based upon a target architecture
+
+IMPROVEMENTS
+
+Any suggestions for improvements should be sent to tools-portage@gentoo.org, or
+added as a bug assigned to us.
+
+We only accept new contributions if they are written in bash or python.
diff --git a/gentoolkit-dev/README.Developer b/gentoolkit-dev/README.Developer
new file mode 100644 (file)
index 0000000..1fc76ae
--- /dev/null
@@ -0,0 +1,49 @@
+
+OVERVIEW
+
+The SVN module 'gentoolkit' contains all the scripts and stuff for both the 
+gentoolkit and the gentoolkit-dev package. The gentoolkit-dev package is
+an optional add-on, that is only intented for the Gentoo developers.
+
+STYLE POLICY
+
+If you're touching any of the python scripts please don't change the indentation
+style (if it's using tabs, you should use tabs too). Especially don't mix
+spaces and tabs as that makes the code completely unreadable.
+Tabs should be default for new scripts.
+
+Each script must provide a manpage.
+
+MAKING A RELEASE
+
+Releases should only be made by members of the tools-portage team. See
+http://www.gentoo.org/proj/en/metastructure/herds/herds.xml?select=tools-portage
+for who to contact on IRC, or shuffle over a bug report to us, or send
+us a mail at tools-portage@gentoo.org if you need an immediate release.
+
+The release manager (big words;) will then do
+
+1) make VERSION=major.minor.patch RELEASE_TAG=<optional> dist
+2) copy release/gentoolkit-dev-${VERSION}-${RELEASE_TAG}.tar.gz to 
+   dev.gentoo.org:/space/distfiles-local/
+3) make a new ebuild, app-portage/gentoolkit-dev/gentoolkit-dev-${VERSION}.ebuild
+   with a SRC_URI that points to 
+   mirror://gentoo/gentoolkit-dev-${VERSION}-${RELEASE_TAG}.tar.gz
+   (just use one of the previous ebuilds)
+
+Important!
+1) _ALWAYS_ make sure you don't "overwrite" a previous release. Your
+   new VERSION must be newer than any previous released version. If you
+   mess up a release, don't overwrite with the same release number, iterate
+   the patch version and try again (and again, and again until you get 
+   it right;)
+
+
+Currently, the following people have "release access":
+
+ - zmedico@gentoo.org
+ - fuzzyray@gentoo.org
+ - idl0r@gentoo.org
+
+If you want a new release, ping either one of us. If you want to get 
+"release access", talk to fuzzyray@gentoo.org
diff --git a/gentoolkit-dev/TODO b/gentoolkit-dev/TODO
new file mode 100644 (file)
index 0000000..117b6d3
--- /dev/null
@@ -0,0 +1,8 @@
+- rewrite ekeyword and echangelog to use gentoolkit
+- look through forums.gentoo.org for additional scripts
+- write a Gentoolkit Guide
+- revision bump tool
+ - bump versioned files in filesdir
+
+ - imlate
+       Compare one arch with all to see possible candidates for several architectures
diff --git a/gentoolkit-dev/makedefs.mak b/gentoolkit-dev/makedefs.mak
new file mode 100644 (file)
index 0000000..2ec1e3e
--- /dev/null
@@ -0,0 +1,21 @@
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+# Override this on command line when making a release, ie 'dist'
+VERSION=9.9.9
+RELEASE_TAG=
+
+# python-config is not installed on all arches Bug #113386
+PYVERSION="`LC_COLLATE=C; python -V 2>&1 | tr '[:upper:]' '[:lower:]' | sed -e 's/ //g;s/\([0-9]\.[0-9]\)\.[0-9]/\1/'`"
+DESTDIR=
+
+docdir=$(DESTDIR)/usr/share/doc/gentoolkit-dev-$(VERSION)$(RELEASE_TAG)
+bindir=$(DESTDIR)/usr/bin
+sbindir=$(DESTDIR)/usr/sbin
+mandir=$(DESTDIR)/usr/share/man/man1
+sysconfdir=$(DESTDIR)/etc
+
+# skeletons
+all:
+clean:
diff --git a/gentoolkit-dev/src/ebump/AUTHORS b/gentoolkit-dev/src/ebump/AUTHORS
new file mode 100644 (file)
index 0000000..2432e06
--- /dev/null
@@ -0,0 +1,5 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Original author:
+Karl Trygve Kalleberg <karltk@gentoo.org>
diff --git a/gentoolkit-dev/src/ebump/ChangeLog b/gentoolkit-dev/src/ebump/ChangeLog
new file mode 100644 (file)
index 0000000..4434b94
--- /dev/null
@@ -0,0 +1,8 @@
+2004-06-21 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Fixed handling of deletion.
+
+2004-03-11 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Fixed incorrect cut'ing of wc -l output when updating ChangeLog
+       
+2004-02-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Initial import
diff --git a/gentoolkit-dev/src/ebump/Makefile b/gentoolkit-dev/src/ebump/Makefile
new file mode 100644 (file)
index 0000000..aa1d347
--- /dev/null
@@ -0,0 +1,20 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+.PHONY: all
+all: 
+
+dist: 
+       mkdir -p ../../$(distdir)/src/ebump/
+       cp Makefile AUTHORS README TODO ChangeLog ebump ebump.1 ../../$(distdir)/src/ebump/
+
+install: all
+       install -m 0755 ebump $(bindir)/
+       install -d $(docdir)/ebump
+       install -m 0644 AUTHORS README TODO ChangeLog $(docdir)/ebump/
+       install -m 0644 ebump.1 $(mandir)/
diff --git a/gentoolkit-dev/src/ebump/README b/gentoolkit-dev/src/ebump/README
new file mode 100644 (file)
index 0000000..c81835c
--- /dev/null
@@ -0,0 +1,18 @@
+
+Package : ebump
+Version : 0.1.0
+Author  : See AUTHORS
+
+MOTIVATION
+
+The ebump utility is a Gentoo-specific tool for bumping the revision of
+a given ebuild and auxiliary files in the Portage tree. It is only
+useful for Gentoo developers with CVS commit access.
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+N/A
diff --git a/gentoolkit-dev/src/ebump/TODO b/gentoolkit-dev/src/ebump/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/gentoolkit-dev/src/ebump/ebump b/gentoolkit-dev/src/ebump/ebump
new file mode 100755 (executable)
index 0000000..b877318
--- /dev/null
@@ -0,0 +1,390 @@
+#! /bin/sh
+#
+# Copyright (c) 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) Gentoo Technologies, Inc.
+# Licensed under the GNU General Public License, version 2
+#
+# Maintainer: Karl Trygve Kalleberg <karltk@gentoo.org>
+
+__version__="0.1.0"
+__author__="Karl Trygve Kalleberg"
+__email__="<karltk@gentoo.org>"
+__description__="Ebuild version bumping tool"
+
+
+
+die() {
+    echo $1 > /dev/stderr
+    exit -1
+}
+
+einfo() {
+    if [ ${opt_verbosity} -gt 1 ] ; then
+       echo $*
+    fi
+}
+
+print_version() {
+    echo "${__description__}, v${__version__}"
+    echo "Copyright (c) 2004 ${__author__} ${__email__}"
+    echo "Copyright (c) 2004 Gentoo Technologies, Inc."
+    echo "Licensed under the GNU General Public License, version 2"
+}
+
+print_usage() {
+    echo "Usage: ebump <options> foo<.ebuild>"
+    echo "Ebuild version bumping tool, v${__version__}"
+    echo "  -V|--version           show version info"
+    echo "  -v|--verbose           increase verbosity"
+    echo "  -q|--quiet             turn off output"
+    echo "  -C|--no-vcs            do not add to VCS"
+    echo "  -m|--message           append message to ChangeLog"
+    echo "  -d|--delete-old        delete previous revision from VCS (DANGEROUS!)"
+}
+
+#
+# Load options from /etc/gentoolkit/ebump.conf and ${HOME}/.gentoo/ebump.conf
+# Home directory file takes precedence.
+#
+load_options() {
+
+    # FIXME: Sourcing config files like this is really a bad idea; users may
+    # easily override any function in this program inside his config files.
+
+    if [ -f /etc/gentoolkit/ebump.conf ] ; then
+       . /etc/gentoolkit/ebump.conf
+    fi
+    if [ -f ${HOME}/.gentoo/gentool-env ] ; then
+       . ${HOME}/.gentoo/gentool-env
+    fi
+    if [ -f ${HOME}/.gentoo/ebump.conf ] ; then
+       . ${HOME}/.gentoo/ebump.conf
+    fi
+
+       # FIXME: remove this warning in 2-3 releases.
+       if [[ -n ${opt_add_cvs} ]];
+       then
+               echo "Warning: opt_add_cvs is deprecated, please use opt_add_vcs from now on!" >/dev/stderr
+       fi
+}
+
+#
+# Find closes ebuild to ${1}, if any
+#
+find_ebuild() {
+    f=${1}
+
+    if [ -f ${1} ] ; then
+       echo ${1}
+    fi
+
+    if [ -f ${1}.ebuild ] ; then
+       echo ${1}
+    fi
+}
+
+#
+# splitname (version|name|revision) package-name-version-revision
+#
+splitname() {
+    case $1 in
+       version)
+           echo ${2} | sed -r "s/.*-([0-9].*)/\1/"
+           ;;
+       name)
+           name=$(echo ${2} | sed -r "s/(.*)-[0-9].*/\1/")
+           if [ ${name} == ${2} ] ; then
+               if [ $(echo ${2} | grep "^[0-9].*") ] ; then
+                   # The filename starts with a version number, thus it has no
+                    # name
+                   name=""
+               else
+                   # The filename doesn't have a recognizeable version number;
+                    # everything is a name
+                   name=${2}
+               fi
+           fi
+           echo ${name}
+           ;;
+       revision)
+           rev=$(echo ${2} | sed -r "s/.*-r([0-9][0-9]*)/\1/")
+           if [ ${rev} == ${2} ] ; then
+               rev=0
+           fi
+           echo ${rev}
+           ;;
+       vernorev)
+           ver=$(echo ${2} | sed -r "s/.*-([0-9].*)-r[0-9]+/\1/")
+           if [ ${ver} == ${2} ] ; then
+               ver=$(echo ${2} | sed -r "s/.*-([0-9].*)/\1/")
+           fi
+           echo ${ver}
+           ;;
+       *)
+           echo
+    esac
+}
+
+process_ebuild() {
+       vcs=$1
+    ebuild_arg=$2
+       shift $#
+
+    # Files to add to VCS
+    addfiles=""
+    # Files to remove from VCS
+    delfiles=""
+
+    if [ -z ${ebuild_arg} ] ; then
+       print_usage
+       exit
+    fi
+
+    #
+    # Try to find a matching ebuild
+    #
+
+    ebuild_name=$(find_ebuild ${ebuild_arg})
+    if [ -z ${ebuild_name} ] ; then
+       die "Could not find ${ebuild_arg}"
+    fi
+    einfo "Processing ebuild ${ebuild_name}"
+
+    #
+    # Bump revision suffix (or add one)
+    #
+
+    PF=$(basename ${ebuild_name} .ebuild)
+    PN=$(splitname name ${PF})
+    PV=$(splitname version ${PF})
+    rev=$(splitname revision ${PF})
+    PV_norev=$(splitname vernorev ${PF})
+    newPF=${PN}-${PV_norev}-r$[rev+1]
+
+#    echo $PF / $PN / $PV / $rev / $PV_norev / $newPF
+
+    einfo "Bumped ${PF}.ebuild to ${newPF}.ebuild"
+
+       if [[ "${vcs}" == "svn" ]];
+       then
+               svn cp ${PF}.ebuild ${newPF}.ebuild
+       else
+               cp ${PF}.ebuild ${newPF}.ebuild
+       fi
+
+       einfo "Reset keywords to ~arch"
+
+       ekeyword '~all' "${newPF}.ebuild"
+
+    addfiles="${addfiles} ${newPF}.ebuild"
+    delfiles="${delfiles} ${PF}.ebuild"
+
+    #
+    # (Optional) Bump relevant files in files/
+    #
+
+    if [ "${opt_bump_auxfiles}" == "y" ] ; then
+
+        # Gather list of auxiliary files in files/ that has a versioned
+       # filename, where the version matches our current version.
+
+       bumplist=""
+       for x in $(echo files/*) ; do
+           if [ ! -z $(echo $x | grep "${PV}$") ] ; then
+               bumplist="${bumplist} ${x}"
+           fi
+       done
+
+        # Bump version of all matches
+
+       for x in ${bumplist} ; do
+
+           bn=$(basename ${x})
+           dn=$(dirname ${x})
+
+           PN=$(splitname name ${bn})
+           PV=$(splitname version ${bn})
+           rev=$(splitname revision ${bn})
+           PV_norev=$(splitname vernorev ${bn})
+
+#          echo $PN / ${PV_norev} / ${rev}
+
+           # Special case for when we have no name part; filename
+           # is just a version number
+           if [ -z "${PN}" ] ; then
+               newbn=${PV_norev}-r$[rev+1]
+           else
+               newbn=${PN}-${PV_norev}-r$[rev+1]
+           fi
+
+           if [ -d ${dn}/${bn} ] ; then
+               if [ -e ${dn}/${newbn} ] ; then
+                   echo "Directory ${dn}/${newbn} exists, not copying" > /dev/stderr
+               else
+                   cp -a ${dn}/${bn} ${dn}/${newbn}
+                       # uhm, is that necessary?
+#                  find ${dn}/${newbn} -name CVS | xargs rm -rf
+               fi
+           else
+               cp ${dn}/${bn} ${dn}/${newbn}
+           fi
+
+           addfiles="${addfiles} ${dn}/${newbn}"
+           delfiles="${delfiles} ${dn}/${bn}"
+
+           einfo "Bumped ${dn}/${bn} to ${dn}/${newbn}"
+       done
+    fi
+
+#    echo "addfiles ${addfiles}"
+#    echo "delfiles ${delfiles}"
+
+       filelist="${addfiles}"
+
+    #
+    # (Optional) Add VCS entry for all new files
+    #
+    if [ "${opt_add_vcs}" == "y" ] ; then
+               for x in ${addfiles} ; do
+                   if [ -d ${x} ] ; then
+                               find ${x} -exec ${vcs} add {} ';'
+                   else
+                               ${vcs} add ${x}
+                   fi
+               done
+               einfo "Added ${addfiles} to VCS"
+    fi
+
+
+    #
+    # (Optional) Delete previous entry
+    #
+    # Could we use 'rm' instead of remove for all vcs?
+    if [ "${opt_delete_old}" == "y" ] ; then
+               for x in ${delfiles} ; do
+                       if [[ "${vcs}" == "cvs" ]];
+                       then
+                           ${vcs} remove -f ${x}
+                       elif [[ "${vcs}" == "git" ]];
+                       then
+                               ${vcs} rm ${x}
+                       else
+                               ${vcs} remove ${x}
+                       fi
+               done
+               einfo "Removed ${delfiles} from VCS"
+    fi
+
+    #
+    # (Optional) Add ChangeLog entry
+    #
+       if [[ "${opt_add_changelog}" == "y" ]] && [[ "${opt_add_vcs}" == "y" ]];
+       then
+               # FIXME: remove this warning in 2-3 releases
+               if [[ -n ${AUTHORNAME} ]] || [[ -n ${AUTHOREMAIL} ]];
+               then
+                       echo "Warning: AUTHORNAME and AUTHOREMAIL is deprecated!" >/dev/stderr
+                       echo "Please take a look at echangelog(1)." >/dev/stderr
+                       echo "To avoid this warning unset AUTHORNAME and AUTHOREMAIL." >/dev/stderr
+               fi
+
+               echangelog "${opt_commitmessage}" || set $?
+
+               if [[ ${1:-0} -ne 0 ]];
+               then
+                       einfo "Modifying ChangeLog failed!"
+               else
+                       einfo "Added ChangeLog entry"
+               fi
+    fi
+}
+
+function get_vcs() {
+       if [[ -d "CVS" ]];
+       then
+               echo "cvs"
+               return 0
+       elif [[ -d ".svn" ]];
+       then
+               echo "svn"
+               return 0
+       else
+               if [[ -x "$(which git)" ]];
+               then
+                       if [[ -n "$(git rev-parse --git-dir 2>/dev/null)" ]];
+                       then
+                               echo "git"
+                               return 0
+                       fi
+               fi
+               echo
+               return 1
+       fi
+}
+
+original_params=${#}
+
+#
+# Global options
+#
+opt_verbosity=1
+opt_warn_on_delete=y
+opt_add_changelog=y
+opt_add_vcs=y
+opt_bump_auxfiles=y
+opt_delete_old=n
+opt_commitmessage=""
+
+load_options
+
+skip=0
+while [ ${#} -gt 0 ] ; do
+    arg=${1}
+    shift
+    if [ ${skip} -gt 0 ] ; then
+       skip=$[skip-1]
+    else
+       case ${arg} in
+           -h|--help)
+               print_usage
+               exit 0
+               ;;
+           -m|--message)
+               opt_commitmessage="${1}"
+               skip=1
+               ;;
+           -C|--no-vcs)
+               opt_add_vcs=n
+               ;;
+           -V|--version)
+               print_version
+               exit
+               ;;
+           -v|--verbose)
+               opt_verbosity=$[opt_verbosity + 1]
+               ;;
+           -q|--quiet)
+               opt_verbosity=0
+               ;;
+           -d|--delete-old)
+               opt_delete_old=y
+               ;;
+           *)
+               ebuild_arg=${arg}
+               ;;
+       esac
+    fi
+done
+
+_vcs=$(get_vcs)
+if [[ -z "${_vcs}" ]];
+then
+       echo "Warning: no cvs, git or svn repository found!" >/dev/stderr
+       echo "Changes can't be added to the VCS" >/dev/stderr
+       opt_add_vcs=n
+       opt_delete_old=n
+fi
+process_ebuild "${_vcs}" ${ebuild_arg}
+
+# TODO:
+# - put cli parser into separate functions
diff --git a/gentoolkit-dev/src/ebump/ebump.1 b/gentoolkit-dev/src/ebump/ebump.1
new file mode 100644 (file)
index 0000000..38cd795
--- /dev/null
@@ -0,0 +1,110 @@
+.TH "ebump" "1" "0.1.0" "Gentoolkit" "Gentoo Administration"
+.SH "NAME"
+.LP 
+ebump \- Gentoo: Ebuild revision bumper
+.SH "SYNTAX"
+.LP 
+ebump [\fIoption\fP] <\fIpackage-name[-version]\fP>
+
+.SH "DESCRIPTION" 
+
+.LP
+\fIebump\fR bumps the revision of a particular ebuild, and all auxiliary
+files in the files/ directory that have a matching version suffix.
+
+.LP
+By default, the all new revision files will be added to the VCS.
+
+.LP 
+You must stand in the directory of the ebuild to be bumped.
+
+.SH "OPTIONS"
+.LP 
+\fB\-C\fR
+.br
+\fB--no-vcs\fB
+.IP 
+Do not add new files to VCS.
+
+.LP 
+\fB\-V\fR
+.br
+\fB--version\fB
+.IP 
+Display version information and exit.
+
+.LP 
+\fB\-v\fR
+.br
+\fB--verbose\fB
+.IP 
+Increase verbosity level. May be used more than once.
+
+.LP 
+\fB\-q\fR
+.br
+\fB--quiet\fB
+.IP 
+Do not output any non-essential information.
+
+.LP 
+\fB\-m\fR <\fIChangeLog text\fR>
+.br 
+\fB\--message\fR <\fIChangeLog text\fR>
+.IP 
+Specifies the message to add to the ChangeLog, instead of the standard
+placeholder.
+
+.LP 
+\fB\-d\fR
+.br
+\fB\--delete-old\fR
+.IP 
+Delete old revision and old auxiliary files from VCS. This is
+\fIdangerous\fR and should only be used if you know exactly what you are
+doing, because
+.br
+1) the old revision may be stable on a different architecture than the one you
+are working on.
+.br
+2) the auxiliary files may be required by other versions of the ebuild.
+.br
+3) the new revision should usually undergo a period of testing before being marked stable.
+
+.SH "CONFIGURATION"
+
+.LP
+\fB/etc/gentoolkit/ebump.conf\fR
+.br
+\fB~/.gentoo/ebump.conf\fR
+.IP
+From these files, \fIebump\fR will load the settings
+.br
+\fBopt_verbosity\fR (default \fI1\fR) - verbosity level 0-10
+.br
+\fBopt_add_changelog\fR (default \fIy\fR) - add entry in ChangeLog
+.br
+\fBopt_add_vcs\fR (default \fIy\fR) - add new files to VCS
+.br
+\fBopt_bump_auxfiles\fR (default \fIy\fR) - bump auxiliary files in files/
+.br
+\fBopt_delete_old\fR (default \fIn\fR) - delete old revision (DANGEROUS!)
+.br
+\fBopt_commitmessage\fR (default \fI""\fR) - default ChangeLog message
+
+.LP
+\fB(DEPRECATED)\fR
+.br
+\fB~/.gentoo/gentool-env\fR
+.IR
+From this file, \fIebump\fR will load the env vars \fBAUTHORNAME\fR and 
+\fBAUTHOREMAIL\fR, which are used to generate proper ChangeLog entries.
+
+.SH "SEE ALSO"
+.LP 
+The rest of the utilities in \fIapp-portage/gentoolkit-dev\fR, such as
+\fIechangelog(1)\fR and \fIekeyword(1)\fR.
+
+.SH "AUTHORS"
+.LP 
+Karl Trygve Kalleberg <karltk@gentoo.org>
diff --git a/gentoolkit-dev/src/echangelog/AUTHORS b/gentoolkit-dev/src/echangelog/AUTHORS
new file mode 100644 (file)
index 0000000..36d5bfd
--- /dev/null
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/gentoolkit-dev/src/echangelog/ChangeLog b/gentoolkit-dev/src/echangelog/ChangeLog
new file mode 100644 (file)
index 0000000..c1c5885
--- /dev/null
@@ -0,0 +1,84 @@
+26 Mar 2006 Aron Griffis <agriffis@gentoo.org>
+       * echangelog: Don't warn about missing ebuilds when updating 
+         copyrights #120061
+
+27 Apr 2005 Aron Griffis <agriffis@gentoo.org>
+       * more changes for #90326; report all trivial files if no significant
+         changes can be found
+
+26 Apr 2005 Aron Griffis <agriffis@gentoo.org>
+       * detect conflicts explicitly
+       * report ChangeLog in the list of files if it's the only file that is
+         changing #90326
+
+23 Mar 2005 Aron Griffis <agriffis@gentoo.org>
+       * handle package moves without adding new version lines
+
+08 Mar 2005 Aron Griffis <agriffis@gentoo.org>
+       * don't complain about cvs add of digests #84377
+       * use gmtime instead of localtime
+
+07 Mar 2005 Aron Griffis <agriffis@gentoo.org>
+       * report all changed versions #84332
+
+25 Feb 2005 Aron Griffis <agriffis@gentoo.org>
+       * strip GECOS #80011
+
+09 Nov 2004 Aron Griffis <agriffis@gentoo.org>
+       * change "cvs diff -fU 0" => "cvs -f diff U0" because -f is a
+         global option, not a diff option
+
+08 Nov 2004 Aron Griffis <agriffis@gentoo.org>
+       * call cvs with -f to refrain from using .cvsrc, which might
+         contain conflicting options
+       * fix auto-addition of ChangeLog; last attempt was broken
+
+03 Nov 2004 Aron Griffis <agriffis@gentoo.org>
+       * abort when there are unresolved files (files that aren't under
+         revision control) just like repoman
+       * auto-add to cvs when a new ChangeLog is created
+
+15 Sep 2004 Aron Griffis <agriffis@gentoo.org>
+       * fix the wrapping to fit in 80 columns properly.  It was
+         previously possible to get lines with 81 chars.  Thanks to
+         ciaranm for reporting.
+
+29 Mar 2004 Aron Griffis <agriffis@gentoo.org>
+       * fix bug 46111 by testing for /<root@/ instead of / root@/
+
+28 Mar 2004 Aron Griffis <agriffis@gentoo.org>
+       * apply patch from plasmaroo, with minor modifications, to enable EDITOR
+         and +- support
+
+27 Mar 2004 Michael Sterrett <mr_bones_@gentoo.org>
+       * don't fall out of the loop if update_copyright() didn't change
+         anything. Just go on to the next file.
+
+21 Mar 2004 Aron Griffis <agriffis@gentoo.org>
+       * Fix typo $0 -> 0
+
+19 Mar 2004 Aron Griffis <agriffis@gentoo.org>
+       * Remove debugging output
+       * Fix $v bug introduced in last commit
+
+16 Mar 2004 Aron Griffis <agriffis@gentoo.org>
+       * Make Feb 17 behavior work without Feb 20 bug :-)
+       * Release as version 0.2.0
+
+20 Feb 2004 Aron Griffis <agriffis@gentoo.org>
+       * Only update copyrights on modified ebuilds, otherwise if you run
+         echangelog again, it reports that all the ebuilds have been updated!
+         The copyright year issue would be better solved on Jan 1 of each year by
+         a separate script.
+
+17 Feb 2004 Aron Griffis <agriffis@gentoo.org>
+       * Update copyrights on all ebuilds, not just the modified ones
+
+07 Jan 2004 Aron Griffis <agriffis@gentoo.org>
+       * Updated Makefile to understand building man-page from pod
+       * Removed static man-page in favor of generated man-page from pod
+       * Added copyright year updating
+       * Allow echangelog to run even when no files have changed
+
+2004-01-07 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added Makefile
diff --git a/gentoolkit-dev/src/echangelog/Makefile b/gentoolkit-dev/src/echangelog/Makefile
new file mode 100644 (file)
index 0000000..a3e5aac
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+.PHONY: all test
+
+all:
+
+test:
+       cd test; bash test.sh
+
+dist:
+       mkdir -p ../../$(distdir)/src/echangelog/test/templates
+       cp Makefile AUTHORS README TODO ChangeLog echangelog echangelog.1 ../../$(distdir)/src/echangelog/
+       cp test/TEST.pm test/test.sh ../../$(distdir)/src/echangelog/test/
+       cp test/templates/test.patch test/templates/vcstest-0.0.1.ebuild ../../$(distdir)/src/echangelog/test/templates
+
+install: all
+       install -m 0755 echangelog $(bindir)/
+       install -d $(docdir)/echangelog 
+       install -m 0644 AUTHORS README $(docdir)/echangelog/
+       install -m 0644 echangelog.1 $(mandir)/
diff --git a/gentoolkit-dev/src/echangelog/README b/gentoolkit-dev/src/echangelog/README
new file mode 100644 (file)
index 0000000..77a7930
--- /dev/null
@@ -0,0 +1,11 @@
+Most of the documentation is contained in the man-page, which you can
+read directly (using GNU man) by doing
+
+       man ./echangelog.1
+
+To rebuild the man-page from pod source, do
+
+       pod2man --name=echangelog --center='Gentoolkit' \
+               echangelog.pod echangelog.1
+
+03 Nov 2004 agriffis
diff --git a/gentoolkit-dev/src/echangelog/TODO b/gentoolkit-dev/src/echangelog/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/gentoolkit-dev/src/echangelog/echangelog b/gentoolkit-dev/src/echangelog/echangelog
new file mode 100755 (executable)
index 0000000..8767a19
--- /dev/null
@@ -0,0 +1,794 @@
+#!/usr/bin/perl -w
+#
+# echangelog: Update the ChangeLog for an ebuild.  For example:
+#
+#   $ echangelog 'Add ~alpha to KEYWORDS'
+#   4a5,7
+#   >   10 Feb 2003; Aron Griffis <agriffis@gentoo.org> oaf-0.6.8-r1.ebuild :
+#   >   Add ~alpha to KEYWORDS
+#   >
+
+use strict;
+use POSIX qw(strftime getcwd setlocale);
+use File::Basename;
+use Getopt::Long;
+
+# Fix bug 21022 by restricting to C locale
+setlocale(&POSIX::LC_ALL, "C");
+
+use Text::Wrap;
+$Text::Wrap::columns = 77;
+$Text::Wrap::unexpand = 0;
+
+# Global variables
+my (@files, @ebuilds, @conflicts, @trivial, @unknown, @new_versions, %actions);
+my ($input, $editor, $entry, $user, $date, $text, $vcs);
+my ($opt_help, $opt_nostrict, $opt_version);
+
+$opt_help = 0;
+$opt_nostrict = 0;
+$opt_version = 0;
+# DEPRECATED
+my $opt_strict = 0;
+
+my %vcs = (
+       bzr => {
+               diff => "bzr diff",
+               status => "bzr status -S .",
+               add => "bzr add",
+               skip => 3,
+               # The same as for hg.
+               regex => qr/^=== \S+ file '\S+\/\S+\/((\S+)\.ebuild)/
+       },
+       cvs => {
+               diff => "cvs -f diff -U0",
+               status => "cvs -fn up",
+               add => "cvs -f add",
+               skip => 6,
+               regex => qr/^Index: (([^\/]*?)\.ebuild)\s*$/
+       },
+       git => {
+               diff => "git diff",
+               status => "git diff-index HEAD --name-status",
+               add => "git add",
+               # This value should usually be 3 but on new file mode we need skip+1.
+               # So 4 should be fine anyway.
+               skip => 4,
+               regex => qr/^diff \-\-git \S*\/((\S*)\.ebuild)/
+       },
+       hg => {
+               diff => "hg diff",
+               status => "hg status .",
+               add => "hg add",
+               skip => 3,
+               # hg diff is relative to the root.
+               # TODO: Write a proper regex :)
+               regex => qr/diff \-r \S+ \S+\/\S+\/((\S+)\.ebuild)/
+       },
+       svn => {
+               diff => "svn diff -N",
+               status => "svn status",
+               add => "svn add",
+               skip => 4,
+               regex => qr/^Index: (([^\/]*?)\.ebuild)\s*$/
+       },
+);
+
+sub usage {
+       (my $usage = <<"        EOF") =~ s/^\t//gm;
+       Usage: echangelog [options] <changelog message>
+
+       Options:
+           --help      err, this screen ...
+           --no-strict do not abort on trivial/no changes
+           --version   show version info
+       EOF
+       print $usage;
+       exit 0;
+}
+
+sub version {
+       my $Revision = "Last svn change rev";
+       my $Date = "Last svn change date";
+       my $foo = "";
+       print "echangelog\n$Revision$foo \n$Date$foo\n";
+       exit 0;
+}
+
+sub getenv($) {
+    my $key = shift;
+
+    # Ensure our variable exist
+    if ( defined($ENV{$key}) ) {
+        # Ensure we don't get empty variables
+        if ( length($ENV{$key}) > 0 ) {
+            return $ENV{$key};
+        }
+    }
+    return undef;
+}
+
+# Bug 264146.
+# Copied from Text::Wrap.
+# The only modified thing is:
+# We trim _just_ tab/space etc. but not \n/\r.
+# \s treats even \n/\r as whitespace.
+# BUGS:
+# ' test'
+# ' test'
+# Will end up in:
+# ' test'
+# ''
+# 'test'
+# See 'my $ps = ($ip eq $xp) ? "\n\n" : "\n";'
+sub text_fill {
+       my ($ip, $xp, @raw) = @_;
+       my @para;
+       my $pp;
+
+       for $pp ( split(/\n\s+/, join("\n", @raw)) ) {
+               $pp =~ s/[\x09|\x0B|\x0C|\x20]+/ /g;
+               my $x = Text::Wrap::wrap($ip, $xp, $pp);
+               push(@para, $x);
+       }
+
+       # if paragraph_indent is the same as line_indent,
+       # separate paragraphs with blank lines
+       my $ps = ($ip eq $xp) ? "\n\n" : "\n";
+       return join ($ps, @para);
+}
+
+sub changelog_info(%) {
+       my %changed = @_;
+
+       open(INFO, '>', 'ChangeLog.new');
+
+       print(INFO "\n");
+       print(INFO "# Please enter the ChangeLog message for your changes. Lines starting\n");
+       print(INFO "# with '#' will be ignored, and an empty message aborts the ChangeLog.\n");
+       print(INFO "#\n# Changes:\n");
+
+       foreach my $key (keys(%changed)) {
+               if ($changed{$key} eq "+") {
+                       printf(INFO "# new file:\t%s\n", $key);
+               }
+               elsif ($changed{$key} eq "-") {
+                       printf(INFO "# deleted:\t%s\n", $key);
+               }
+               else {
+                       printf(INFO "# modified:\t%s\n", $key);
+               }
+       }
+
+       close(INFO);
+}
+
+sub update_cat_pn {
+       my $t = shift;
+       my $cwd = getcwd();
+
+       my $category = basename(dirname($cwd));
+       my $package_name = basename($cwd);
+
+       $t =~ s/^(# ChangeLog for).*/$1 $category\/$package_name/;
+
+       return $t;
+}
+
+# Just to ensure we don't get duplicate entries.
+sub mypush(\@@) {
+       my $aref = shift;
+
+       foreach my $value (@_) {
+               push(@{$aref}, $value) if !grep(/^\Q$value\E$/, @{$aref});
+       }
+}
+
+GetOptions(
+       'help' => \$opt_help,
+       'no-strict' => \$opt_nostrict,
+       'version' => \$opt_version,
+       'strict' => \$opt_strict,
+);
+
+usage() if $opt_help;
+version() if $opt_version;
+
+if($opt_strict) {
+       print STDERR "Warning: The option '--strict' has been deprecated and will be removed soon!\n";
+       print STDERR "--strict behaviour is now default.\n";
+}
+
+# Figure out what kind of repo we are in.
+# Respect $PATH while looking for the VCS
+if (getenv("PATH")) {
+       foreach my $path ( split(":", getenv("PATH")) ) {
+               if ( -X "${path}/bzr" ) {
+                       open(BZR, '-|', "${path}/bzr root 2>/dev/null");
+                       $vcs = "bzr" if defined(<BZR>);
+                       close(BZR);
+                       last if $vcs;
+               }
+               if ( -X "${path}/cvs" ) {
+                       $vcs = "cvs" if -d "CVS";
+                       last if $vcs;
+               }
+               if ( -X "${path}/git" ) {
+                       open(GIT, '-|', "${path}/git rev-parse --git-dir 2>/dev/null");
+                       $vcs = "git" if defined(<GIT>);
+                       close(GIT);
+                       last if $vcs;
+               }
+               if ( -X "${path}/hg" ) {
+                       open(HG, '-|', "${path}/hg root 2>/dev/null");
+                       $vcs = "hg" if defined(<HG>);
+                       close(HG);
+                       last if $vcs;
+               }
+               if ( -X "${path}/svn" ) {
+                       $vcs = "svn" if -d ".svn";
+                       last if $vcs;
+               }
+       }
+}
+
+if ( ! $vcs ) {
+       print STDERR "Either no CVS, .git, .svn, ... directories found, the specific VCS has not been\n";
+       print STDERR "installed or you don't have execute rights!\n";
+       exit(1);
+}
+
+# Read the current ChangeLog
+if (-f 'ChangeLog') {
+       open(I, '<', 'ChangeLog') or die "Can't open ChangeLog for input: $!\n";
+       { local $/ = undef; $text = <I>; }
+       close(I);
+} else {
+       # No ChangeLog here, maybe we should make one...
+       if (<*.ebuild>) {
+               open(C, '-|', "portageq portdir") or die "portageq returned with an error: $!\n";
+               my $portdir = <C>;
+               $portdir =~ s/\s+$//;
+               close(C);
+
+               die "Can't find PORTDIR\n" if (length $portdir == 0);
+
+               open(I, '<', "$portdir/skel.ChangeLog")
+                       or die "Can't open $portdir/skel.ChangeLog for input: $!\n";
+               { local $/ = undef; $text = <I>; }
+               close(I);
+
+               $text =~ s/^\*.*//ms; # don't need the fake entry
+
+               $text = update_cat_pn($text);
+       } else {
+               die "This should be run in a directory with ebuilds...\n";
+       }
+}
+
+# Figure out what has changed around here
+open C, $vcs{$vcs}{status}.' 2>&1 |' or die "Can't run ".$vcs{$vcs}{status}.": $!\n";
+while (<C>) {
+       # I don't want mess our existing stuff with the horrible bazaar stuff.
+       # TODO: add stuff for untracked/conflicting files.
+       if ($vcs eq "bzr") {
+               # NEW, DELETED, MODIFIED
+               if (/^[\s\+\-]([NDM])\s+(.*)/) {
+                       my ($status, $filename) = ($1, $2);
+                       # strip category/package/ since everything is relative to the repo root.
+                       $filename =~ s/^([^\/]+\/){2}//;
+
+                       # skip empty $filename, e.g. if you add a new package, the first
+                       # line would be the package directory app-foo/bar/ but thats stripped above.
+                       next if !$filename;
+                       # skip directories
+                       next if -d $filename;
+
+                       ($actions{$filename} = $status) =~ tr/NDM/+-/d;
+                       push(@files, $filename);
+                       next;
+                       }
+               # RENAMED/MOVED
+               elsif (/^R\s+(\S+) => (\S+)/) {
+                       my ($old, $new) = ($1, $2);
+                       $old =~ s/^([^\/]+\/){2}//;
+                       $new =~ s/^([^\/]+\/){2}//;
+
+                       next if !$old or !$new;
+                       next if -d $old or -d $new;
+
+                       $actions{$old} = '-';
+                       $actions{$new} = '+';
+
+                       push(@files, $old, $new);
+                       next;
+               }
+       }
+       if (/^C\s+(\S+)/) {
+               # TODO: The git part here might be unused
+               if($vcs eq "git") {
+                       my $filename = $1;
+                       $filename =~ /\S*\/(\S*)/;
+
+                       next if -d $filename;
+
+                       push @conflicts, $filename;
+                       next;
+               }
+
+               push @conflicts, $1;
+               next;
+       }
+       elsif (/^\?\s+(\S+)/) {
+               push @unknown, $1;
+               $actions{$1} = '?';
+               next;
+       }
+       elsif (/^([ARMD])\s+\+?\s*(\S+)/) {
+               my ($status, $filename) = ($1,$2);
+
+               if($vcs eq "git") {
+                       open(P, '-|', "git rev-parse --sq --show-prefix");
+                       my $prefix = <P>;
+                       close(P);
+
+                       if (defined($prefix)) {
+                               chomp($prefix);
+
+                               if ($filename =~ /\Q$prefix\E(\S*)/) {
+                                       $filename = $1 ;
+                               }
+                               else {
+                                       next;
+                               }
+                       }
+               }
+
+               next if -d $filename;
+
+               push(@files, $filename);
+               ($actions{$filename} = $status) =~ tr/DARM/-+-/d;
+       }
+}
+
+sub git_unknown_objects {
+       open(GIT, "-|", "${vcs} ls-files --exclude-standard --others");
+       while(defined( my $line = <GIT> )) {
+               chomp($line);
+
+               # IMHO we can skip those files, even if they're untracked
+               #next if $line =~ m/^\.gitignore$/;
+               
+               push(@unknown, $line);
+       }
+       close(GIT);
+}
+
+# git only shows files already added so we need to check for unknown files
+# separately here.
+if($vcs eq "git") {
+       git_unknown_objects();
+}
+
+# Separate out the trivial files for now
+@files = grep {
+       !/^(Manifest|ChangeLog)$/ or do { push @trivial, $_; 0; }
+} @files;
+
+@unknown = grep {
+       !/^(Manifest|ChangeLog)$/ or do { push @trivial, $_; 0; }
+} @unknown;
+
+# Don't allow any conflicts
+if (@conflicts) {
+       print STDERR <<EOT;
+$vcs reports the following conflicts.  Please resolve them before
+running echangelog.
+EOT
+       print STDERR map "C $_\n", @conflicts;
+       exit 1;
+}
+
+# Don't allow unknown files (other than the trivial files that were separated
+# out above)
+if (@unknown) {
+       print STDERR <<EOT;
+$vcs reports the following unknown files.  Please use "$vcs add" before
+running echangelog, or remove the files in question.
+EOT
+       print STDERR map "? $_\n", @unknown;
+       exit 1;
+}
+
+# Sort the list of files as portage does.  None of the operations through
+# the rest of the script should break this sort.
+sub sortfunc($$) {
+       my ($a, $b) = @_;
+       (my $va = $a) =~ s/.*?-(\d.*?)(?:\.ebuild)?$/$1/;
+       (my $vb = $b) =~ s/.*?-(\d.*?)(?:\.ebuild)?$/$1/;
+       my ($na, $sa, $sna, $ra) = ($va =~ /^(.*?)(?:_(alpha|beta||pre|rc|p)(\d*))?(?:-r(\d+))?$/);
+       my ($nb, $sb, $snb, $rb) = ($vb =~ /^(.*?)(?:_(alpha|beta||pre|rc|p)(\d*))?(?:-r(\d+))?$/);
+       my (@na) = split /\.|(?<=\d)(?=[^\d\.])/, $na;
+       my (@nb) = split /\.|(?<=\d)(?=[^\d\.])/, $nb;
+       my $retval;
+
+       #
+       # compare version numbers first
+       #
+       for (my $i = 0; defined $na[$i] or defined $nb[$i]; $i++) {
+               # def vs. undef
+               return +1 if defined $na[$i] and !defined $nb[$i];
+               return -1 if defined $nb[$i] and !defined $na[$i];
+
+               # num vs. num
+               if ($na[$i] =~ /^\d/ and $nb[$i] =~ /^\d/) {
+                       $retval = ($na[$i] <=> $nb[$i]);
+                       return $retval if $retval;
+                       next;
+               }
+
+               # char vs. char
+               if ($na[$i] =~ /^\D/ and $nb[$i] =~ /^\D/) {
+                       $retval = ($na[$i] cmp $nb[$i]);
+                       return $retval if $retval;
+                       next;
+               }
+
+               # num vs. char
+               $retval = ($na[$i] =~ /\d/ and -1 or +1);
+               return $retval;
+       }
+
+       #
+       # compare suffix second
+       #
+       if (defined $sa and !defined $sb) {
+               return +2 if $sa eq "p";
+               return -2;
+       }
+       if (defined $sb and !defined $sa) {
+               return -3 if $sb eq "p";
+               return +3;
+       }
+
+       if (defined $sa) { # and defined $sb
+               $retval = ($sa cmp $sb);
+               if ($retval) {
+                       return +4 if $sa eq "p";
+                       return -4 if $sb eq "p";
+                       return $retval; # suffixes happen to be alphabetical order, mostly
+               }
+
+               # compare suffix number
+               return +5 if defined $sna and !defined $snb;
+               return -5 if defined $snb and !defined $sna;
+
+               if (defined $sna) {  # and defined $snb
+                       $retval = ($sna <=> $snb);
+                       return $retval if $retval;
+               }
+       }
+
+       #
+       # compare rev third
+       #
+       return +6 if defined $ra and !defined $rb;
+       return -6 if defined $rb and !defined $ra;
+
+       if (defined $ra) { # and defined $rb
+               return ($ra <=> $rb);
+       }
+
+       #
+       # nothing left to compare
+       #
+       return 0;
+}
+
+# Forget ebuilds that only have changed copyrights, unless that's all
+# the changed files we have
+@ebuilds = grep /\.ebuild$/, @files;
+@files = grep !/\.ebuild$/, @files;
+
+if (@ebuilds) {
+       if ($vcs eq "git") {
+               open C, $vcs{$vcs}{diff}." HEAD -- @ebuilds 2>&1 |" or die "Can't run: ".$vcs{$vcs}{diff}."$!\n";
+       } else {
+               open C, $vcs{$vcs}{diff}." @ebuilds 2>&1 |" or die "Can't run: ".$vcs{$vcs}{diff}."$!\n";
+       }
+
+       $_ = <C>;
+
+       while (defined $_) {
+               # only possible with cvs
+               if (/^$vcs diff: (([^\/]*?)\.ebuild) was removed/) {
+                       mypush(@files, $1);
+               }
+               # We assume GNU diff output format here.
+               # git format: diff --git a/app-doc/repodoc/metadata.xml b/app-doc/repodoc/metadata.xml
+               elsif (/$vcs{$vcs}{regex}/) {
+                       my ($file, $version) = ($1, $2);
+
+                       if ($vcs eq "git") {
+                               while (<C>) {
+                                       last if /^deleted file mode|^index/;
+                                       if (/^new file mode/) {
+                                               mypush(@files, $file);
+                                               mypush(@new_versions, $version);
+                                               last;
+                                       }
+                               }
+                       }
+
+                       if ($vcs eq "bzr") {
+                               if (/^=== added file/) {
+                                       mypush(@files, $file);
+                                       mypush(@new_versions, $version);
+                                       last;
+                               }
+                               elsif(/^=== renamed file '.+\/([^\/]+\.ebuild)' => '.+\/(([^\/]+)\.ebuild)'/) {
+                                       mypush(@files, $1, $2);
+                                       mypush(@new_versions, $3);
+                                       last;
+                               }
+                       }
+
+                       # check if more than just copyright date changed.
+                       # skip some lines (vcs dependent)
+                       foreach(1..$vcs{$vcs}{skip}) {
+                               $_ = <C>;
+                       }
+
+                       while (<C>) {
+                               last if /^[A-Za-z]/;
+                               if (/^[-+](?!# Copyright)/) {
+                                       mypush(@files, $file);
+                                       last;
+                               }
+                       }
+
+                       # at this point we've either added $f to @files or not,
+                       # and we have the next line in $_ for processing
+                       next;
+               }
+               elsif (/^$vcs.*?: (([^\/]*?)\.ebuild) is a new entry/) {
+                       mypush(@files, $1);
+                       mypush(@new_versions, $2);
+               }
+
+               # other cvs output is ignored
+               $_ = <C>;
+       }
+}
+close C;
+
+# Subversion diff doesn't identify new versions. So use the status command
+if (($vcs eq "svn") and (@ebuilds)) {
+       open C, $vcs{$vcs}{status}." @ebuilds 2>&1 |" or die "Can't run: ".$vcs{$vcs}{status}."$!\n";
+       $_ = <C>;
+
+       while (defined $_) {
+               if (/^A\s+\+?\s*(([^\s]*)\.ebuild)/) {
+                       mypush(@files, $1);
+                       mypush(@new_versions, $2);
+               }
+
+               $_ = <C>;
+       }
+}
+
+# When a package move occurs, the versions appear to be new even though they are
+# not.  Trim them from @new_versions in that case.
+@new_versions = grep { $text !~ /^\*\Q$_\E\s/m } @new_versions;
+
+# Check if we have any files left, otherwise re-insert ebuild list
+# (of course, both might be empty anyway)
+@files = @ebuilds unless (@files);
+
+# Allow ChangeLog entries with no changed files, but give a fat warning
+unless (@files) {
+       print STDERR "**\n";
+       print STDERR "** NOTE: No non-trivial changed files found.  Normally echangelog\n";
+       print STDERR "** should be run after all affected files have been added and/or\n";
+       print STDERR "** modified.  Did you forget to $vcs add?\n";
+       print STDERR "**\n";
+
+       if (!$opt_nostrict) {
+               print STDERR "** In strict mode, exiting\n";
+               print STDERR "** If you know what you're doing there pass '--no-strict' to echangelog\n";
+               exit(1);
+       }
+
+       @files = sort sortfunc @trivial;
+
+       # last resort to put something in the list
+       unless (@files) {
+               @files = qw/ChangeLog/;
+               $actions{'ChangeLog'} = "";
+       }
+}
+
+# sort
+@files = sort sortfunc @files;
+@new_versions = sort sortfunc @new_versions;
+
+# Get the input from the cmdline, editor or stdin
+if ($ARGV[0]) {
+       $input = "@ARGV";
+} else {
+       $editor = getenv('ECHANGELOG_EDITOR') ? getenv('ECHANGELOG_EDITOR') : getenv('EDITOR') || undef;
+
+       if ($editor) {
+               # Append some informations.
+               changelog_info(%actions);
+
+               system("$editor ChangeLog.new");
+
+               if ($? != 0) {
+                       # This usually happens when the editor got forcefully killed; and
+                       # the terminal is probably messed up: so we reset things.
+                       system('stty sane');
+                       print STDERR "Editor died!  Reverting to stdin method.\n";
+                       undef $editor;
+               } else {
+                       if (open I, "<ChangeLog.new") {
+                               local $/ = undef;
+                               $input = <I>;
+                               close(I);
+
+                               # Remove comments from changelog_info().
+                               local $/ = "\n";
+                               $input =~ s/^#.*//mg;
+                               local $/ = undef;
+                       } else {
+                               print STDERR "Error opening ChangeLog.new: $!\n";
+                               print STDERR "Reverting to stdin method.\n";
+                               undef $editor;
+                       }
+               }
+               unlink('ChangeLog.new') if -f 'ChangeLog.new';
+       }
+
+       unless ($editor) {
+               print "Please type the log entry: use Ctrl-d to finish, Ctrl-c to abort...\n";
+               local $/ = undef;
+               $input = <>;
+       }
+}
+die "Empty entry; aborting\n" unless $input =~ /\S/;
+
+# If there are any long lines, then wrap the input at $columns chars
+# (leaving 2 chars on left, one char on right, after adding indentation below).
+$input = text_fill('  ', '  ', $input);
+
+# Prepend the user info to the input
+# Changes related to bug 213374;
+# This sequence should be right:
+# 1. GENTOO_COMMITTER_NAME && GENTOO_COMMITTER_EMAIL
+# 2. GENTOO_AUTHOR_NAME && GENTOO_AUTHOR_EMAIL
+# 3. ECHANGELOG_USER (fallback/obsolete?)
+# 4. getpwuid()..
+if ( getenv("GENTOO_COMMITTER_NAME") && getenv("GENTOO_COMMITTER_EMAIL") ) {
+       $user = sprintf("%s <%s>", getenv("GENTOO_COMMITTER_NAME"), getenv("GENTOO_COMMITTER_EMAIL"));
+}
+elsif ( getenv("GENTOO_AUTHOR_NAME") && getenv("GENTOO_AUTHOR_EMAIL") ) {
+       $user = sprintf("%s <%s>", getenv("GENTOO_AUTHOR_NAME"), getenv("GENTOO_AUTHOR_EMAIL"));
+}
+elsif ( getenv("ECHANGELOG_USER") ) {
+       $user = getenv("ECHANGELOG_USER");
+}
+else {
+       my ($fullname, $username) = (getpwuid($<))[6,0];
+       $fullname =~ s/,.*//; # remove GECOS, bug 80011
+       $user = sprintf('%s <%s@gentoo.org>', $fullname, $username);
+}
+
+# Make sure that we didn't get "root"
+die "Please set ECHANGELOG_USER or run as non-root\n" if $user =~ /<root@/;
+
+$date = strftime("%d %b %Y", gmtime);
+$entry = "$date; $user ";
+$entry .= join ', ', map "$actions{$_}$_", @files;
+$entry .= ':';
+$entry = Text::Wrap::fill('  ', '  ', $entry); # does not append a \n
+$entry .= "\n$input";                          # append user input
+
+# Each one of these regular expressions will eat the whitespace
+# leading up to the next entry (except the two-space leader on the
+# front of a dated entry), so it needs to be replaced with a
+# double carriage-return.  This helps to normalize the spacing in
+# the ChangeLogs.
+if (@new_versions) {
+       # Insert at the top with a new version marker
+       $text =~ s/^( .*? )               # grab header
+               \s*\n(?=\ \ \d|\*|\z)  # suck up trailing whitespace
+               /"$1\n\n" .
+               join("\n", map "*$_ ($date)", reverse @new_versions) .
+               "\n\n$entry\n\n"/sxe
+                       or die "Failed to insert new entry (4)\n";
+} else {
+       # Changing an existing patch or ebuild, no new version marker
+       # required
+       $text =~ s/^( .*? )               # grab header
+               \s*\n(?=\ \ \d|\*|\z)  # suck up trailing whitespace
+               /$1\n\n$entry\n\n/sx
+                       or die "Failed to insert new entry (3)\n";
+}
+
+# New packages and/or ones that have moved around often have stale data here.
+# But only do that in places where ebuilds are around (as echangelog can be
+# used in profiles/ and such places).
+if (grep(/\.ebuild$/, @files)) {
+       $text = update_cat_pn($text);
+}
+
+sub update_copyright {
+       my ($t) = @_;
+       (my $year = $date) =~ s/.* //;
+
+       $t =~ s/^# Copyright \d+(?= )/$&-$year/m or
+       $t =~ s/^(# Copyright) \d+-(\d+)/$1 1999-$year/m;
+
+       return $t;
+}
+
+# Update the copyright year in the ChangeLog
+$text = update_copyright($text);
+
+# Write the new ChangeLog
+open O, '>ChangeLog.new' or die "Can't open ChangeLog.new for output: $!\n";
+print O $text            or die "Can't write ChangeLog.new: $!\n";
+close O                  or die "Can't close ChangeLog.new: $!\n";
+
+# Update affected ebuild copyright dates.  There is no reason to update the
+# copyright lines on ebuilds that haven't changed.  I verified this with an IP
+# lawyer.
+for my $e (grep /\.ebuild$/, @files) {
+       if (-s $e) {
+               my ($etext, $netext);
+
+               open E, "<$e" or warn("Can't read $e to update copyright year\n"), next;
+               { local $/ = undef; $etext = <E>; }
+               close E;
+
+               # Attempt the substitution and compare
+               $netext = update_copyright($etext);
+               next if $netext eq $etext; # skip this file if no change.
+
+               # Write the new ebuild
+               open E, ">$e.new" or warn("Can't open $e.new\n"), next;
+               print E $netext and
+               close E or warn("Can't write $e.new\n"), next;
+
+               # Move things around and show the diff
+               system "diff -U 0 $e $e.new";
+               rename "$e.new", $e or warn("Can't rename $e.new: $!\n");
+
+               # git requires to re-add this file else it wouln't be included in the commit.
+               if ($vcs eq "git")
+               {
+                       system("$vcs{$vcs}{add} ${e}");
+               }
+       }
+}
+
+# Move things around and show the ChangeLog diff
+system 'diff -Nu ChangeLog ChangeLog.new';
+rename 'ChangeLog.new', 'ChangeLog' or die "Can't rename ChangeLog.new: $!\n";
+
+# Okay, now we have a starter ChangeLog to work with.
+# The text will be added just like with any other ChangeLog below.
+# Add the new ChangeLog to vcs before continuing.
+if ($vcs eq "cvs") {
+       if (open F, "CVS/Entries") {
+               system("cvs -f add ChangeLog") unless (scalar grep /^\/ChangeLog\//, <F>);
+       }
+} elsif ($vcs eq "svn") {
+       if (open F, ".svn/entries") {
+               system("svn add ChangeLog") unless (scalar grep /ChangeLog/, <F>);
+       }
+} else {
+       system("$vcs{$vcs}{add} ChangeLog 2>&1 >> /dev/null");
+}
+
+# vim: set ts=4 sw=4 tw=0:
diff --git a/gentoolkit-dev/src/echangelog/echangelog.1 b/gentoolkit-dev/src/echangelog/echangelog.1
new file mode 100644 (file)
index 0000000..1575644
--- /dev/null
@@ -0,0 +1,270 @@
+.\" Automatically generated by Pod::Man 2.16 (Pod::Simple 3.07)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  \*(C+ will
+.\" give a nicer C++.  Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available.  \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.ie \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.el \{\
+.    de IX
+..
+.\}
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "echangelog 1"
+.TH echangelog 1 "2009-04-28" "perl v5.10.0" "Gentoolkit"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+echangelog \- Gentoo: update portage ChangeLogs
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+echangelog [ \fItext\fR ]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+This tool provides an easy way to create or update portage ChangeLogs
+in Gentoo.  The tool scans the current directory, which is assumed to
+be a package directory such as /usr/portage/app\-editors/vim, finds
+what files have been changed or added, and inserts the appropriate
+entry to ChangeLog.  If \fItext\fR is not provided on the command-line,
+echangelog prompts for it.
+.PP
+All modifications should occur before running echangelog so that it
+can include the appropriate file information in the ChangeLog entry.
+For example, you should run \*(L"cvs add\*(R" on your files, otherwise
+echangelog won't know those files are part of the update.
+.PP
+If your text would cause the ChangeLog entry to exceed 80 columns, it
+will be rewrapped to keep the ChangeLog neat.  If you need special
+formatting in the ChangeLog, then you can either (1) run echangelog
+with no text on the command-line, and make sure that your text won't
+be too wide, (2) edit the ChangeLog manually.  If you prefer (2), I'd
+recommend something like \*(L"echangelog blah\*(R" so that the header lines
+are computed correctly, then edit and change \*(L"blah\*(R" to your preferred
+text.
+.PP
+In addition to updating the ChangeLog, echangelog will automatically
+update the copyright year of all out-of-date ebuilds, as well as the
+ChangeLog itself.  These updates are included in the diff displayed by
+echangelog when it finishes its work.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+Presently echangelog is simple enough that it supplies no options.
+Probably I'll add \fB\-\-help\fR and \fB\-\-version\fR in the future, but for
+now it's enough to track the gentoolkit version.
+.SH "EXAMPLES"
+.IX Header "EXAMPLES"
+To create a ChangeLog for a completely new package.  The header is
+parsed from skel.ebuild.
+.PP
+.Vb 2
+\&  $ cvs add metalog\-0.1.ebuild
+\&  cvs server: use \*(Aqcvs commit\*(Aq to add this file permanently
+\&
+\&  $ echangelog \*(AqNew ebuild, thanks to Harvey McGillicuddy\*(Aq
+\&  \-\-\- ChangeLog   1969\-12\-31 19:00:00.000000000 \-0500
+\&  +++ ChangeLog.new       2003\-02\-23 14:04:06.000000000 \-0500
+\&  @@ \-0,0 +1,9 @@
+\&  +# ChangeLog for app\-admin/metalog
+\&  +# Copyright 2000\-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2
+\&  +# $Header$
+\&  +
+\&  +*metalog\-0.1 (23 Feb 2003)
+\&  +
+\&  +  23 Feb 2003; Aron Griffis <agriffis@gentoo.org> metalog\-0.1.ebuild :
+\&  +  New ebuild, thanks to Harvey McGillicuddy
+\&  +
+.Ve
+.PP
+To bump a revision.  Note you need to \*(L"cvs add\*(R" so that echangelog
+will notice the new file.
+.PP
+.Vb 2
+\&  $ cvs add metalog\-0.1\-r1.ebuild
+\&  cvs server: use \*(Aqcvs commit\*(Aq to add this file permanently
+\&
+\&  $ echangelog \*(AqBump revision to fix bug #999\*(Aq
+\&  \-\-\- ChangeLog   2003\-02\-23 14:04:06.000000000 \-0500
+\&  +++ ChangeLog.new       2003\-02\-23 14:07:48.000000000 \-0500
+\&  @@ \-2,6 +2,11 @@
+\&   # Copyright 2000\-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2
+\&   # $Header$
+\&
+\&  +*metalog\-0.1\-r1 (23 Feb 2003)
+\&  +
+\&  +  23 Feb 2003; Aron Griffis <agriffis@gentoo.org> metalog\-0.1\-r1.ebuild :
+\&  +  Bump revision to fix bug #999
+\&  +
+\&   *metalog\-0.1 (23 Feb 2003)
+\&
+\&     23 Feb 2003; Aron Griffis <agriffis@gentoo.org> metalog\-0.1.ebuild :
+.Ve
+.PP
+For a multi-line entry, omit the command-line arg.
+.PP
+.Vb 10
+\&  $ echangelog
+\&  Please type the log entry, finish with ctrl\-d
+\&  Bump revision to fix bug #999.  Necessary to bump the revision because
+\&  the problem appears at run\-time, not compile\-time.  This should also
+\&  give users the updated default configuration file.
+\&  \-\-\- ChangeLog   2003\-02\-23 14:09:12.000000000 \-0500
+\&  +++ ChangeLog.new       2003\-02\-23 14:12:43.000000000 \-0500
+\&  @@ \-2,6 +2,13 @@
+\&   # Copyright 2000\-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2
+\&   # $Header$
+\&
+\&  +*metalog\-0.1\-r1 (23 Feb 2003)
+\&  +
+\&  +  23 Feb 2003; Aron Griffis <agriffis@gentoo.org> metalog\-0.1\-r1.ebuild :
+\&  +  Bump revision to fix bug #999.  Necessary to bump the revision because
+\&  +  the problem appears at run\-time, not compile\-time.  This should also
+\&  +  give users the updated default configuration file.
+\&  +
+\&   *metalog\-0.1 (23 Feb 2003)
+\&
+\&     23 Feb 2003; Aron Griffis <agriffis@gentoo.org> metalog\-0.1.ebuild :
+.Ve
+.SH "ENVIRONMENT VARIABLES"
+.IX Header "ENVIRONMENT VARIABLES"
+.IP "\s-1ECHANGELOG_USER\s0" 4
+.IX Item "ECHANGELOG_USER"
+If echangelog can't figure out your username for the entry, you should
+set \s-1ECHANGELOG_USER\s0.  For example, export ECHANGELOG_USER=\*(L"Aron
+Griffis <agriffis@gentoo.org>\*(R"
+.SH "NOTES"
+.IX Header "NOTES"
+As of the most recent version of echangelog (when this man-page
+appeared), echangelog puts all new entries at the top of the file
+instead of finding the appropriate *version line within the file.
+This is because that \*(L"new\*(R" ChangeLog format was never agreed upon by
+the Gentoo developers.  Unfortunately the existence of both formats
+will undoubtedly cause much confusion.
+.PP
+This also means that the examples above are wrong, since I just copied
+them from some old email.  However they're not much wrong. ;\-)
+.PP
+This tool was written by Aron Griffis <agriffis@gentoo.org>.  Bugs
+found should be filed against me at http://bugs.gentoo.org/
diff --git a/gentoolkit-dev/src/echangelog/test/TEST.pm b/gentoolkit-dev/src/echangelog/test/TEST.pm
new file mode 100644 (file)
index 0000000..6632148
--- /dev/null
@@ -0,0 +1,26 @@
+# We just return a static/predefined date because we're working with
+# static md5 checksums.
+
+package TEST;
+
+use strict;
+use warnings;
+
+BEGIN {
+       use Exporter();
+       our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
+       
+       $VERSION     = 1.00;
+
+       @ISA         = qw(Exporter);
+       @EXPORT      = qw(&strftime);
+       %EXPORT_TAGS = ( );
+       @EXPORT_OK   = qw();
+}
+our @EXPORT_OK;
+
+sub strftime {
+       return "01 Jan 2009";
+}
+
+1;
diff --git a/gentoolkit-dev/src/echangelog/test/templates/test.patch b/gentoolkit-dev/src/echangelog/test/templates/test.patch
new file mode 100644 (file)
index 0000000..72d46fa
--- /dev/null
@@ -0,0 +1,6 @@
+--- test.patch 2009-04-28 14:13:26.171225175 +0200
++++ test.patch 2009-04-28 14:12:26.246497830 +0200
+@@ -0,0 +1,3 @@
++This is just an example.
++Its used for several echangelog tests.
++
diff --git a/gentoolkit-dev/src/echangelog/test/templates/vcstest-0.0.1.ebuild b/gentoolkit-dev/src/echangelog/test/templates/vcstest-0.0.1.ebuild
new file mode 100644 (file)
index 0000000..2824b83
--- /dev/null
@@ -0,0 +1,16 @@
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+DESCRIPTION="echangelog test ebuild"
+HOMEPAGE=""
+SRC_URI=""
+
+LICENSE=""
+SLOT="0"
+KEYWORDS=""
+IUSE=""
+
+DEPEND=""
+RDEPEND=""
+
diff --git a/gentoolkit-dev/src/echangelog/test/test.sh b/gentoolkit-dev/src/echangelog/test/test.sh
new file mode 100755 (executable)
index 0000000..14703d4
--- /dev/null
@@ -0,0 +1,176 @@
+#!/bin/bash
+
+source /etc/init.d/functions.sh
+
+SUPPORTED_VCS=( "cvs" "svn" "git" )
+VCSTEST="echangelog-test/vcstest"
+_ROOT=$(pwd)
+
+export ECHANGELOG_USER="Just a test <echangelogtest@gentoo.org>"
+
+MD5_INIT="34d54bc2ab1a2154b0c7bd5cdd7f6119"
+MD5_PATCH="db1ab89bb7374824d0f198078f79a83f"
+MD5_REVBUMP="31ddfa60d2ae4dd1fccd7e3d2bd2c06c"
+MD5_COPYRIGHT="6f39fa409ea14bb6506347c53f6dee50"
+MD5_OBSOLETE="0aedadf159c6f3add97a3f79fb867221"
+MD5_FINAL="17eb0df69f501cc6fdaffebd118b7764"
+
+function md5() {
+       local fname=$1
+       echo $(md5sum ${fname} | awk '{ print $1 }')
+}
+
+function ech() {
+       local bin=$1
+       local msg=$2
+
+       perl -I$(dirname $(dirname ${bin})) ${bin} "${msg}"
+}
+
+function make_test() {
+       local root=$1
+       local vcs=$2
+
+       local echangelog="${root}/tmp/echangelog"
+       local tmp="${root}/tmp/${vcs}"
+       local template="${root}/templates"
+
+       cd $root
+       mkdir -p ${tmp}
+       cd ${tmp}
+       
+       [[ "${vcs}" == "cvs" ]] && mkdir -p ${tmp}/cvsroot
+       [[ "${vcs}" == "svn" ]] && mkdir -p ${tmp}/svnroot
+
+       if [[ "${vcs}" == "git" ]];
+       then
+               git init
+               touch .gitignore
+               git add .gitignore
+               git commit -a -m 'Initial Commit'
+       elif [[ "${vcs}" == "svn" ]];
+       then
+               svnadmin create svnroot
+               svn co file://${tmp}/svnroot svn
+               cd svn
+       elif [[ "${vcs}" == "cvs" ]];
+       then
+               CVSROOT="${tmp}/cvsroot" cvs init
+               mkdir cvsroot/cvs
+               cvs -d:local:${tmp}/cvsroot co cvs
+               cd cvs
+       fi
+
+       mkdir -p ${VCSTEST}
+
+       cp ${template}/vcstest-0.0.1.ebuild ${VCSTEST}
+       ${vcs} add $(dirname ${VCSTEST})
+       if [[ "${vcs}" == "cvs" ]];
+       then
+               ${vcs} add ${VCSTEST}
+               ${vcs} add "${VCSTEST}/vcstest-0.0.1.ebuild"
+       fi
+
+       cd ${VCSTEST}
+       ech ${echangelog} 'New ebuild for bug <id>.'
+
+       if [[ "${MD5_INIT}" != "$(md5 ChangeLog)" ]];
+       then
+               eerror "WRONG MD5_INIT!"
+       fi
+
+       mkdir files
+       cp ${template}/test.patch files
+       if [[ "${vcs}" == "cvs" ]];
+       then
+               ${vcs} add files/
+               ${vcs} add files/test.patch
+       else
+               ${vcs} add files
+       fi
+
+       ech ${echangelog} "Added adittional patch to fix foo."
+
+       if [[ "${MD5_PATCH}" != "$(md5 ChangeLog)" ]];
+       then
+               eerror "WRONG MD5_PATCH!"
+       fi
+
+       if [[ "${vcs}" == "svn" ]];
+       then
+               ${vcs} commit -m 'New ebuild for bug <id>.' ../
+       else
+               ${vcs} commit -m 'New ebuild for bug <id>.'
+       fi
+
+       [[ "${vcs}" == "cvs" ]] && sed -i -e 's:# $Header\: .*$:# $Header\: $:' ChangeLog
+
+       cp vcstest-0.0.1.ebuild vcstest-0.0.1-r1.ebuild
+       ${vcs} add vcstest-0.0.1-r1.ebuild
+
+       ech ${echangelog} "Revbump..."
+
+       if [[ "${MD5_REVBUMP}" != "$(md5 ChangeLog)" ]];
+       then
+               eerror "WRONG MD5_REVBUMP!"
+       fi
+
+       sed -i -e 's:# Copyright 1999-2009 Gentoo Foundation:# Copyright 1999-2010 Gentoo Foundation:' vcstest-0.0.1.ebuild
+       ech ${echangelog} "Revbump...; Just copyright changed."
+
+       if [[ "${MD5_COPYRIGHT}" != "$(md5 ChangeLog)" ]];
+       then
+               eerror "WRONG MD5_COPYRIGHT!"
+       fi
+
+       if [[ "${vcs}" == "cvs" ]];
+       then
+               rm -f files/test.patch
+               ${vcs} remove files/test.patch
+       else
+               ${vcs} rm files/test.patch
+       fi
+
+       ech ${echangelog} "Revbump...; Just copyright changed; Removed obsolete patch."
+
+       if [[ "${MD5_OBSOLETE}" != "$(md5 ChangeLog)" ]];
+       then
+               eerror "WRONG MD5_OBSOLETE!"
+       fi
+
+       echo>>vcstest-0.0.1.ebuild
+       ech ${echangelog} "Revbump...; Just copyright changed; Removed obsolete patch; Modified more then just the copyright."
+
+       if [[ "${MD5_FINAL}" != "$(md5 ChangeLog)" ]];
+       then
+               eerror "WRONG MD5_FINAL!"
+       fi
+}
+
+[[ -d "${_ROOT}/tmp" ]] && rm -rf ${_ROOT}/tmp
+mkdir -p ${_ROOT}/tmp
+
+ebegin "Preparing echangelog"
+
+if [[ -e ../echangelog ]];
+then
+       cp ../echangelog "${_ROOT}/tmp" || set $?
+       sed -i -e 's:use POSIX qw.*:use POSIX qw(setlocale getcwd);\nuse TEST qw(strftime);:' "${_ROOT}/tmp/echangelog" || set $?
+       eend ${1:-0} || exit ${1}
+else
+       eerror "error"
+       eend ${1:-1}
+       exit 1
+fi
+
+for vcs in ${SUPPORTED_VCS[*]};
+do
+       if [[ -x "$(which ${vcs} 2>/dev/null)" ]];
+       then
+               ebegin "Starting test with ${vcs}"
+               make_test $_ROOT "${vcs}" || set $?
+               eend ${1:-0}
+       else
+               ewarn "No ${vcs} executable found, skipping test..."
+       fi
+done
diff --git a/gentoolkit-dev/src/ego/AUTHOR b/gentoolkit-dev/src/ego/AUTHOR
new file mode 100644 (file)
index 0000000..36d5bfd
--- /dev/null
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/gentoolkit-dev/src/ego/AUTHORS b/gentoolkit-dev/src/ego/AUTHORS
new file mode 100644 (file)
index 0000000..36d5bfd
--- /dev/null
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/gentoolkit-dev/src/ego/ChangeLog b/gentoolkit-dev/src/ego/ChangeLog
new file mode 100644 (file)
index 0000000..503d0da
--- /dev/null
@@ -0,0 +1,2 @@
+2004-15-01 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added Makefile
diff --git a/gentoolkit-dev/src/ego/Makefile b/gentoolkit-dev/src/ego/Makefile
new file mode 100644 (file)
index 0000000..b27a2cb
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+
+dist:
+       mkdir -p ../../$(distdir)/src/ego/
+       cp Makefile AUTHORS README TODO ChangeLog ego ../../$(distdir)/src/ego/
+
+install: all
+       install -d $(docdir)/ego        
+       install -m 0644 AUTHORS README ego $(docdir)/ego/
+
diff --git a/gentoolkit-dev/src/ego/README b/gentoolkit-dev/src/ego/README
new file mode 100644 (file)
index 0000000..6c44b4c
--- /dev/null
@@ -0,0 +1,2 @@
+[ -f ${HOME}/scripts/ego.bash ] && 
+alias ego="unalias ego ; source ${HOME}/scripts/ego.bash ; ego "
diff --git a/gentoolkit-dev/src/ego/TODO b/gentoolkit-dev/src/ego/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/gentoolkit-dev/src/ego/ego b/gentoolkit-dev/src/ego/ego
new file mode 100644 (file)
index 0000000..f1691f2
--- /dev/null
@@ -0,0 +1,86 @@
+echo1() { 
+  echo "$1"
+}
+
+ego() {
+  local portdir tmpdir category pkg target
+  
+  # This is WAY faster than portageq:
+  #   portdir=$(portageq portdir)
+  #   tmpdir=$(portageq envvar PORTAGE_TMPDIR)/portage
+  eval $(
+    . /etc/make.globals 
+    . /etc/make.conf
+    export PORTDIR PORTAGE_TMPDIR
+    export | sed -n '
+      s/^declare -x PORTDIR=/portdir=/p
+      s/^declare -x PORTAGE_TMPDIR=\(.*\)/tmpdir=\1\/portage/p'
+  )
+
+  case $1 in
+    *-*/*)
+      pkg=${1##*/}
+      category=${1%/*} 
+      ;;
+
+    ?*)    
+      pkg=$1 
+      # require an ebuild so that we can block deprecated packages
+      # such as dev-libs/rep-gtk
+      category=$(echo1 $portdir/*-*/$pkg/*.ebuild)
+      [[ -f $category ]] || category=$(echo1 $portdir/*-*/$pkg*/*.ebuild)
+      [[ -f $category ]] || category=$(echo1 $portdir/*-*/*$pkg/*.ebuild)
+      [[ -f $category ]] || category=$(echo1 $portdir/*-*/*$pkg*/*.ebuild)
+      if [[ ! -f $category ]]; then
+       echo "Can't find $pkg in $portdir" >&2
+       return 1
+      fi
+      pkg=${category%/*}
+      pkg=${pkg##*/}
+      category=${category#$portdir/}
+      category=${category%%/*}
+      ;;
+
+    *)
+      # Check if we're under $portdir first
+      pkg=${PWD##*/}
+      category=${PWD%/*}
+      category=${category##*/}
+      if [[ ! -d $portdir/$category/$pkg ]]; then
+       # Next check if we're in PORTAGE_TMPDIR
+       if [[ $PWD = $tmpdir/* ]]; then
+         pkg=${PWD#$tmpdir/}
+         pkg=${pkg%%/*}
+         pkg=${pkg%%-[0-9]*}  # not really a valid assumption
+         category=$(echo1 $portdir/*-*/$pkg/*.ebuild)
+         if [[ ! -f $category ]]; then
+           echo "Can't find $pkg in $portdir" >&2
+           return 1
+         fi
+         category=${category#$portdir/}
+         category=${category%%/*}
+       else
+         echo "syntax: ego [pkgname]" >&2
+         echo "or simply ego from a dir under $portdir or $tmpdir" >&2
+         return 1
+       fi
+      fi
+      ;;
+  esac
+
+  # go to tmpdir or portdir?
+  if [[ $PWD/ = */$category/$pkg/* ]]; then
+    [[ -n $tmpdir ]] || tmpdir=$(portageq envvar PORTAGE_TMPDIR)/portage
+    target=$(command ls -1td $tmpdir/$pkg-* 2>/dev/null | head -n 1)
+    if [[ -z $target ]]; then
+      echo "No matches found for $tmpdir/$pkg-*" >&2
+      return 1
+    fi
+    cd $target/work/$pkg* 2>/dev/null ||
+    cd $target/work 2>/dev/null ||
+    cd $target
+  else
+    cd $portdir/$category/$pkg
+  fi
+}
+
diff --git a/gentoolkit-dev/src/ekeyword/AUTHORS b/gentoolkit-dev/src/ekeyword/AUTHORS
new file mode 100644 (file)
index 0000000..36d5bfd
--- /dev/null
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/gentoolkit-dev/src/ekeyword/ChangeLog b/gentoolkit-dev/src/ekeyword/ChangeLog
new file mode 100644 (file)
index 0000000..68d99e5
--- /dev/null
@@ -0,0 +1,46 @@
+24 Apr 2009 Paul Varner <fuzzyray@gentoo.org>
+       * Handle multiline KEYWORDS
+
+07 Jan 2009 Mike Frysinger <vapier@gentoo.org>
+       * Support intended KEYWORDS
+       * Convert every instance of KEYWORDS in the file
+       * Error out on invalid arguments #156827
+       * Tighten up diff output to show KEYWORDS changes
+
+27 Oct 2005 Aron Griffis <agriffis@gentoo.org>
+       * Fix handling of comments
+       * Add support for bare ~ as a synonym for ~all
+
+23 Mar 2005 Aron Griffis <agriffis@gentoo.org>
+       * Only modify non-masked keywords with "all"
+
+17 Mar 2005 Aron Griffis <agriffis@gentoo.org>
+       * Sort keywords alphabetically
+
+09 Nov 2004 Aron Griffis <agriffis@gentoo.org>
+       * Fix mismatching of ppc vs. ppc-macos #69683
+
+15 Sep 2004 Aron Griffis <agriffis@gentoo.org>
+       * Update for GLEP 22 keywords
+       * Change copyright line for Gentoo Foundation
+
+12 Apr 2004 Aron Griffis <agriffis@gentoo.org>
+       * Add ability to remove keywords with ^, for example:
+         ekeyword ^alpha blah.ebuild
+       * Add support for -*, for example: ekeyword -* would add it;
+         ekeyword ^* would remove it.
+       * Add a man-page for ekeyword
+
+09 Apr 2004 Aron Griffis <agriffis@gentoo.org>
+       * Add ability to modify all keywords via all, ~all, or -all, for
+         example: ekeyword -all ~alpha ia64 blah.ebuild
+
+31 Mar 2004 Aron Griffis <agriffis@gentoo.org>
+       * Fix bug 28426 with patch from Mr_Bones_ to keep ekeyword from confusing
+         ppc and ppc64
+
+2004-01-12 Aron Griffis <agriffis@gentoo.org>
+       * Allow multiple keywords
+
+2004-01-07 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added Makefile
diff --git a/gentoolkit-dev/src/ekeyword/Makefile b/gentoolkit-dev/src/ekeyword/Makefile
new file mode 100644 (file)
index 0000000..a1a5802
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+%.1 : %.pod
+       pod2man $< > $@
+
+.PHONY: all
+all: ekeyword.1
+
+dist: ekeyword.1
+       mkdir -p ../../$(distdir)/src/ekeyword
+       cp Makefile AUTHORS README TODO ChangeLog ekeyword ekeyword.1 ../../$(distdir)/src/ekeyword/
+
+install: all
+       install -m 0755 ekeyword $(bindir)/
+       install -d $(docdir)/ekeyword
+       install -m 0644 AUTHORS README TODO ChangeLog $(docdir)/ekeyword/
+       install -m 0644 ekeyword.1 $(mandir)/
+
+clean:
+       $(RM) ekeyword.1
diff --git a/gentoolkit-dev/src/ekeyword/README b/gentoolkit-dev/src/ekeyword/README
new file mode 100644 (file)
index 0000000..ec7ff5e
--- /dev/null
@@ -0,0 +1,20 @@
+Package : ekeyword
+Version : 0.1.0
+Author  : See AUTHORS
+
+MOTIVATION
+
+Update the KEYWORDS in an ebuild.
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+- Should rewrite to Python, and use Portage directly to select candidate
+  ebuilds.
+- Should add entry to ChangeLog.
+
+For improvements, send a mail to agriffis@gentoo.org or make out a bug at 
+bugs.gentoo.org.
diff --git a/gentoolkit-dev/src/ekeyword/TODO b/gentoolkit-dev/src/ekeyword/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/gentoolkit-dev/src/ekeyword/ekeyword b/gentoolkit-dev/src/ekeyword/ekeyword
new file mode 100755 (executable)
index 0000000..e38801d
--- /dev/null
@@ -0,0 +1,147 @@
+#!/usr/bin/perl -w
+#
+# Copyright 2003-2009, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# Written by Aron Griffis <agriffis@gentoo.org>
+#
+# ekeyword: Update the KEYWORDS in an ebuild.  For example:
+#
+#   $ ekeyword ~alpha oaf-0.6.8-r1.ebuild
+#     - ppc sparc x86
+#     + ~alpha ppc sparc x86
+
+my ($kw_re) = '^(?:([-~^]?)(\w[\w-]*)|([-^]\*))$';
+my (@kw);
+
+# make sure the cmdline consists of keywords and ebuilds
+unless (@ARGV > 0) {
+       # NOTE: ~all will ignore all -arch keywords
+       print STDERR "syntax: ekeyword { arch | ~[arch] | -[arch] } ebuild...\n";
+       print STDERR "instead of 'arch' you can also use 'all' which covers all existing keywords...\n";
+       exit(1);
+}
+for my $a (@ARGV) {
+       $a = '~all' if $a eq '~' or $a eq $ENV{'HOME'}; # for vapier
+       next if $a =~ /$kw_re/o;                                # keyword
+       next if $a =~ /^\S+\.ebuild$/;                  # ebuild
+       die "I don't understand $a\n";
+}
+
+my $files = 0;
+for my $f (@ARGV) {
+       if ($f =~ /$kw_re/o) {
+               push @kw, $f;
+               next;
+       }
+
+       print "$f\n";
+
+       open I, "<$f"       or die "Can't read $f: $!\n";
+       open O, ">$f.new"   or die "Can't create $f.new: $!\n";
+       select O;
+
+       my $keys_before;
+       my $keys_after;
+       while (<I>) {
+               if (/^\s*KEYWORDS=/) {
+
+                       # extract the quoted section from KEYWORDS
+                       while (not /^\s*KEYWORDS=["'].*?["']/) {
+                               chomp;
+                               my $next = <I>;
+                               $_ = join " ", $_, $next;
+                       }
+                       (my $quoted = $_) =~ s/^.*?["'](.*?)["'].*/$1/s;
+                       $keys_before = $quoted;
+
+                       # replace -* with -STAR for our convenience below
+                       $quoted =~ s/-\*/-STAR/;
+
+                       for my $k (@kw) {
+                               my ($leader, $arch, $star) = ($k =~ /$kw_re/o);
+
+                               # handle -* and ^*
+                               if (defined $star) {
+                                       $leader = substr $star,0,1;
+                                       $arch = 'STAR';
+                               }
+
+                               # remove keywords
+                               if ($leader eq '^') {
+                                       if ($arch eq 'all') {
+                                               $quoted = '';
+                                       } else {
+                                               $quoted =~ s/[-~]?\Q$arch\E\b//;
+                                       }
+
+                               # add or modify keywords
+                               } else {
+                                       if ($arch eq 'all') {
+                                               # modify all non-masked keywords in the list
+                                               $quoted =~ s/(^|\s)~?(?=\w)/$1$leader/g;
+                                       } else {
+                                               # modify or add keyword
+                                               unless ($quoted =~ s/[-~]?\Q$arch\E(\s|$)/$leader$arch$1/) {
+                                                       # modification failed, need to add
+                                                       if ($arch eq 'STAR') {
+                                                               $quoted = "$leader$arch $quoted";
+                                                       } else {
+                                                               $quoted .= " $leader$arch";
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       # replace -STAR with -*
+                       $quoted =~ s/-STAR\b/-*/;
+
+                       # sort keywords and fix spacing
+                       $quoted = join " ", sort {
+                               (my $sa = $a) =~ s/^\W//;
+                               (my $sb = $b) =~ s/^\W//;
+                               return -1 if $sa eq '*';
+                               return +1 if $sb eq '*';
+                               $sa .= "-";
+                               $sb .= "-";
+                               $sa =~ s/([a-z0-9]+)-([a-z0-9]*)/$2-$1/g;
+                               $sb =~ s/([a-z0-9]+)-([a-z0-9]*)/$2-$1/g;
+                               $sa cmp $sb;
+                       } split " ", $quoted;
+
+                       $keys_after = $quoted;
+
+                       # re-insert quoted to KEYWORDS
+                       s/(["']).*?["']/$1$quoted$1/;
+
+                       print $_ or die "Can't write $f.new: $!\n";
+               } else {
+                       print, next;
+               }
+       }
+
+       close I;
+       close O;
+       select STDOUT;
+
+       if ($keys_before ne $keys_after) {
+               # This gives uniform output, but actually seems to make
+               # it harder to pick out differences, and doesn't work so
+               # well when adding/removing keywords
+               #$keys_before =~ s/(^| )/  /g;
+               #$keys_before =~ s/ ([-~])/$1/g;
+               #$keys_after =~ s/(^| )/  /g;
+               #$keys_after =~ s/ ([-~])/$1/g;
+               print "  -  $keys_before\n  +  $keys_after\n";
+               #system "diff -U 0 $f $f.new | sed -n -r 's:^(.)[[:space:]]*KEYWORDS=\"(.*)\":  \\1 \\2:p'";
+               #system "diff -U 0 $f $f.new | sed -n '/KEYWORDS=/s:^:  :p'";
+       }
+       rename "$f.new", "$f" or die "Can't rename: $!\n";
+       $files++;
+}
+
+if ($files == 0) {
+    die "No ebuilds processed!";
+}
+
+# vim:ts=4 sw=4
diff --git a/gentoolkit-dev/src/ekeyword/ekeyword.pod b/gentoolkit-dev/src/ekeyword/ekeyword.pod
new file mode 100644 (file)
index 0000000..ff837b9
--- /dev/null
@@ -0,0 +1,68 @@
+=head1 NAME
+
+ekeyword - Gentoo: modify package KEYWORDS
+
+=head1 SYNOPSIS
+
+ekeyword { arch|~arch|-arch|^arch } ebuild...
+
+=head1 DESCRIPTION
+
+This tool provides a simple way to add or update KEYWORDS in a set of
+ebuilds.  Each command-line argument is processed in order, so that
+keywords are added to the current list as they appear, and ebuilds are
+processed as they appear.
+
+Instead of specifying a specific arch, it's possible to use the word
+"all".  This causes the change to apply to all keywords presently
+specified in the ebuild.
+
+The ^ leader instructs ekeyword to remove the specified arch.
+
+=head1 OPTIONS
+
+Presently ekeyword is simple enough that it supplies no options.
+Probably I'll add B<--help> and B<--version> in the future, but for
+now it's enough to track the gentoolkit version.
+
+=head1 EXAMPLES
+
+To mark a single arch stable:
+
+  $ ekeyword alpha metalog-0.7-r1.ebuild
+  metalog-0.7-r1.ebuild
+    -KEYWORDS="~alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~sparc ~x86"
+    +KEYWORDS="alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~sparc ~x86"
+
+When bumping a package, to mark all arches for testing:
+
+  $ ekeyword ~all metalog-0.7-r2.ebuild
+  metalog-0.7-r2.ebuild
+    -KEYWORDS="alpha amd64 hppa ia64 mips ppc sparc x86"
+    +KEYWORDS="~alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~sparc ~x86"
+
+To signify that a package is broken for all arches except one:
+
+  $ ekeyword ^all -* ~x86 metalog-0.7-r3.ebuild
+  metalog-0.7-r3.ebuild
+    -KEYWORDS="~alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~sparc ~x86"
+    +KEYWORDS="-* ~x86"
+
+To do lots of things at once:
+
+  $ ekeyword alpha metalog-0.7-r1.ebuild \
+      ~all metalog-0.7-r2.ebuild ^all -* ~x86 metalog-0.7-r3.ebuild
+  metalog-0.7-r1.ebuild
+    -KEYWORDS="~alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~sparc ~x86"
+    +KEYWORDS="alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~sparc ~x86"
+  metalog-0.7-r2.ebuild
+    -KEYWORDS="alpha amd64 hppa ia64 mips ppc sparc x86"
+    +KEYWORDS="~alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~sparc ~x86"
+  metalog-0.7-r3.ebuild
+    -KEYWORDS="~alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~sparc ~x86"
+    +KEYWORDS="-* ~x86"
+
+=head1 NOTES
+
+This tool was written by Aron Griffis <agriffis@gentoo.org>.  Bugs
+found should be filed against me at http://bugs.gentoo.org/
diff --git a/gentoolkit-dev/src/ekeyword2/ekeyword2 b/gentoolkit-dev/src/ekeyword2/ekeyword2
new file mode 100755 (executable)
index 0000000..ce8842d
--- /dev/null
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+
+# Output like:
+# setuptools-0.6_rc9.ebuild
+# < KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd -x86 ~x86-fbsd"
+# ---
+# > KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~sparc-fbsd x86 ~x86-fbsd"
+
+from __future__ import with_statement
+from sys import argv
+from fnmatch import fnmatch
+from shutil import copyfile
+from os import environ as env
+
+import re
+import string
+
+from portage import settings
+
+STABLE_KEYWORDS = frozenset(settings["PORTAGE_ARCHLIST"].split())
+BROKEN_KEYWORDS = frozenset(['-*'] + ['-'+k for k in STABLE_KEYWORDS])
+TEST_KEYWORDS   = frozenset(['~'+k for k in STABLE_KEYWORDS])
+KNOWN_KEYWORDS  = STABLE_KEYWORDS | TEST_KEYWORDS | BROKEN_KEYWORDS
+
+argv = set(argv[1:])
+kw_re = re.compile(r'KEYWORDS="([^"]*)"')
+ebuilds = frozenset([x for x in argv if fnmatch(x, '*.ebuild')])
+pretend = bool(argv.intersection(('-p', '--pretend',)))
+keywords = argv.difference(('-p', '--pretend',)) - ebuilds
+
+if not ebuilds:
+       print 'usage: ekeyword [-p|--pretend] [^|~|-][all] [[^|~|-]arch [[^|~|-]arch]...] ebuild [ebuild...]'
+
+for e in ebuilds:
+       # TODO: error handling for file I/O
+       kw = set(keywords)
+       if not pretend:
+               try:
+                       copyfile(e, e+'.orig')
+               except IOError:
+                       print "Can't copy file %s. Check permissions." % e
+                       exit(1)
+       try:
+               with open(e) as c:
+                       ebuild = c.read()
+       except IOError:
+               print "Can't open file %s. Aborting." % e
+               exit(1)
+       
+       orig = kw_re.search(ebuild)
+       curkw = set(orig.groups()[0].split())
+
+       # ^ or ^all by itself means remove all keywords
+       # (however, other keywords established in the same args still get set.)
+       if kw.intersection(('^', '^all',)):
+               kw -= set(('^', '^all',))
+               curkw = set()
+
+       # ~ or ~all by itself means set ~keyword for all keywords
+       # since ~ expands to "$HOME" in the shell, assume the user meant ~ if we see
+       # the expansion of "$HOME". (Hope there's no user named 'all'.)
+       if kw.intersection(('~', '~all', env['HOME'],)):
+               kw -= set(('~', '~all', env['HOME'],))
+               curkw = set(['~'+k if k in STABLE_KEYWORDS else k for k in curkw])
+
+       for k in kw:
+               # Remove keywords starting with ^
+               if k[0] == '^':
+                       curkw -= set((k[1:], '-'+k[1:], '~'+k[1:], ))
+               # Set ~ and - keywords to TEST and BROKEN, respectively
+               elif k[0] == '~' or k[0] == '-':
+                       curkw -= set((k[1:], '-'+k[1:], '~'+k[1:], ))
+                       curkw |= set((k,))
+               # Set remaining keywords to STABLE
+               else:
+                       curkw -= set(('~'+k,))
+                       curkw |= set((k,))
+
+       # Sort by arch, then OS (Luckily, this makes -* show up first if it's there)
+       result = 'KEYWORDS="%s"' % ' '.join(sorted(curkw,
+               key=lambda x: x.strip(string.punctuation).lower()))
+
+       if not pretend:
+               try:
+                       with open(e, 'w') as rebuild:
+                               rebuild.write(kw_re.sub(result, ebuild))
+               except IOError:
+                       print "Can't write file %s. Aborting." % e
+                       exit(1)
+
+       unknown_keywords = curkw - KNOWN_KEYWORDS
+       if unknown_keywords:
+               print "\nWarning: Unknown keywords '%s'.\n" % ', '.join(sorted(unknown_keywords))
+
+       print '<<< %s' % orig.group()
+       print '>>> %s' % result
diff --git a/gentoolkit-dev/src/eshowkw/Makefile b/gentoolkit-dev/src/eshowkw/Makefile
new file mode 100644 (file)
index 0000000..77bf1d8
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+include ../../makedefs.mak
+
+.PHONY: all
+
+all:
+
+dist:
+       mkdir -p ../../$(distdir)/src/eshowkw/
+       cp Makefile eshowkw eshowkw.1 ../../$(distdir)/src/eshowkw/
+
+install: all
+       install -m 0755 eshowkw $(bindir)/
+       install -m 0644 eshowkw.1 $(mandir)/
+
diff --git a/gentoolkit-dev/src/eshowkw/eshowkw b/gentoolkit-dev/src/eshowkw/eshowkw
new file mode 100644 (file)
index 0000000..d5d3584
--- /dev/null
@@ -0,0 +1,357 @@
+#!/bin/bash
+# vim: set sw=4 sts=4 et tw=80 :
+
+# Author:        Ciaran McCreesh <ciaranm@gentoo.org>
+# Purpose:       Display ebuild keywords in a graphical form
+# Invocation:    eshowkw [ packagename ] (defaults to current directory if no
+#                packagename is provided)
+
+shopt -s extglob
+
+PID_TO_KILL=$$
+
+die() {
+    echo "$@" 1>&2
+    kill $PID_TO_KILL
+}
+
+trap 'exit 250' 15
+
+get_portage_dir() {
+    local dir
+    if [[ -z ${portage_dir_cache} ]] ; then
+        for dir in "${HOME}/cvs/gentoo-x86" "/usr/portage" ; do
+            [[ -d ${dir}/profiles ]] && portage_dir_cache=${dir} && break
+        done
+    fi
+    [[ -z ${portage_dir_cache} ]] && portage_dir_cache=$(portageq portdir )
+    export portage_dir_cache
+    echo ${portage_dir_cache}
+}
+
+version_sort() {
+    local items= left=0
+    items=( $@ )
+
+    while [[ ${left} -lt ${#items[@]} ]] ; do
+        local lowest_idx=${left}
+        local idx=$(( ${lowest_idx} + 1 ))
+        while [[ ${idx} -lt ${#items[@]} ]] ; do
+            version_compare "${items[${lowest_idx}]}" "${items[${idx}]}"
+            [[ $? -eq 3 ]] && lowest_idx=${idx}
+            idx=$(( ${idx} + 1 ))
+        done
+        local tmp=${items[${lowest_idx}]}
+        items[${lowest_idx}]=${items[${left}]}
+        items[${left}]=${tmp}
+        left=$(( ${left} + 1 ))
+    done
+    echo ${items[@]}
+}
+
+version_compare() {
+    local ver_a=${1} ver_b=${2} parts_a parts_b cur_idx_a=0 cur_idx_b=0
+    parts_a=( $(get_all_version_components "${ver_a}" ) )
+    parts_b=( $(get_all_version_components "${ver_b}" ) )
+
+    ### compare number parts.
+    local inf_loop=0
+    while true ; do
+        # grab the current number components
+        local cur_tok_a=${parts_a[${cur_idx_a}]}
+        local cur_tok_b=${parts_b[${cur_idx_b}]}
+
+        # number?
+        if [[ -n ${cur_tok_a} ]] && [[ -z ${cur_tok_a//[[:digit:]]} ]] ; then
+            cur_idx_a=$(( ${cur_idx_a} + 1 ))
+            [[ ${parts_a[${cur_idx_a}]} == "." ]] \
+                && cur_idx_a=$(( ${cur_idx_a} + 1 ))
+        else
+            cur_tok_a=""
+        fi
+
+        if [[ -n ${cur_tok_b} ]] && [[ -z ${cur_tok_b//[[:digit:]]} ]] ; then
+            cur_idx_b=$(( ${cur_idx_b} + 1 ))
+            [[ ${parts_b[${cur_idx_b}]} == "." ]] \
+                && cur_idx_b=$(( ${cur_idx_b} + 1 ))
+        else
+            cur_tok_b=""
+        fi
+
+        # done with number components?
+        [[ -z ${cur_tok_a} ]] && [[ -z ${cur_tok_b} ]] && break
+
+        # to avoid going into octal mode, strip any leading zeros. otherwise
+        # bash will throw a hissy fit on versions like 6.3.068.
+        cur_tok_a=${cur_tok_a##+(0)}
+        cur_tok_b=${cur_tok_b##+(0)}
+
+        # if a component is blank, make it zero.
+        [[ -z ${cur_tok_a} ]] && cur_tok_a=0
+        [[ -z ${cur_tok_b} ]] && cur_tok_b=0
+
+        # compare
+        [[ ${cur_tok_a} -lt ${cur_tok_b} ]] && return 1
+        [[ ${cur_tok_a} -gt ${cur_tok_b} ]] && return 3
+    done
+
+    ### number parts equal. compare letter parts.
+    local letter_a=
+    letter_a=${parts_a[${cur_idx_a}]}
+    if [[ ${#letter_a} -eq 1 ]] && [[ -z ${letter_a/[a-z]} ]] ; then
+        cur_idx_a=$(( ${cur_idx_a} + 1 ))
+    else
+        letter_a="@"
+    fi
+
+    local letter_b=
+    letter_b=${parts_b[${cur_idx_b}]}
+    if [[ ${#letter_b} -eq 1 ]] && [[ -z ${letter_b/[a-z]} ]] ; then
+        cur_idx_b=$(( ${cur_idx_b} + 1 ))
+    else
+        letter_b="@"
+    fi
+
+    # compare
+    [[ ${letter_a} < ${letter_b} ]] && return 1
+    [[ ${letter_a} > ${letter_b} ]] && return 3
+
+    ### letter parts equal. compare suffixes in order.
+    local suffix rule part r_lt r_gt
+    for rule in "alpha=1" "beta=1" "pre=1" "rc=1" "p=3" "r=3" ; do
+        suffix=${rule%%=*}
+        r_lt=${rule##*=}
+        [[ ${r_lt} -eq 1 ]] && r_gt=3 || r_gt=1
+
+        local suffix_a=
+        for part in ${parts_a[@]} ; do
+            [[ ${part#${suffix}} != ${part} ]] && \
+                [[ -z ${part##${suffix}*([[:digit:]])} ]] && \
+                suffix_a=${part#${suffix}}0
+        done
+
+        local suffix_b=
+        for part in ${parts_b[@]} ; do
+            [[ ${part#${suffix}} != ${part} ]] && \
+                [[ -z ${part##${suffix}*([[:digit:]])} ]] && \
+                suffix_b=${part#${suffix}}0
+        done
+
+        [[ -z ${suffix_a} ]] && [[ -z ${suffix_b} ]] && continue
+
+        [[ -z ${suffix_a} ]] && return ${r_gt}
+        [[ -z ${suffix_b} ]] && return ${r_lt}
+
+        # avoid octal problems
+        suffix_a=${suffix_a##+(0)} ; suffix_a=${suffix_a:-0}
+        suffix_b=${suffix_b##+(0)} ; suffix_b=${suffix_b:-0}
+
+        [[ ${suffix_a} -lt ${suffix_b} ]] && return 1
+        [[ ${suffix_a} -gt ${suffix_b} ]] && return 3
+    done
+
+    ### no differences.
+    return 2
+}
+
+get_all_version_components() {
+    local ver_str=${1} result result_idx=0
+    result=( )
+
+    while [[ -n "$ver_str" ]] ; do
+        case "${ver_str:0:1}" in
+            # number: parse whilst we have a number
+            [[:digit:]])
+                result[$result_idx]="${ver_str%%[^[:digit:]]*}"
+                ver_str="${ver_str##+([[:digit:]])}"
+                result_idx=$(($result_idx + 1))
+                ;;
+
+            # separator: single character
+            [-_.])
+                result[$result_idx]="${ver_str:0:1}"
+                ver_str="${ver_str:1}"
+                result_idx=$(($result_idx + 1))
+                ;;
+
+            # letter: grab the letters plus any following numbers
+            [[:alpha:]])
+                local not_match="${ver_str##+([[:alpha:]])*([[:digit:]])}"
+                result[$result_idx]=${ver_str:0:$((${#ver_str} - ${#not_match}))}
+                ver_str="${not_match}"
+                result_idx=$(($result_idx + 1))
+                ;;
+
+            # huh?
+            *)
+                result[$result_idx]="${ver_str:0:1}"
+                ver_str="${ver_str:1}"
+                result_idx=$(($result_idx + 1))
+                ;;
+        esac
+    done
+
+    echo ${result[@]}
+}
+
+get_package_dir() {
+    if [[ -z ${1} ]] ; then
+        pwd
+        return 0
+    fi
+
+    if [[ -d ${1} ]] ; then
+        readlink -f ${1}
+        return 0
+    fi
+
+    get_portage_dir 1>/dev/null
+    if [[ ${1/\/} != ${1} ]] ; then
+        local d=$(get_portage_dir )/${1}
+        if [[ -d ${d} ]] ; then
+            echo ${d}
+            return 0
+        fi
+    else
+        local d
+        d=( $(echo $(get_portage_dir )/*-*/${1} ) )
+        if [[ ${#d[@]} -gt 1 ]] ; then
+            die "${1} is ambiguous"
+        elif [[ -d ${d[0]} ]] ; then
+            echo ${d[0]}
+            return 0
+        fi
+    fi
+
+    return 1
+}
+
+repeat() {
+    local i
+    for (( i=0 ; i < ${1} ; i=$(( ${i} + 1 )) )) ; do
+        echo -n "${2}"
+    done
+}
+
+get_keywords() {
+    (
+        inherit() { :; }
+        source "$(pwd)/${1}" 2>/dev/null
+        echo ${KEYWORDS}
+    )
+}
+
+colorarch() {
+    case "${1}" in
+        amd64)
+            echo -n -e "\033[0;33m${2}\033[0;0m"
+            ;;
+        x86)
+            echo -n -e "\033[0;31m${2}\033[0;0m"
+            ;;
+        *)
+            echo -n "${2}"
+            ;;
+    esac
+}
+
+colourise() {
+    case "${1}" in
+        \*)
+            echo -n -e "\033[0;31m*\033[0;0m"
+            ;;
+        +)
+            echo -n -e "\033[0;32m+\033[0;0m"
+            ;;
+        -)
+            echo -n -e "\033[0;31m-\033[0;0m"
+            ;;
+        \~)
+            echo -n -e "\033[0;33m~\033[0;0m"
+            ;;
+        *)
+            echo -n "${1}"
+            ;;
+    esac
+}
+
+show_keyword_diagram() {
+    echo -n -e "Keywords for \033[1;34m"
+    local title=$(readlink -f $(pwd ) )
+    title=${title#$(readlink -f ../.. )/*( )}
+    echo -n "${title}"
+    echo -e "\033[0;0m:"
+    echo
+
+    local archs=() arch_length=0 arch=
+    while read arch
+    do
+        [[ -z "${arch}" ]] && continue
+        [[ "${arch:0:1}" == "#" ]] && continue
+        [[ ${#arch} -gt ${arch_length} ]] && arch_length=${#arch}
+
+        archs[${#archs[*]}]=$arch
+    done < "$(get_portage_dir)/profiles/arch.list"
+
+
+    local versions= pkgname= version_length=0 version=
+    pkgname=$(basename $(readlink -f ./ ) )
+    versions=( $(for e in $(echo *.ebuild ) ; do \
+            [[ -f ${e} ]] && echo ${e} | sed -e 's/\.ebuild$//g' \
+                    -e "s/^${pkgname}-//g" ; \
+            done ) )
+    versions=( $(version_sort ${versions[@]} ) )
+    for version in "${versions[@]}" ; do
+        [[ ${#version} -gt ${version_length} ]] && version_length=${#version}
+    done
+
+    local i=0 archletter=
+    for (( i = 0 ; i < ${arch_length} ; i=$(( ${i} + 1 )) )) ; do
+        repeat ${version_length} " "
+        echo -n " | "
+        for arch in "${archs[@]}" ; do
+            archletter="${arch:${i}:1}"
+            echo -n "$(colorarch "${arch}" "${archletter:- }" ) "
+        done
+        echo
+    done
+
+    repeat ${version_length} "-"
+    echo -n "-+"
+    repeat ${#archs[@]} "--"
+    echo
+
+    for version in "${versions[@]}" ; do
+        echo -n "${version}"
+        repeat $(( ${version_length} - ${#version} )) " "
+        echo -n " | "
+
+        local keyword keywords
+        keywords=( $(get_keywords "${pkgname}-${version}.ebuild" ) )
+        for arch in "${archs[@]}" ; do
+            local display=" "
+            [[ ${keywords[@]/-\*} != ${keywords[@]} ]] && display="*"
+            for keyword in "${keywords[@]}" ; do
+                [[ ${arch} == "${keyword#[~-]}" ]] && \
+                    display=${keyword:0:1} && \
+                    break;
+            done
+            [[ -z ${display#[~ *-]} ]] || display="+"
+            echo -n "$(colourise "${display}" ) "
+        done
+
+        echo
+    done
+}
+
+main() {
+    local dir=$(get_package_dir "${1}" )
+    [[ -z ${dir} ]] && die "Couldn't find '${1}'"
+    cd ${dir}
+    [[ $(echo *.ebuild ) != "*.ebuild" ]] || die "No ebuilds in ${dir}"
+    show_keyword_diagram
+    true
+}
+
+main "$@"
+
diff --git a/gentoolkit-dev/src/eshowkw/eshowkw.1 b/gentoolkit-dev/src/eshowkw/eshowkw.1
new file mode 100644 (file)
index 0000000..8d08048
--- /dev/null
@@ -0,0 +1,14 @@
+.TH "eshowkw" "1" "Ciaran McCreesh" "gentoolkit-dev"
+.SH "NAME"
+.LP 
+eshowkw \- Displays ebuild keywords in a graphical form.
+.SH "SYNTAX"
+.LP 
+eshowkw [ packagename ] (defaults to current directory if no packagename is provided)
+
+
+.SH "AUTHORS"
+.LP 
+Ciaran McCreesh <ciaranm@gentoo.org>
+.SH "BUGS"
+Please report any bugs to http://bugs.gentoo.org
diff --git a/gentoolkit-dev/src/eviewcvs/AUTHORS b/gentoolkit-dev/src/eviewcvs/AUTHORS
new file mode 100644 (file)
index 0000000..36d5bfd
--- /dev/null
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/gentoolkit-dev/src/eviewcvs/Makefile b/gentoolkit-dev/src/eviewcvs/Makefile
new file mode 100644 (file)
index 0000000..7ab81c6
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright 2005 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+%.1 : %.pod
+       pod2man $< > $@
+
+.PHONY: all
+all: eviewcvs.1
+
+dist: eviewcvs.1
+       mkdir -p ../../$(distdir)/src/eviewcvs/
+       cp Makefile AUTHORS README eviewcvs eviewcvs.pod eviewcvs.1 ../../$(distdir)/src/eviewcvs/
+
+install: all
+       install -m 0755 eviewcvs $(bindir)/
+       install -d $(docdir)/eviewcvs   
+       install -m 0644 AUTHORS README $(docdir)/eviewcvs/
+       install -m 0644 eviewcvs.1 $(mandir)/
+
+clean:
+       $(RM) eviewcvs.1
diff --git a/gentoolkit-dev/src/eviewcvs/README b/gentoolkit-dev/src/eviewcvs/README
new file mode 100644 (file)
index 0000000..c7258d7
--- /dev/null
@@ -0,0 +1,11 @@
+Most of the documentation is contained in the man-page, which you can
+read directly (using GNU man) by doing
+
+       man ./eviewcvs.1
+
+To rebuild the man-page from pod source, do
+
+       pod2man --name=eviewcvs --center='Gentoolkit' \
+               eviewcvs.pod eviewcvs.1
+
+03 Nov 2004 agriffis
diff --git a/gentoolkit-dev/src/eviewcvs/eviewcvs b/gentoolkit-dev/src/eviewcvs/eviewcvs
new file mode 100755 (executable)
index 0000000..280ec0b
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/bash
+# $Id$
+#
+# Copyright 2005, Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# Written by Aron Griffis <agriffis@gentoo.org>
+#
+# eviewcvs - generate viewcvs urls for some files
+#
+
+if [[ -t 1 ]]; then
+    blue="\e[34;01m"
+    cyan="\e[36;01m"
+    green="\e[32;01m"
+    red="\e[31;01m"
+    off="\e[0m"
+else
+    unset blue cyan green red off
+fi
+
+startdir="$PWD"
+url="http://sources.gentoo.org/viewcvs.py"
+unset diffs
+declare -a hdr orev
+
+chdir() {
+    cd "$1" || return
+
+    # Figure out where we are, hopefully
+    unset cwd root
+    if [[ -f CVS/Repository ]]; then
+        cwd=$(<CVS/Repository)
+    elif [[ -f .svn/entries ]]; then
+        cwd=$(grep -om1 'url=.*' .svn/entries)
+        cwd=${cwd#*/var/svnroot/}
+        cwd=${cwd%\"*}
+    fi
+}
+
+# Default to all files in directory
+[[ -n $* ]] || set -- *
+
+for f in "$@"; do
+    [[ -f $f ]] || continue
+
+    # Determine the directory settings
+    if [[ $f == */* ]]; then
+        chdir ${f%/*}
+        f=${f##*/}
+    else
+        chdir ${startdir}
+    fi
+
+    # Default to the directory settings
+    fwd=$cwd
+
+    # Get the header for this file, from which we can extract the root,
+    # directory and revision
+    hdr=( $(egrep -m1 -o '\$(Header|Id):[^$]*\$' "$f") )
+    frev=${hdr[2]}
+    case ${hdr[*]} in
+        \$Header:\ /var/cvsroot/*/*\ \$*)
+            fwd=${hdr[1]}                       # /var/cvsroot/gentoo-src/keychain/keychain.sh,v
+            fwd=${fwd#/var/cvsroot/}            # gentoo-src/keychain/keychain.sh,v
+            fwd=${fwd%/*}                       # gentoo-src/keychain
+            ;;
+        '')
+            if [[ -d CVS ]]; then
+                frev=$(cvs log "$f" 2>/dev/null | awk '/^head:/{print $2}')
+            elif [[ -d .svn ]]; then
+                frev=$(svn info "$f" 2>/dev/null | awk '/^Revision:/{print $2}')
+            fi
+            ;;
+    esac
+    [[ -n ${frev} ]] || continue
+
+    # Here is the simple URL to view it
+    echo "${url}/${fwd:+$fwd/}${green}${f}${off}?rev=${frev}&view=markup"
+
+    # Also supply a diff URL if possible
+    if [[ ${frev##*.} -gt 1 ]]; then
+        orev=( ${frev//./ } )           # convert to array
+        (( orev[${#orev[@]}-1]-- ))     # decrement the last element
+        orev=${orev[*]}                 # convert to string
+        orev=${orev// /.}               # revert spaces to dots
+        diffs="${diffs:+$diffs
+}${url}/${fwd:+$fwd/}${blue}${f}${off}?r1=${orev}&r2=${frev}"
+    fi
+done
+
+if [[ -n ${diffs} ]]; then
+    echo "${diffs}"
+fi
+
+# vim:set expandtab sw=4 smarttab:
diff --git a/gentoolkit-dev/src/eviewcvs/eviewcvs.pod b/gentoolkit-dev/src/eviewcvs/eviewcvs.pod
new file mode 100644 (file)
index 0000000..b4403c8
--- /dev/null
@@ -0,0 +1,48 @@
+=head1 NAME
+
+eviewcvs - Gentoo: generate viewcvs URLs
+
+=head1 SYNOPSIS
+
+eviewcvs [ I<files...> ]
+
+=head1 DESCRIPTION
+
+This tool generates a list of viewcvs URLs based on the files listed, or all the
+files in the current directory if the file list is omitted.  The first part of
+the output, hilighted in green, is the simple URLs to view the files.  The
+second part of the output, hilighted in blue, is the URLs to view the diffs
+against the previous revision.
+
+=head1 OPTIONS
+
+Presently eviewcvs is simple enough that it supplies no options.
+Probably I'll add B<--help> and B<--version> in the future, but for
+now it's enough to track the gentoolkit version.
+
+=head1 EXAMPLES
+
+To generate viewcvs URLs for a given file:
+
+  $ eviewcvs package.mask
+  http://www.gentoo.org/cgi-bin/viewcvs.cgi/profiles/package.mask?rev=1.3716&content-type=text/vnd.viewcvs-markup
+  http://www.gentoo.org/cgi-bin/viewcvs.cgi/profiles/package.mask?r1=1.3715&r2=1.3716
+
+To generate viewcvs URLs for all files in a directory:
+
+  $ cd portage/net-misc/keychain
+  $ eviewcvs
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/ChangeLog?rev=1.54&view=markup
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/Manifest?rev=1.86&view=markup
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/keychain-2.6.1.ebuild?rev=1.3&view=markup
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/keychain-2.6.2.ebuild?rev=1.1&view=markup
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/metadata.xml?rev=1.3&view=markup
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/ChangeLog?r1=1.53&r2=1.54
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/Manifest?r1=1.85&r2=1.86
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/keychain-2.6.1.ebuild?r1=1.2&r2=1.3
+  http://sources.gentoo.org/viewcvs.py/gentoo-x86/net-misc/keychain/metadata.xml?r1=1.2&r2=1.3
+
+=head1 AUTHOR
+
+This tool was written by Aron Griffis <agriffis@gentoo.org>.  Bugs
+found should be filed against me at http://bugs.gentoo.org/
diff --git a/gentoolkit-dev/src/imlate/Makefile b/gentoolkit-dev/src/imlate/Makefile
new file mode 100644 (file)
index 0000000..dd88d2b
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+include ../../makedefs.mak
+
+.PHONY: all
+
+all:
+
+dist:
+       mkdir -p ../../$(distdir)/src/imlate/
+       cp Makefile imlate imlate.1 ../../$(distdir)/src/imlate/
+
+install: all
+       install -m 0755 imlate $(bindir)/
+       install -m 0644 imlate.1 $(mandir)/
+
diff --git a/gentoolkit-dev/src/imlate/imlate b/gentoolkit-dev/src/imlate/imlate
new file mode 100755 (executable)
index 0000000..ed019cc
--- /dev/null
@@ -0,0 +1,484 @@
+#!/usr/bin/python
+
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+# author: Christian Ruppert <idl0r@gentoo.org>
+
+VERSION = "0.0.4"
+
+# works just with stable keywords!
+MAIN_ARCH = "auto" # can be overridden by -m ARCH
+TARGET_ARCH = "auto" # can be overridden by -t ARCH
+# auto means e.g.:
+# MAIN_ARCH = amd64
+# TARGET_ARCH = ~amd64
+# That will show you general stable candidates for amd64.
+# The arch will be taken from your portage settings (e.g. make.conf).
+
+################################
+# do not change anything below #
+################################
+
+from os.path import join, basename
+from sys import stderr, stdout
+from os import stat
+from time import time
+from xml.dom import minidom, NotFoundErr
+from xml.parsers.expat import ExpatError
+# TODO: just import needed stuff to safe memory and maybe use "as foo"
+import portage
+import portage.versions
+
+if __name__ == "__main__":
+       from optparse import OptionParser
+       from time import gmtime, strftime
+
+# portage < 2.1.6 needs portage_const instead of portage.const,
+# which is not the problem...
+# the problem is the keyword handling (ACCEPT_KEYWORDS)
+# portage < 2.1.6 does not support -*
+# but -* is needed to ensure that we just check for arch
+if portage.versions.vercmp(portage.VERSION, "2.1.6") < 0:
+       raise StandardError, "imlate requires portage >=2.1.6"
+
+# override/change portage module settings
+def _portage_settings( var, value, settings = None ):
+       if not settings:
+               settings = portage.settings
+
+       settings.unlock()
+       settings[var] = value
+       # backup_changes is very important since it can cause trouble,
+       # if we do not backup our changes!
+       settings.backup_changes( var )
+       settings.lock()
+
+# add stuff to our imlate dict
+def _add_ent( imlate, cat, pkg, ver, our_ver ):
+       if not cat in imlate.keys():
+               imlate[cat] = {}
+       if not pkg in imlate[cat].keys():
+               imlate[cat][pkg] = []
+
+       imlate[cat][pkg].append( ver )
+       imlate[cat][pkg].append( our_ver )
+
+       return imlate
+
+def _fill( width, line, fill = " " ):
+       while len( line ) < width:
+               line = "%s%s" % ( str( line ), str( fill ) )
+       return line
+
+# create a hopefully pretty result
+def show_result( conf, pkgs ):
+       # X - len(colX) = space to fill
+       col1 = 40
+       col2 = 20
+
+       _header = "%s candidates for 'gentoo' on '%s'"
+       _helper = "category/package[:SLOT]                 our version         best version"
+       _cand = ""
+       header = ""
+
+       if conf["FILE"] == "stdout":
+               out = stdout
+       elif conf["FILE"] == "stderr":
+               out = stderr
+       else:
+               out = open( conf["FILE"], "w" )
+
+       if conf["STABLE"] and conf["KEYWORD"]:
+               _cand = "%i Stable and %i Keyword(~)" % ( conf["STABLE_SUM"],
+                                                                                               conf["KEYWORD_SUM"] )
+       elif conf["STABLE"]:
+               _cand = "%i Stable" % conf["STABLE_SUM"]
+       elif conf["KEYWORD"]:
+               _cand = "%i Keyword(~)" % conf["KEYWORD_SUM"]
+
+       header = _header % ( _cand, conf["MAIN_ARCH"] )
+
+       print >> out, "Generated on: %s" % conf["TIME"]
+       print >> out, _fill( len( header ), "", "=" )
+       print >> out, header
+       print >> out, _fill( len( header ), "", "=" )
+       print >> out
+
+       print >> out, _helper
+       print >> out, _fill( len( _helper ), "", "-" )
+
+       for cat in sorted( pkgs.keys() ):
+               print >> out, "%s/" % cat
+               for pkg in sorted( pkgs[cat].keys() ):
+                       print >> out, "%s%s%s" % ( _fill( col1, ( "  %s" % pkg ) ),
+                                                                       _fill( col2, pkgs[cat][pkg][1] ),
+                                                                       pkgs[cat][pkg][0] )
+
+       if conf["FILE"] != "stdout":
+               out.close()
+
+def _get_metadata(metadata, element, tag):
+       values = []
+
+       try:
+               metadatadom = minidom.parse(metadata)
+       except ExpatError, e:
+               raise ExpatError("%s: %s" % (metadata, e,))
+
+       try:
+               elements = metadatadom.getElementsByTagName(element)
+               if not elements:
+                       return values
+       except NotFoundErr:
+               return values
+
+       try:
+               for _element in elements:
+                       node = _element.getElementsByTagName(tag)
+
+                       if tag == "herd" and (not node or not node[0].childNodes):
+#                              print >> stderr, "'%s' is missing a <herd> tag or it is empty," % metadata
+#                              print >> stderr, "please file a bug at https://bugs.gentoo.org and refer to http://www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=4"
+                               values.append("no-herd")
+                               continue
+
+                       values.append(node[0].childNodes[0].data)
+       except NotFoundErr:
+               raise NotFoundErr("%s: Malformed input: missing 'flag' tag(s)" % (metadata))
+
+       metadatadom.unlink()
+       return values
+
+def is_maintainer(maintainer, metadata):
+       data = []
+
+       if maintainer == None:
+               return True
+
+       mtainer = maintainer.split(",")
+
+       data = _get_metadata(metadata, "maintainer", "email")
+
+       if not data and len(maintainer) == 0:
+               return True
+       elif not data and len(maintainer) > 0:
+               return False
+       else:
+               for addy in data:
+                       for contact in mtainer:
+                               if addy == contact:
+                                       return True
+                               if addy.startswith(contact):
+                                       return True
+       return False
+
+def is_herd(herd, metadata):
+       data = []
+
+       if herd == None:
+               return True
+
+       hrd = herd.split(",")
+       data = _get_metadata(metadata, "pkgmetadata", "herd")
+
+       if not data and len(herd) == 0:
+               return True
+       elif not data and len(herd) > 0:
+               return False
+       else:
+               for hd in data:
+                       for hd2 in hrd:
+                               if hd == hd2:
+                                       return True
+                               if hd.startswith(hd2):
+                                       return True
+       
+       return False
+
+
+# fetch a list of arch (just stable) packages
+# -* is important to be sure that just arch is used
+def get_packages( conf ):
+       _pkgs = {}
+
+       _portage_settings( "ACCEPT_KEYWORDS", ( "-* %s" % str( conf["TARGET_ARCH"] ) ),
+                                       conf["portdb"].settings )
+
+       for cp in conf["portdb"].dbapi.cp_all():
+               cpvrs = []
+               slots = {}
+
+               if conf["USER_PKGS"]:
+                       if not cp in conf["USER_PKGS"]:
+                               continue
+
+               # None is important to match also on empty string
+               if conf["MAINTAINER"] != None:
+                       if not is_maintainer(conf["MAINTAINER"], join(conf["PORTDIR"], cp, "metadata.xml")):
+                               continue
+               if conf["HERD"] != None:
+                       if not is_herd(conf["HERD"], join(conf["PORTDIR"], cp, "metadata.xml")):
+                               continue
+
+               cpvrs = conf["portdb"].dbapi.match( cp )
+
+               for cpvr in cpvrs:
+                       slot = conf["portdb"].dbapi.aux_get( cpvr, ["SLOT"] )[0]
+                       if not slot in slots:
+                               slots[slot] = []
+                       slots[slot].append(cpvr)
+               
+               for slot in sorted(slots):
+                       cpvr = portage.versions.best( slots[slot] )
+
+                       if cpvr:
+                               ( cat, pkg, ver, rev ) = portage.versions.catpkgsplit( cpvr )
+
+                               if not cat in _pkgs.keys():
+                                       _pkgs[cat] = {}
+                               if not pkg in _pkgs[cat].keys():
+                                       _pkgs[cat][pkg] = []
+
+                               if rev != "r0":
+                                       ver = "%s-%s" % ( ver, rev )
+
+                               _pkgs[cat][pkg].append( ver )
+
+       return _pkgs
+
+# compare get_packages() against MAIN_ARCH
+def get_imlate( conf, pkgs ):
+       _portage_settings( "ACCEPT_KEYWORDS", ( "-* %s" % str( conf["MAIN_ARCH"] ) ),
+                                       conf["portdb"].settings )
+
+       stable = str( conf["MAIN_ARCH"].lstrip("~") )
+       testing = "~%s" % stable
+       exclude = "-%s" % stable
+       exclude_all = "-*"
+
+       imlate = {}
+
+       for cat in sorted( pkgs.keys() ):
+               for pkg in sorted( pkgs[cat].keys() ):
+                       for vr in pkgs[cat][pkg]:
+                               cpvr = ""
+                               abs_pkg = ""
+                               kwds = ""
+                               our = ""
+                               our_ver = ""
+                               mtime = 0
+                               slot = 0
+       
+                               # 0 = none(default), 1 = testing(~arch), 2 = stable(arch),
+                               # 3 = exclude(-arch), 4 = exclude_all(-*)
+                               # -* would be overridden by ~arch or arch
+                               kwd_type = 0
+
+                               cpvr = "%s/%s-%s" % ( cat, pkg, vr )
+       
+                               # absolute ebuild path for mtime check
+                               abs_pkg = join( conf["PORTDIR"], cat, pkg, basename( cpvr ) )
+                               abs_pkg = "%s.ebuild" % str( abs_pkg )
+       
+                               kwds = conf["portdb"].dbapi.aux_get( cpvr, ["KEYWORDS"] )[0]
+       
+                               # FIXME: %s is bad.. maybe even cast it, else there are issues because its unicode
+                               slot = ":%s" % conf["portdb"].dbapi.aux_get( cpvr, ["SLOT"] )[0]
+                               if slot == ":0":
+                                       slot = ""
+       
+                               # sorted() to keep the right order
+                               # e.g. -* first, -arch second, arch third and ~arch fourth
+                               # -* -foo ~arch
+                               # example: -* would be overridden by ~arch
+                               for kwd in sorted( kwds.split() ):
+                                       if kwd == stable:
+                                               kwd_type = 2
+                                               break
+                                       elif kwd == exclude:
+                                               kwd_type = 3
+                                               break
+                                       elif kwd == exclude_all:
+                                               kwd_type = 4
+                                       elif kwd == testing:
+                                               kwd_type = 1
+                                               break
+       
+                               # ignore -arch and already stabilized packages
+                               if kwd_type == 3 or kwd_type == 2:
+                                       continue
+                               # drop packages with -* and without ~arch or arch
+                               # even if there is another version which includes arch or ~arch
+                               if kwd_type == 4:
+                                       continue
+                               # drop "stable candidates" with mtime < 30 days
+                               # Shall we use gmtime/UTC here?
+                               if kwd_type == 1:
+                                       mtime = int( ( time() - stat( abs_pkg ).st_mtime ) / 60 / 60 / 24 )
+                                       if mtime < conf["MTIME"]:
+                                               continue
+       
+                               # look for an existing stable version
+                               our = portage.versions.best( conf["portdb"].dbapi.match( "%s/%s%s" % ( cat, pkg, slot ) ) )
+                               if our:
+                                       _foo = portage.versions.pkgsplit( our )
+                                       our_ver = _foo[1]
+                                       if _foo[2] != "r0":
+                                               our_ver = "%s-%s" % ( our_ver, _foo[2] )
+                               else:
+                                       our_ver = ""
+       
+                               # we just need the version if > our_ver
+                               if our_ver:
+                                       if portage.versions.vercmp( our_ver, vr ) >= 0:
+                                               continue
+       
+                               if kwd_type == 1 and conf["STABLE"]:
+                                       imlate = _add_ent( imlate, cat, ("%s%s" % (pkg, slot)), vr, our_ver )
+                                       conf["STABLE_SUM"] += 1
+                               elif kwd_type == 0 and conf["KEYWORD"]:
+                                       conf["KEYWORD_SUM"] += 1
+                                       imlate = _add_ent( imlate, cat, ( "~%s%s" % (pkg, slot) ),
+                                                                       vr, our_ver )
+
+       return imlate
+
+# fetch portage related settings
+def get_settings( conf = None ):
+       if not isinstance( conf, dict ) and conf:
+               raise TypeError, "conf must be dict() or None"
+       if not conf:
+               conf = {}
+
+       # TODO: maybe we should improve it a bit ;)
+       mysettings = portage.config( config_incrementals = portage.const.INCREMENTALS, local_config = False )
+
+       if conf["MAIN_ARCH"] == "auto":
+               conf["MAIN_ARCH"] = "%s" % mysettings["ACCEPT_KEYWORDS"].split(" ")[0].lstrip("~")
+       if conf["TARGET_ARCH"] == "auto":
+               conf["TARGET_ARCH"] = "~%s" % mysettings["ACCEPT_KEYWORDS"].split(" ")[0].lstrip("~")
+
+       # TODO: exclude overlay categories from check
+       if conf["CATEGORIES"]:
+               _mycats = []
+               for _cat in conf["CATEGORIES"].split(","):
+                       _cat = _cat.strip()
+                       _mycats.append(_cat )
+                       if _cat not in mysettings.categories:
+                               raise ValueError, "invalid category for -C switch '%s'" % _cat
+               mysettings.categories = _mycats
+
+       # maybe thats not necessary because we override porttrees below..
+       _portage_settings( "PORTDIR_OVERLAY", "", mysettings )
+       trees = portage.create_trees()
+       trees["/"]["porttree"].settings = mysettings
+       portdb = trees["/"]["porttree"]
+       portdb.dbapi.mysettings = mysettings
+       portdb.dbapi.porttrees = [portage.portdb.porttree_root]
+       # does it make sense to remove _all_ useless stuff or just leave it as it is?
+       #portdb.dbapi._aux_cache_keys.clear()
+       #portdb.dbapi._aux_cache_keys.update(["EAPI", "KEYWORDS", "SLOT"])
+
+       conf["PORTDIR"] = portage.settings["PORTDIR"]
+       conf["portdb"] = portdb
+
+       return conf
+
+
+# just for standalone
+def main():
+       conf = {}
+       pkgs = {}
+
+       parser = OptionParser( version = "%prog " + VERSION )
+       parser.usage = "%prog [options] [category/package] ..."
+       parser.disable_interspersed_args()
+
+       parser.add_option( "-f", "--file", dest = "filename", action = "store", type = "string",
+                       help = "write result into FILE [default: %default]", metavar = "FILE", default = "stdout" )
+       parser.add_option( "-m", "--main", dest = "main_arch", action = "store", type = "string",
+                       help = "set main ARCH (e.g. your arch) [default: %default]", metavar = "ARCH", default = MAIN_ARCH )
+       parser.add_option( "-t", "--target", dest = "target_arch", action = "store", type = "string",
+                       help = "set target ARCH (e.g. x86) [default: %default]", metavar = "ARCH", default = TARGET_ARCH )
+       parser.add_option( "--mtime", dest = "mtime", action = "store", type = "int",
+                       help = "set minimum MTIME in days [default: %default]", metavar = "MTIME", default = 30 )
+
+       # TODO: leave a good comment here (about True/False) :)
+       parser.add_option( "-s", "--stable", dest = "stable", action = "store_true", default = False,
+                       help = "just show stable candidates (e.g. -s and -k is the default result) [default: True]" )
+       parser.add_option( "-k", "--keyword", dest = "keyword", action = "store_true", default = False,
+                       help = "just show keyword candidates (e.g. -s and -k is the default result) [default: True]" )
+
+       parser.add_option( "-M", "--maintainer", dest = "maintainer", action = "store", type = "string",
+                       help = "Show only packages from the specified maintainer", metavar = "MAINTAINER", default = None)
+
+       parser.add_option( "-H", "--herd", dest = "herd", action = "store", type = "string",
+                       help = "Show only packages from the specified herd", metavar = "HERD", default = None)
+
+#      # EXPERIMENTAL
+#      parser.add_option( "-e", "--experimental", dest = "experimental", action = "store_true", default = False,
+#                      help = "enables experimental functions/features (have a look for # EXPERIMENTAL comments in the source) [default: %default]" )
+
+       parser.add_option( "-C", "--category", "--categories", dest = "categories", action = "store", default = None,
+                       metavar = "CATEGORIES",
+                       help = "just check in the specified category/categories (comma separated) [default: %default]")
+
+       ( options, args ) = parser.parse_args()
+
+       if len( args ) > 0:
+               conf["USER_PKGS"] = args
+       else:
+               conf["USER_PKGS"] = []
+
+       # cleanup optparse
+       try:
+               parser.destroy()
+       except AttributeError:
+               # to be at least python 2.4 compatible
+               del parser._short_opt
+               del parser._long_opt
+               del parser.defaults
+
+       # generated timestamp (UTC)
+       conf["TIME"] = strftime( "%a %b %d %H:%M:%S %Z %Y", gmtime() )
+
+       # package counter
+       conf["KEYWORD_SUM"] = 0
+       conf["STABLE_SUM"] = 0
+
+       if not options.main_arch in portage.archlist and options.main_arch != "auto":
+               raise ValueError, "invalid MAIN ARCH defined!"
+       if not options.target_arch in portage.archlist and options.target_arch != "auto":
+               raise ValueError, "invalid TARGET ARCH defined!"
+
+       conf["MAIN_ARCH"] = options.main_arch
+       conf["TARGET_ARCH"] = options.target_arch
+
+       conf["FILE"] = options.filename
+       conf["MTIME"] = options.mtime
+
+       if not options.stable and not options.keyword:
+               conf["STABLE"] = True
+               conf["KEYWORD"] = True
+       else:
+               conf["STABLE"] = options.stable
+               conf["KEYWORD"] = options.keyword
+
+#      conf["EXPERIMENTAL"] = options.experimental
+       conf["CATEGORIES"] = options.categories
+
+       conf["MAINTAINER"] = options.maintainer
+       conf["HERD"] = options.herd
+
+       # append to our existing
+       conf = get_settings( conf )
+       pkgs = get_packages( conf )
+       pkgs = get_imlate( conf, pkgs )
+
+       show_result( conf, pkgs )
+
+if __name__ == "__main__":
+       main()
+
diff --git a/gentoolkit-dev/src/imlate/imlate.1 b/gentoolkit-dev/src/imlate/imlate.1
new file mode 100644 (file)
index 0000000..d4a1dc2
--- /dev/null
@@ -0,0 +1,48 @@
+.TH "imlate" "1" "0.0.4" "Christian Ruppert" "gentoolkit-dev"
+.SH "NAME"
+.LP 
+imlate \- Displays candidates for keywords for an architecture based upon a target architecture.
+.SH "SYNTAX"
+.LP 
+imlate [options]
+
+
+.SH "OPTIONS"
+.TP 
+.B \-\-version
+show program's version number and exit
+.TP 
+.B \-h, \-\-help
+show this help message and exit
+.TP 
+.B \-f FILE, \-\-file=FILE
+write result into FILE [default: stdout]
+.TP 
+.B \-m ARCH, \-\-main=ARCH  
+set main ARCH (e.g. your arch) [default: amd64]
+.TP 
+.B \-t ARCH, \-\-target=ARCH
+set target ARCH (e.g. x86) [default: x86]
+.TP
+.B \-\-mtime=MTIME
+set minimum MTIME in days [default: 30]
+.TP 
+.B \-s, \-\-stable
+just show stable candidates (e.g. \-s and \-k is the default result) [default: True]
+.TP 
+.B \-k, \-\-keyword
+just show keyword candidates (e.g. \-s and \-k is the default result) [default: True]
+.TP
+.B \-M MAINTAINER, \-\-maintainer=MAINTAINER
+Show only packages from the specified maintainer
+.TP
+.B \-H HERD, \-\-herd=HERD
+Show only packages from the specified herd
+.TP 
+.B \-C CATEGORIES, \-\-category=CATEGORIES, \-\-categories=CATEGORIES
+just check in the specified category/categories (comma separated) [default: none]
+.SH "AUTHORS"
+.LP 
+Christian Ruppert <idl0r@gentoo.org>
+.SH "BUGS"
+Please report any bugs to http://bugs.gentoo.org
diff --git a/gentoolkit/AUTHORS b/gentoolkit/AUTHORS
new file mode 100644 (file)
index 0000000..c8b89aa
--- /dev/null
@@ -0,0 +1,28 @@
+* gentoolkit
+  Original author: Karl Trygve Kalleberg <karltk@gentoo.org>
+  Library additions and refactor: Douglas Anderson <douglasjanderson@gmail.com>
+  Current maintainer: Paul Varner <fuzzyray@gentoo.org>
+
+* eclean
+  Original author: Thomas de Grenier de Latour (tgl) <degrenier@easyconnect.fr>
+
+* epkginfo
+  Author: Ned Ludd <solar@gentoo.org>
+  earch: Eldad Zack <eldad@gentoo.org>
+  metadata: Olinger <EvvL AT RustedHalo DOT net>
+
+* equery
+  Original author: Karl Trygve Kalleberg <karltk@gentoo.org>
+  0.3.0 author: Douglas Anderson <douglasjanderson@gmail.com>
+
+* eread
+  Original author: Donnie Berkholz <dberkholz@gentoo.org>
+  Updated by: Uwe Klosa <uwe.klosa@gmail.com>
+
+* euse
+  Original perl version: Arun Bhanu <codebear@gentoo.org>
+  New bash version: Marius Mauch <genone@gentoo.org>
+
+* revdep-rebuild
+  Original author: Stanislav Brabec
+  Rewrite author: Michael A. Smith
diff --git a/gentoolkit/COPYING b/gentoolkit/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gentoolkit/CREDITS b/gentoolkit/CREDITS
new file mode 100644 (file)
index 0000000..3f9d6f0
--- /dev/null
@@ -0,0 +1,16 @@
+
+       This is an incomplete list of people who have contributed non-
+       trivial patches to gentoolkit-0.3.0 and later, in the spirit of
+       the Linux kernel CREDITS file. It is sorted alphabetically by
+       name (N). Other fields are email (E) and description (D).
+       We'd like to thank:
+
+-------------------------------------------------------------------------------
+
+N: Brian Dolbec
+E: dol-sen@users.sourceforge.net
+D: Separating dependency gathering functionality from output in depends.py.
+
+
+
+# vim: set ts=8 sw=8 tw=80:
diff --git a/gentoolkit/ChangeLog b/gentoolkit/ChangeLog
new file mode 100644 (file)
index 0000000..85f41f8
--- /dev/null
@@ -0,0 +1,864 @@
+2009-12-17: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to properly honor
+       PORTAGE_NICENESS as an incremental to the current nice level (Bug
+       297174).
+
+2009-12-08: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Merge rev 113 from djanderson's genscripts repo
+
+2009-10-30: Christian Ruppert <idl0r@gentoo.org>
+       * revdep-rebuild: Speedup portageq queries. Include FuzzyRay's patch to
+               respect EMERGE_DEFAULT_OPTS.
+
+2009-10-27: Christian Ruppert <idl0r@gentoo.org>
+       * revdep-rebuild: Use realpath in get_file_owner to be able to get the
+               package containing e.g. /lib/libreadline.so.6 on amd64, fixes bug 280341.
+
+2009-10-27: Christian Ruppert <idl0r@gentoo.org>
+       * revdep-rebuild.1: Fix bug 281050, thanks to Jesús Guerrero
+               <i92guboj@gentoo.org>.
+
+2009-10-26: Christian Ruppert <idl0r@gentoo.org>
+       * euse: Don't add an additional new line at the end of file.
+       * equery.1: Fix bug 248844, thanks to J M W <ultip@ymail.com>.
+
+2009-08-18: Robert Buchholz <rbu@gentoo.org>
+       * glsa-check: Remove 'new' target from glsa-list, everyone
+        should use 'affected' or 'all'
+       * glsa-check: fix getminupgrade API doc and TypeError (Bug 281101)
+       * glsa-check: Make --pretend output in glsa-check nicer.
+        Add colors and fix linebreaks.
+
+2009-06-22: Paul Varner <fuzzyray@gentoo.org>
+       * glsa-check: Fix traceback with glsa-check -f (Bug 275105)
+
+2009-06-05: Paul Varner <fuzzyray@gentoo.org>
+       * epkginfo: Add patch from Sebastian Mingramm to Make epkginfo slot
+       aware and only print keywords for the highest visible versions in each
+       slot. (Bug 232635)
+
+2009-05-20: Paul Varner <fuzzyray@gentoo.org>
+       * All: Convert from using /etc/gentoolkit-version to
+       /usr/share/gentoolkit/VERSION
+
+2009-05-20: Paul Varner <fuzzyray@gentoo.org>
+       * glsa-check: Add patches from Robert Buchholz to do the following:
+       Handle unicode encoding better, python tweaks to speedup execution,
+       change behavior of getMinUpgrade, restructure system affection
+       detection, and do not inject GLSAs into the checkfile when fixing.
+
+2009-05-07: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Add patch from djanderson to make meta get and check package
+       dirs in a manner similar to other gentoolkit scripts and fix
+       some docstrings. (Bug 268895)
+
+2009-05-05: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Added modular rewrite from djanderson
+       * gentoolkit: Added modular rewrite from djanderson
+       * All: converted to setup.py build system
+
+2009-04-30: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add patch from loki_val to check -l dependencies in 
+       .la files (Bug #267898)
+
+2009-04-24: Paul Varner <fuzzyray@gentoo.org>
+       * ekeyword: Fix to handle multiline KEYWORDS (Bug #267250)
+
+2009-01-08: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix package.py to account for PORTDIR being a symbolic link
+       when checking if a package is in an overlay. (Bug #253968)
+
+2008-11-25: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fixes for non-linux Gentoo systems. Add patch from
+       igli to fix find command to comply with POSIX. Change awk calls to
+       gawk.
+
+2008-11-11: Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Add --strict option (Bug 246242).
+       * echangelog: Fix processing of virtual category (Bug 179530)
+
+2008-09-17: Paul Varner <fuzzyray@gentoo.org>
+       * euse: Fix check_sanity function to use get_all_make_defaults
+       function when checking for the make.defaults files in the profile.
+       (Bug #233651)
+
+2008-09-03: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix depgraph function to print out dependencies that don't
+       resolve to a package (Bug #236492)
+
+2008-08-26: Paul Varner <fuzzyray@gentoo.org>
+       * glsa-check: Fix has_key() deprecation message. (Bug #232797)
+       * revdep-rebuild: Update fix for Bug 232270 to utilize better patch
+       from Ian Abbott.
+
+2008-08-22: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix find_packages and find_installed_packages to print
+       a warning instead of a traceback when an InvalidAtom exception occurs.
+       (Bug #234358)
+       * equery: Fix equery belongs to strip multiple slashes from path
+       names. (Bug #234584)
+
+2008-07-24: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix equery check to convert mtime to an integer so that
+       comparisions always work. Thanks to Alexey Parshin for discovering the
+       problem and providing a patch. (Bug 232803)
+
+2008-07-22: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix gentoolkit.split_package_name to work with
+       newer portage.catpkgsplit that now returns a tuple instead of a
+       list. (Bug 232599)
+
+2008-07-21: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix filtering of masked paths from SEARCH_DIRS
+       variable. (Bug 232270)
+
+2008-07-18: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Remove prefixed '+/-' signs from IUSE for equery uses
+       command. (Bug 232019)
+
+2008-07-09: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to use TMPDIR instead of HOME for
+       temporary files. (Bug 203414)
+       * revdep-rebuild: Fix revdep-rebuild to not evaluate broken objects
+       multiple times. (Bug 220761)
+
+2008-07-09: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix gentoolkit to work without thread support in 
+       python. (Bug 223255)
+
+2008-06-16: Marius Mauch <genone@gentoo.org>
+       * euse: Add support for multi-parent profiles, account for missing 
+       final newline in make.conf
+
+2008-03-19: Paul Varner <fuzzyray@gentoo.org>
+       * glsa-check: Fix imports so mail functionality in glsa-check works
+       with python versions less than 2.5 (Bug 211706)
+
+2008-03-13: Paul Varner <fuzzyray@gentoo.org>
+       * euse: Add --info-installed option from patch provided by Andreas
+       Waidler. (Bug 212573)
+
+2008-03-13: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix trying to emerge an empty list of packages. (Bug
+       213294)
+
+2008-02-28: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix traceback when accessing the portage
+       db. (Bug #211716)
+
+2008-02-21: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: "Use /etc/init.d/functions.sh instead of
+       /sbin/functions.sh. (Bug 210940)
+
+2008-02-18: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Apply patch to allow combined short options.
+       (Bug 188343)
+       * revdep-rebuild: Don't duplicate broken file output. (Bug 201319)
+       * revdep-rebuild: unset GREP_OPTIONS to prevent problems with grep.
+       (Bug 189257)
+       * revdep-rebuild: Apply patch to prevent false matches of object names.
+       (Bug 196460)
+       * revdep-rebuild: Apply patch to better handle masked and removed
+       packages. (Bug 205227)
+       * revdep-rebuild: Filter SEARCH_DIRS_MASK paths from SEARCH_DIRS.
+       (Bug 194993)
+       * revdep-rebuild: Apply patch for revdep-rebuild portable find function.
+       (Bug 194234)
+       * equery: Fix equery list to not generate an internal portage error when
+       fed input with too many slashes. (Bug 119806)
+       * equery: Assume the -p flag when equery list -I is used by itself.
+       (Bug 106278)
+
+
+2007-10-09: Marius Mauch <genone@gentoo.org>
+       * glsa-check: Change "affected" target so it's based on "new" instead of 
+       "all" (IOW: exclude already applied/injected GLSAs).
+
+2007-10-05: Marius Mauch <genone@gentoo.org>
+       * glsa-check: Use UTF-8 strings to avoid EncodeErrors if a GLSA contains
+       non-ascii characters (bug #162493)
+
+2007-09-19: Paul Varner <fuzzyray@gentoo.org>
+       * epkginfo: Fix handling of KEYWORDS="" in an ebuild. (Bug #193108)
+       * revdep-rebuild: Fix handling of /var/db/pkg when it is a symbolic
+       link. (Bug #179392)
+
+2007-09-18: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Apply patch from Carlo Marcelo Arenas Belon to fix incorrect
+       display of masking status in list command. (Bug #188678)
+       * revdep-rebuild: Correctly handle LD_LIBRARY_MASK when checking for
+       "no version information" errors/ (Bug #182882)
+
+2007-09-12: Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Fix processing of the long arguments to work correctly. (Bug
+       #192345)
+       * revdep-rebuild: Correctly handle the case where an ebuild no longer
+       exists for a package (Bug #188918)
+       * eread: Fix eread to not accept invalid input for file selection.
+       (Bug #189994)
+
+2007-08-08: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix progress bar to only update when there is a
+       change (Bug #186945)
+       * revdep-rebuild: Ensure that we source functions.sh before calling
+       ewarn, etc.
+
+2007-08-06: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix processing of .la files (Bug #187453)
+       * revdep-rebuild: Add -X option back for backwards compatibilty (Bug
+       #187366)
+
+2007-07-30: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix grepping for non-existant package-owners file
+       (Bug #187141)
+
+2007-07-05: Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Added refactored revdep-rebuild from Michael A.
+       Smith (Bug #184042)
+
+2007-05-30: Marius Mauch <genone@gentoo.org>
+       * glsa-check: check SLOT when selecting and displaying upgrades
+       * glsa-check: new --emergelike option to use the best version 
+       within the same SLOT instead of the one with the smallest delta.
+       * glsa-check: prefer visible upgrades to masked upgrades
+       * equery: check for and warn about unknown options (bug 119674)
+       * equery,eclean,glsa-check,epkginfo: Only add /usr/lib/portage/pym 
+       to python search path when necessary
+
+2007-05-21: Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Add patch from genstef to fix issues from Bug 176337
+
+2007-05-11: Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Changed permission check to see if you are either root or
+       belong to the portage group (Bug #177385)
+
+2007-05-11: Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Updated eclean to not delete metadata.dtd by default (Bug
+       #176951)
+
+2007-05-10: Marius Mauch <genone@gentoo.org>
+       * euse: Fix incorrect flag status display when a flag appears multiple
+       times in a single location
+
+2007-04-25: Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Re-added subversion/git support with fixes from genstef.
+       (Bug #136048)
+
+2007-04-24: Paul Varner <fuzzyray@gentoo.org>
+       * etcat: Removed from Makefile (deprecated since 04-25-2005)
+       * qpkg: Removed from Makefile (deprecated since 04-25-2005)
+
+2007-04-23: Paul Varner <fuzzyray@gentoo.org>
+       * genpkgindex, epkginfo: Move to /usr/bin from
+       /usr/lib/gentoolkit/bin (Bug #175759)
+
+2007-04-10: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Change equery uses to command to display the best matching
+       uninstalled package version if an uninstalled package is specified.
+       Changed the meaning of -a to mean display all versions. (Bug #152325)
+
+2007-04-01: Alec Warner <antarus@gentoo.org>
+       * eread: Fix path and fully qualified paths (Bug #172969)
+
+2007-03-31: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix traceback in equery which (Bug #134053)
+
+2007-03-29: Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Change package.get_???_deps() methods to try the portage
+       tree first, since emerge always uses the portage tree for dependencies.
+       (Bug #164678)
+
+2007-03-29: Paul Varner <fuzzyray@gentoo.org>
+       * equery: Convert deprecated strings functions to str methods (Bug
+       #172694)
+
+2007-03-25: Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Remove subversion/git patch due to many bugs.
+
+2007-03-18 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Change --no-color to --nocolor for consistency
+       within gentoolkit. (Bug #165165)
+
+2007-03-16 Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix typo in package.py (Bug #168347)
+
+2007-03-15 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix equery check to not fail for symlinks prefixed with ./
+       (Bug #170702)
+
+2007-03-14 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Trim trailing slash from query for equery belongs command
+       (Bug #170981)
+
+2007-03-13 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix bug with --package-names option not rebuilding
+       packages (Bug #169761)
+
+2007-03-10 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Add --depth option to equery depgraph to limit the depth of
+       the dependency graph. (Bug #115807)
+
+2007-03-09 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add support to detect "no version information
+       available" message from ldd (Bug #169973)
+
+2007-03-08 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Improved handling of KeyError in equery depends command
+       (Bug #169929)
+
+2007-03-07 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Change ordering algorithm to use --deep instead of
+       --emptytree on the advice of zmedico
+
+2007-02-26 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Display access information in verbose list mode (bug 168482)
+
+2007-02-19 Paul Varner <fuzzyray@gentoo.org>
+       * echangelog: Updated to support git and subversion (Bug #136048)
+
+2007-01-10 Paul Varner <fuzzyray@gentoo.org>
+       * epkgmove: removed epkgmove command due to popular demand. (Bug
+       161360)
+       * gensync: Deprecated gensync in favor of app-portage/layman (multiple
+       bugs)
+
+2007-01-02 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix equery depends --indirect command. (Bug #124552)
+
+2006-12-31 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Reworked equery depends command to be more accurate.
+
+2006-12-13 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix handling of /etc/portage/package.mask (Bug
+       #158025) Thanks to Wolfram Schlich for the patch.
+
+2006-12-12 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Add --tree option to equery files command. (Bug #62898)
+       Thanks to scope for the patch.
+
+2006-12-06 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Modify equery size command to work like the equery list
+       command for pkgspec arguments
+
+2006-11-27 Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Fix typographical error in help and man page. (Bug #156243)
+
+2006-10-11 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix fileAsStr to understand device files.
+       (http://forums.gentoo.org/viewtopic-p-3639575.html)
+
+2006-10-07 Paul Varner <fuzzyray@gentoo.org>
+       * euse: Fix quoting bug in get_real_path() (Bug #150335).
+
+2006-09-21 Paul Varner <fuzzyray@gentoo.org>
+       * eread: Add eread script for reading and managing portage ELOG files.
+       Thanks to Donnie Berkholz for writing this.
+
+2006-09-03 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Remove unused environment variables before calling
+       emerge (Bug #142074). Check for permissions to write temporary files
+       (Bug #142308)
+
+2006-08-12 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Add new --mail option to send out vulnerability reports
+       (output of --list plus --dump for each matched glsa as attachment),
+       using elog configuration.
+
+2006-07-31 Paul Varner <fuzzyray@gentoo.org>
+       * euse: Replace calls to readlink with bash function for Gentoo/ALT
+       compatibility. (Bugs #140477, #128960)
+
+2006-07-28 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to correctly handle --ask being
+       passed to emerge. Thanks to Sal Gonzalez <ghostx@optonline.net> for
+       the patch. (Bug #37485)
+
+2006-07-07 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Rename --no-path to --no-ld-path and change
+       functionality to not set LD_LIBRARY_PATH.  This fixes bug #96946 as
+       well as bug #137313
+       * revdep-rebuild: Apply patch from truedfx to fix bug #38751
+
+2006-07-05 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add --no-path option to revdep-rebuild for bug
+       #137313
+
+2006-06-25 Marius Mauch <genone@gentoo.org>
+       * glsa-check: update cve code for bug 128115
+
+2006-06-14 Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix package.py to honor $ROOT. (bug #136811)
+
+2006-05-22 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Use qfile to locate packages if portage-utils is
+       installed (Bug #128374). Be even more paranoid about extra slashes in
+       path names (Bug #128108). Remove unused code. Update configuration
+       section of manpage (Bug #126038).
+
+2006-04-02 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Remove double-slashes from path names (Bug #128108)
+
+2006-04-01 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add fix so that packages no longer in the tree cause
+       errors (Bug #128174). Fix case where masked packages cause
+       revdep-rebuild to not rebuild any packages (Bug #128085)
+
+2006-03-29 Marius Mauch <genone@gentoo.org>
+       * euse: Add support for special %active argument as placeholder for
+       active use flags
+
+2006-03-26 Aron Griffis <agriffis@gentoo.org>
+       * echangelog: Don't warn about missing ebuilds when updating 
+         copyrights #120061
+
+2006-03-25 Aron Griffis <agriffis@gentoo.org>
+       * eviewcvs: Update for sources.gentoo.org, add subversion support
+
+2006-03-21 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix to clear environment before portageq call. (Bug
+       #126038)
+
+2006-03-08 Paul Varner <fuzzyray@gentoo.org>
+       * genpkgindex: Add binary package indexing utility. (Bug 82132)
+
+2006-03-01 Paul Varner <fuzzyray@gentoo.org>
+       * gentoolkit: Fix depends parsing to properly handle conjunction. (bug
+       #123725). Thanks to tgl for the patch.
+       * gentoolkit: Added function to get post-merge dependencies (PDEPEND)
+       (bug #99191)
+       * gentoolkit: Change get_dependency functions to always use the
+       portage tree
+       * equery: Added post-merge dependencies to depends and depgraph
+       actions. (bug #99191)
+       * equery: Removed requirement for package to be installed to use
+       depgraph action.
+
+2006-02-16 Marius Mauch <genone@gentoo.org>
+       * euse: add/remove use flags even if there is no USE= statement in make.conf
+       (bug #95432)
+
+2006-02-16 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Fix bug causing the wrong summary to be displayed 
+       for --test --verbose (bug #123084)
+
+2006-02-06 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Reset PORTAGE_NICENESS, so that emerge is not niced
+       twice. Thanks to Lukas Reck for the patch. (Bug 121482)
+
+2006-01-24 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Use vbd information in verbose list mode (patch by solar)
+
+2006-01-18 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Add capability to check libtool .la files for
+       non-existant references.
+
+2006-01-06 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to play nicely with portage-2.1
+       (Bug 118124)
+
+2005-12-28 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix to automatically determine how to call find (Bug 111203)
+
+2005-12-19 Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Add regular expression matching for exclude files (Bug 114365)
+
+2005-12-13 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix USE flag parsing. (Bug 115294)
+
+2005-12-07 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix revdep-rebuild to work with findutils-4.2.27.
+       (bug 111203)
+       * equery: Added note to error message about quoting redirection
+       characters. (Bug 113423)
+       * gentoolkit: Removed python-config call from make file. (Bug 113386)
+
+2005-11-23 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Changed default behavior for equery list to search for
+       partial name matches. Added equery list examples to the equery man
+       page. (Bugs 113032, 113134)
+
+2005-11-15 Marius Mauch <genone@gentoo.org>
+       * glsa-check: Changed several messages to stderr instead of stdout
+       * glsa-check: Added new --cve option as requested by solar (bug 98589)
+       * glsa-check: Added support for a EMERGE_OPTS env variable to modify the emerge call of glsa-check --fix
+       * glsa-check: Added a new target "affected"
+       * glsa-check: Removed the warning message as it is now pretty much tested
+       * glsa-check: Show GLSA title on --test if --verbose is also used
+       
+2005-11-11 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Added sanity check to equery files (Bug 75983)
+       * equery: Fix string matching for equery depends (Bug 85653)
+       * gentoolkit: Fix package.size() to report correct size for symbolic
+       links (Bug 90384)
+       * equery: Fix equery depgraph to show all dependencies (Bug 99191)
+       * equery: Fix traceback with invalid regular expression for equery
+       list (Bug 109392)
+
+2005-11-04 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Fix equery belongs to correctly work when passed an argument
+       list of multiple files (Bug 111501)
+
+2005-11-02 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Fix to work with findutils-4.2.25 (Bug 111203)
+
+2005-10-18 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Make equery look at both DEPEND and RDEPEND for dependencies
+       * gentoolkit: Fix _parse_deps to understand || syntax (Bug 101377)
+
+2005-10-14 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Add qpkg --dups functionality to equery list command (bug
+       109156)
+
+2005-10-13 Paul Varner <fuzzyray@gentoo.org>
+       * equery: equery depgraph shows USE flags (Bug 74554)
+       * equery: equery should properly parse use.local.desc (Bug 74569)
+       * equery: equery list escapes regular expressions (Bug 77113)
+       * equery: equery uses displays flags correctly (Bug 86633)
+       * equery: equery -N option to disable pipe detection (Bug 90046)
+       * equery: equery list properly detects version string (Bug 91286)
+       * equery: equery belongs now requires a filename (Bug 94618)
+       * equery: equery files over a pipe only prints file names (Bug 100148)
+       * revdep-rebuild: Fix typo in man page (Bug 109147)
+
+2005-09-25 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Update to read configuration files from
+       /etc/revdep-rebuild
+
+2005-09-23 Paul Varner <fuzzyray@gentoo.org>
+       * equery: Sort output from equery list (bug 67152)
+       * equery: Update man page (Bugs 73893, 74944)
+       * equery: equery which returns best-visible ebuild (bug 78687)
+       * equery: equery --quiet is actually quiet (bug 78921)
+       * equery: Fixed typo in equery -h (bug 82352)
+       * gentoolkit: gentoolkit now uses a single portage.config object (bug
+       90680)
+       * equery: equery uses returns unique, sorted list (bug 91623)
+       * equery: equery always honors nocolor flag and settings (bug 98634)
+
+2005-09-08 Paul Varner <fuzzyray@gentoo.org>
+       * eclean: Inital commit of eclean 0.4.1 from Thomas de Grenier de
+       Latour (tgl) <degrenier@easyconnect.fr> (bug 33877)
+
+2005-06-28 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Revert fix for bug 93574 as it can cause packages to
+       be missed. (bug 97171)
+
+2005-06-07 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Delete temporary files if the environment does not 
+       match the previous environment (bug 95274)
+
+2005-06-05 Paul Varner <fuzzyray@gentoo.org>
+       * revdep-rebuild: Imported revdep-rebuild release from bug 62644
+       * revdep-rebuild: Major changes to the functionality when using 
+       --package-names/-X The script should now update slotted packages 
+       correctly. (bug 22161)
+       * revdep-rebuild: Customizable searching controlled through environment
+       variables.  This removes the need for end users to directly modify the
+       script. (bugs 32276, 38011, 59803)
+       * revdep-rebuild: The directories to search are no longer hard coded
+       into the script.  revdep-rebuild now determines the directories to
+       search based upon /etc/profile.env and /etc/ld.so.conf. (bugs 32276,
+       38011, 89781)
+       * revdep-rebuild: --ignore option to ignore temporary files left from
+       previous runs. Automatically ignore temporary files older than 24 hours.
+       (bug 34052)
+       * revdep-rebuild: Always return an exit status based upon success or
+       failure. (bug 38472)
+       * revdep-rebuild: Fixed to only emerge packages with direct missing
+       dependencies. (bug 38487)
+       * revdep-rebuild: New man page. (bug 40042)
+       * revdep-rebuild: emerge is no longer called with --nodeps. This allows
+       for needed dependencies to be pulled in. (bug 62893)
+       * revdep-rebuild: Cleaned up grammatical errors (bug 85278)
+       * revdep-rebuild: Added support for revdep-rebuild --soname 
+       /path/to/library.so (bug 91503)
+       * revdep-rebuild: Removed symbolically linked directories from search
+       (bug 93574)
+       * revdep-rebuild: --nocolor option to turn off colored output, the
+       script also obeys the NOCOLOR setting from /etc/make.conf.
+       * revdep-rebuild: Removed dependency on qpkg
+       * revdep-rebuild: Script uses PORTAGE_NICENESS from /etc/make.conf
+       * revdep-rebuild: Undocumented --keep-temp option.  This is primarily
+       for debugging/testing. This option prevents temporary files from being
+       deleted.
+       * revdep-rebuild: Changed --soname --soname-regexp options to --library
+       and treat all arguments as basic regular expressions. --soname and
+       --soname-regexp can still be used as options for backwards
+       compatability.
+       * revdep-rebuild: Removed requirement to keep revdep-rebuild and emerge
+       options distinct. Options that are unrecognized by revdep-rebuild are
+       passed directly to emerge.
+
+2005-04-30 Marius Mauch <genone@gentoo.org>
+       * glsa-check: add V to short option list so it actually works
+       * equery: added new option --name-only to belongs command to make it
+       "emerge-compatible"
+       
+2005-04-26 Marius Mauch <genone@gentoo.org>
+       * gentoolkit: fix broken Makefile
+       * gentoolkit: add some sticky tape to get the stupid thing working again
+       * equery: fix a few minor problems
+
+2005-04-25 Marius Mauch <genone@gentoo.org>
+       * qpkg: moving to /usr/share/doc/gentoolkit-*/deprecated
+       * etcat: moving to /usr/share/doc/gentoolkit-*/deprecated
+       * revdep-rebuild: replacing qpkg call with equivalent grep/sed call
+
+2005-04-07 Marius Mauch <genone@gentoo.org>
+       * euse: fixed bugs 74344, 75525 and 84521
+       * euse: add better support for cascaded profiles
+       * glsa-check: use --oneshot (bug 79819)
+       * glsa.py: fix stupid revision comparison bug (bug 75233)
+
+2005-03-12 Aron Griffis <agriffis@gentoo.org>
+       * Added eviewcvs to -dev, utility for generating viewcvs URLs
+
+2005-03-01 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Dropped epkgmove from the -dev
+       * Released gentoolkit-dev-0.2.3
+
+2005-03-01 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Released gentookit-dev-0.2.2
+
+2004-12-09 Marius Mauch <genone@gentoo.org>
+       * glsa.py: Another stupid bug, this time revisionMatch() broke as
+       ~foobar-rN isn't valid anymore
+
+2004-12-08 Marius Mauch <genone@gentoo.org>
+       * equery: implemented the --category option
+       * glsa-check: fixed the bug where it wanted to unnecessary merge masked
+       packages
+       * glsa-check: added a check to verify that all non-option arguments are
+       valid GLSAs
+       * glsa.py: changed the outfile parameter in Glsa.dump() to outstream so
+       we don't have to open/close a file which breaks pipes
+       * glsa.py: checks now for python versions below 2.3 and throws an
+       exception
+
+2004-11-29 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * branched v0-3-0: major rework in equery is in progess. the main
+       branch is reserved for minor and incremental fixups.
+
+2004-10-20 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * release.sh: New script that automates the relase of a new gentoolkit
+       relase. Only works for gentoolkit-dev at the moment.
+       * src/echangelog/Makefile: Fixed spurious '}'
+       * Released gentoolkit-dev-0.2.1
+
+2004-10-31 Marius Mauch <genone@gentoo.org>
+       * qpkg: security fix for bug #68846
+
+2004-10-20 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * etcat: fixed get_use_vars to get_use_flags, fixes #67349.
+
+2004-10-18 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: collapsed ChangeLog into base ChangeLog
+       * gentoolkit: reverted indenting back to tabs, due to loud protests
+       from Marius;)
+       * equery: collapsed ChangeLog into base ChangeLog
+       * equery: reverted indenting back to tabs, due to loud protests from
+       Marius;)
+       * equery: minor syntactical cleanups.
+       * equery: minor documentation improvements
+       * equery: added errors module that will hold various types of internal
+       errors raised.
+       * equery: added try block around on md5sum check, which fails on various
+       conditions like insufficient permission or stale temporary checksum
+       files.
+
+2004-10-17 Marius Mauch <genone@gentoo.org>
+       * equery: fix for bug #67473 (checking md5sums of prelinked binaries)
+       * equery: fix for bug #67275 (--nocolor didn't work as configure was
+       called before parsing the arguments
+       * equery: changed defaults for `equery depends` as making a depgraph for
+       the full portage tree isn't a good idea and find_all_packages() uses way
+       to much memory currently
+       * euse: replaced the old perl version with a newly written bash version.
+
+2004-10-12 Marius Mauch <genone@gentoo.org>
+       * equery: fix for bug #67210
+
+2004-10-10 Marius Mauch <genone@gentoo.org>
+       * Removed old-scripts directory from gentoolkit
+       * euse: added a errormessage that it doesn't support cascading profiles
+       * equery: small bugfixes
+       * equery: performance speedup for `equery belongs` by using portage
+       directly
+       * equery: added MD5 verification to `equery check`
+       * equery: renamed 'hasuses' to 'hasuse'
+       * equery: added filter patch for `equery files` from bug 43422, thanks
+       to degrenier@easyconnect.fr
+       * Released gentoolkit-0.2.0_pre10
+
+2004-10-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added unit tests for all supported commands
+       * equery: Fixed printing order and recognition of overlay, #53432.
+
+2004-10-11 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Split gentoolkit.py into helpers.py and package.py
+
+2004-10-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Fixed Makefiles to work with posix-compatible shells
+       * gentoolkit: Fixed is_overlay() to report properly, #53432.
+
+2004-10-06 Marius Mauch <genone@gentoo.org>
+       * glsa.py: Convert Unicode strings to ascii before passing them to
+       portage
+       * glsa.py: Some formatting fixes for dump()
+       * glsa.py: changed the matching routines so the reports are hopefully
+       more accurate
+       * glsa-check: added color support 
+       * glsa-check: added a --verbose option to show the warnings about
+       invalid GLSAs
+
+2004-09-30 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added unit tests for --help
+       * equery: Added unit tests for 'files'
+
+2004-09-27 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Added find_installed_packages
+       * equery: Added short commands
+       * equery: Fixed copyright dates
+       * equery: Fixed belongs to search only installed packages
+       * equery: Fixed reporting bug in uses command
+       * equery: Fixed reference to cppv
+       * equery: Added import of die
+       * equery: Added searching header to uses
+       * equery: Fixed hasuses to report properly
+       * Released gentoolkit-0.2.0_pre9
+       * Released gentoolkit-dev-0.2.0_pre4
+
+2004-09-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added man page rewrites by Katerina Barone-Adesi
+       <katerinab@gmail.com>, fixes #63045
+       * equery: Fixed spacing issues with files, fixes #63036.
+       * equery: Added depends command by Olivier Crete <tester@gentoo.org>,
+       fixes #40830.
+       * equery: Reworked output yet again.
+       * equery: Belongs handles multiple files on the command line, partially
+       fixes #62361.
+       * gentoolkit: Reworked printing functions
+
+2004-08-29 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Added printing functions
+       * equery: Added check for bad regexp in belongs, fixes #58494
+       * equery: Added proper error reporting to stderr, fixes #57580
+
+2004-08-22 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * gentoolkit: Fixed Package.get_env_var to use the correct tree db.
+       * gentoolkit: Renamed Package.get_use_vars to Package.get_use_flags
+       * equery: Searches now include masked packages, when installed.
+       * equery: Fixed output to be piping-friendly
+       * equery: Added -N option to force non-piping output
+       * equery: Added hasuses command
+
+2004-08-01 Marius Mauch <genone@gentoo.org>
+       * Fixed grep expression for `qpkg -f`
+
+2004-05-04 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added a -f/--full-regex option to belongs and some logic so
+       users can do belongs ant, belongs /usr/bin/ant and belongs -f ".*ant.*"
+       while getting sensible results. Fixes #37637.
+
+2004-04-14 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Released gentoolkit-dev-0.2.0_pre3
+
+2004-03-31 Marius Mauch <genone@gentoo.org>
+       * glsa-check: updates, fixing #45528 and #45522, adding support for rXX
+       operators and passing filenames as arguments to Glsa()
+
+2004-03-13 Marius Mauch <genone@gentoo.org>
+       * Added glsa-check and glsa.py, please note:
+               - they are only temporary for testing, so no manpage/ChangeLog
+               - their CVS home is in gentoo-projects
+       * fixed bugs #42160, #40935, #43389
+       * equery: fixing descriptions for local USE flags
+       * equery: more checking on exceptions
+
+2004-02-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added ebump
+       * Added gensync
+       * Added epkgmove, closes #36663.
+
+2004-02-06 Marius Mauch <genone@gentoo.org>
+       * fixed bugs #40159, #39798, #39652, #39596, #39293
+       * changed etcat and equery behavior for ambigous package names,
+               they now return values for all matching packages rather
+               than erroring out
+       * added "will be phased out" messages to etcat and qpkg
+
+2004-01-23 Marius Mauch <genone@gentoo.org>
+       * lots of bugfixes
+       * equery: now catches Exceptions thrown by portage
+       * equery: minor bugfixes
+
+2004-01-14 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added src/ego to gentoolkit-dev
+
+2004-01-12 Marius Mauch <genone@gentoo.org>
+       * equery: added mask, keyword and slot information to list command
+       * equery: fixed traceback in equery
+       * equery: added more information on "equery list"
+
+2004-01-10 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added src/ego
+
+2004-01-07 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added Makefile
+       * Added new build system
+       * Added src/old-scripts, the scripts from app-portage/gentoolkit
+       * Renamed gentool to equery
+       * Released 0.2.0_pre1
+
+2003-12-31 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added which command
+       * equery: Added check command (not finished)
+
+2003-12-12 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added size command
+       * equery: Added depgraph command
+
+2003-12-11 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added list command
+       * equery: Added uses command
+
+2003-12-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Removed emerge-rsync, emerge-webrsync
+       * Added moo
+       * Added skeleton man pages to all packages
+       * Added Makefile
+
+2003-10-05 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * equery: Added files command
+       * equery: Added belongs command
+
+2003-10-04 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Rewrote dep-clean to python
+
+2003-06-31 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Rewrote more of etcat; many of the functions now employ
+               gentoolkit.
+       * Replaced qpkg with stubs of a reimplementation in python.
+
+2003-06-27 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added patch to echangelog to consider PORTDIR, fixes #23881.
+
+2003-06-26 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Added echangelog, by agenkin
+       * Added ekeyword, by agenkin
+       * Added gentoolkit, the common python library for all Gentoolkit tools.
+       * Revived pkg-size as a testbed for the gentoolkit library
+       * Fixed some minor issues in qpkg
+       * Added revdep-rebuild-1
+       * Added revdep-rebuild-2
+       * Restructuring etcat
+       * Fixed some minor issues in dep-clean
+
+2002-11-21 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Renamed pkg-size to gentool-package-size
+       * Renamed pst-package-count to gentool-package-count
+       * Retired rest of pst-* stuff
+
+2002-08-06 Karl Trygve Kalleberg <karltk@gentoo.org>
+       * Created separate CVS module for Gentoolkit
+       * Restructured directory hierarchy
diff --git a/gentoolkit/DEVELOPING b/gentoolkit/DEVELOPING
new file mode 100644 (file)
index 0000000..e8b7ab3
--- /dev/null
@@ -0,0 +1,161 @@
+Python Code Guidelines
+----------------------
+These are a few guidelines to stick to when modifying or adding code to
+Gentoolkit. These guidelines do not apply to pym/gentoolkit/test/*.
+
+First, read Python's PEP 8: http://www.python.org/dev/peps/pep-0008/
+Next, read Portage's DEVELOPING file.
+Next, read Portage's Docstring specs:
+      http://www.gentoo.org/proj/en/portage/doc/policies/docstring-spec.xml
+
+(Portage DEVELOPING overrides PEP 8, so we use tabs instead of spaces.)
+
+Here are a few clarifications and changes to the above:
+
+Line-Wrapping
+-------------
+- Do NOT space out to an opening paren (etc). Different tab settings make this
+  difficult to read:
+
+  BAD:
+  foo = get_foo(keyword1=default,
+                keyword2=default,
+                ...)
+
+  OK:
+  foo = get_foo(keyword1=default, keyword2=default, keyword3=default,
+      keyword4=default, ...)
+
+  OK:
+  foo = get_foo(
+      keyword1=default,
+      keyword2=default,
+      ...
+  )
+
+- Preferred line length is 80 characters with a tab length of 4 characters.
+- "The preferred way of wrapping long lines is by using Python's implied line
+  continuation inside parentheses, brackets and braces" rather than using '\'
+  (PEP 8).
+  Even better than that is pre-defining a snippet of the long line in a
+  variable. So:
+
+  BAD: (too long)
+    self._descriptions = [e.text for e in self._xml_tree.findall("longdescription")]
+
+  OK:
+    self._descriptions = \
+        [e.text for e in self._xml_tree.findall("longdescription")]
+
+  OK:
+    self._descriptions = [
+        e.text for e in self._xml_tree.findall("longdescription")
+    ]
+
+  OK: (easiest to read and test)
+    long_descriptions = self._xml_tree.findall("longdescription")
+    self._descriptions = [e.text for e in long_descriptions]
+
+Imports:
+--------
+Same as PEP 8 and Portage's DEVELOPING spec, but make sure to split python
+default libraries, Gentoo system libraries (like portage) and lastly local
+modules (gentoolkit), so:
+
+  GOOD:
+    import os
+    import re
+
+    import portage
+    from portage.versions import catpkgsplit, pkgcmp
+
+    from gentoolkit import CONFIG
+    from gentoolkit import pprinter as pp
+    ...
+
+Exceptions:
+-----------
+Always raise exceptions and catch them at the last possible moment. This allows
+API consumers to catch them and decide what to do with them. Do not print an
+error to stderr from inside a consumable. Never "print" an error string, always
+write it to stderr.
+
+Lastly, if an error is fatal (should be raised all the way up), try to make
+sure it is an errors.Gentoolkit* error. That allows API consumers to catch all
+fatal errors with: except 'gentoolkit.errors.GentoolkitException'. See
+bin/equery to see how we catch those exceptions before they hit the user.
+
+  BAD:
+    try:
+        result = tree.aux_get(str(self.cpv), [var])
+    except KeyError:
+        sys.stderr.write("aux_get returned unexpected results")
+
+  BETTER:
+    # This line raises KeyError if self.cpv not found:
+    result = tree.aux_get(str(self.cpv), [var])
+
+  BEST:
+    try:
+        result = tree.aux_get(str(self.cpv), [var])
+    except KeyError:
+        err = "aux_get returned unexpected results"
+        raise errors.GentoolkitFatalError(err)
+
+Docstrings:
+-----------
+Follow this example for any complicated function or method. Don't worry about
+docstring for private methods (like _foo) or methods like __this__. It's
+definitely OK to write docstrings for these methods as well if there's
+something tricky about them, though.
+
+  def graph_reverse_depends(...):
+      # A ONE-LINE sentence describing the function in simplest terms:
+      """Graph direct reverse dependencies for self.
+
+      # An optional second paragraph to give more detailed information.
+      # Sometimes an 'Example usage' here can be worth more than a lengthy
+      # description. Examples should be an ACTUAL interpreter session:
+      Example usage:
+          >>> from gentoolkit.dependencies import Dependencies
+          >>> ffmpeg = Dependencies('media-video/ffmpeg-0.5_p20373')
+          >>> # I only care about installed packages that depend on me:
+          ... from gentoolkit.helpers import get_installed_cpvs
+          >>> # I want to pass in a sorted list. We can pass strings or
+          ... # Package or Atom types, so I'll use Package to sort:
+          ... from gentoolkit.package import Package
+          >>> installed = sorted(Package(x) for x in get_installed_cpvs())
+          >>> deptree = ffmpeg.graph_reverse_depends(
+          ...     only_direct=False,  # Include indirect revdeps
+          ...     pkgset=installed)   # from installed pkgset
+          >>> len(deptree)
+          44
+
+      # Lastly use epydoc's special fields to document further.
+      # See: http://epydoc.sourceforge.net/fields.html
+      @type pkgset: iterable
+      @keyword pkgset: sorted pkg cpv strings or anything sublassing
+          L{gentoolkit.cpv.CPV} to use for calculate our revdep graph.
+      @type max_depth: int
+      @keyword max_depth: Maximum depth to recurse if only_direct=False.
+          -1 means no maximum depth;
+           0 is the same as only_direct=True;
+          >0 means recurse only this many times;
+      @type only_direct: bool
+      @keyword only_direct: to recurse or not to recurse
+      @type printer_fn: callable
+      @keyword printer_fn: If None, no effect. If set, it will be applied to
+          each L{gentoolkit.atom.Atom} object as it is added to the results.
+      @rtype: list
+      @return: L{gentoolkit.dependencies.Dependencies} objects
+      """
+
+Other concerns:
+---------------
+- Choose names which are full, clear words (not necessary in small loops).
+- It is NEVER necessary to prefix names with "my". It adds no useful
+  information.
+- Comment and document in simple, unambiguous and non-repetitive English.
+- When adding a TODO, FIXME or XXX comment, please date it and add your name so
+  that other devs know who to ask about the proposed change.
+- Be careful of spelling.
diff --git a/gentoolkit/MANIFEST.in b/gentoolkit/MANIFEST.in
new file mode 100644 (file)
index 0000000..8f26bea
--- /dev/null
@@ -0,0 +1,13 @@
+include AUTHORS
+include ChangeLog
+include COPYING
+include CREDITS
+include DEVELOPING
+include NEWS
+include README
+include TODO
+include setup.py
+recursive-include data *
+recursive-include bin *
+recursive-include man *
+recursive-include pym *.py *.txt
diff --git a/gentoolkit/NEWS b/gentoolkit/NEWS
new file mode 100644 (file)
index 0000000..5e349be
--- /dev/null
@@ -0,0 +1,40 @@
+News (new features/major bug fixes)
+
+gentoolkit-0.3.0
+----------------
+
+epkginfo:
+  * is now a link to equery meta and has all the features equery meta has
+
+equery:
+  * --help menus cleaned up, using notations (b)elongs instead of belongs(b).
+  * man page has been rewritten.
+  * --quiet effects more modules.
+  * 2 new modules:
+    * changes - Gentoo ChangeLog viewer, try:
+      `equery changes portage` to see entry for portage version that emerge
+        wants to install;
+      `equery changes portage --from=2.2_rc20 --to=2.2_rc30` to see all entries
+        between the specified versions.
+    * meta - Displays information available in metadata.xml and keyword info.
+      Try `equery meta boost` to list herd, maintainers, keywords, and more.
+  * Modules which are meant to run on multiple packages (check, list, size) now
+    allow category and package name globbing, (so no more need for --exact-name
+    or --category).
+
+      # Exact name matching by default:
+      $ equery l zilla
+       * Searching for zilla ...
+
+      # Use globs to fuzzy match
+      $ equery l *zilla*
+       * Searching for *zilla* ...
+      [IP-] [  ] www-client/mozilla-firefox-3.5.4:0
+
+      # Use globs to 'category filter'
+      $ equery l www-client/*
+       * Searching for * in www-client ...
+      [I--] [XX] www-client/chromium-4.0.223.5:0
+      [IP-] [  ] www-client/epiphany-2.26.3-r2:0
+      [IP-] [  ] www-client/links-2.2:2
+      [IP-] [  ] www-client/mozilla-firefox-3.5.4:0
diff --git a/gentoolkit/README b/gentoolkit/README
new file mode 100644 (file)
index 0000000..5c785d3
--- /dev/null
@@ -0,0 +1,46 @@
+Package: gentoolkit/gentoolkit-dev
+Authors: Aron Griffis <agriffis@gentoo.org>
+         Brandon Low <lostlogic@gentoo.org>
+         Ian Leitch <port001@gentoo.org>
+         Karl Trygve Kalleberg <karltk@gentoo.org>
+         Marius Mauch <genone@gentoo.org>
+         Paul Varner <fuzzyray@gentoo.org>
+         See src/<tool>/AUTHORS for tool-specific authors
+
+MOTIVATION
+
+The gentoolkit and gentoolkit-dev packages contain a collection of useful
+administration scripts particular to the Gentoo Linux distribution. It contains
+rough drafts and implementations of features that may in time make it into
+Portage, or into full-fledged tools in their own right.
+
+The gentoolkit-dev package is intended primarily for Gentoo developers.
+
+CONTENTS
+
+gentoolkit
+==========
+eclean         - tool to clean up outdated distfiles and packages
+equery         - replacement for etcat and qpkg
+etcat          - extracts auxillary information from portage (deprecated)
+euse           - tool to manage USE flags
+glsa-check     - tool to manage GLSA's (Gentoo Linux Security Advisory)
+qpkg           - convient package query tool (deprecated)
+revdep-rebuild - scans/fixes broken shared libs and binaries
+
+gentoolkit-dev
+==============
+ebump          - Ebuild revision bumper
+echangelog     - update portage ChangeLogs
+ego            - 
+ekeyword       - modify package KEYWORDS
+epkgmove       - tool for moving and renaming packages in CVS
+eviewcvs       - generate viewcvs URLs
+gensync        - Overlay Sync Tool
+
+IMPROVEMENTS
+
+Any suggestions for improvements should be sent to tools-portage@gentoo.org, or
+added as a bug assigned to us.
+
+We only accept new contributions if they are written in bash or python.
diff --git a/gentoolkit/README.dev b/gentoolkit/README.dev
new file mode 100644 (file)
index 0000000..feefe47
--- /dev/null
@@ -0,0 +1,40 @@
+Adding or modifying code:
+=========================
+- If you add new code, best practice is to write a test for it.
+- If you're modifying code that doesn't have a test and you can write a test
+  for it, please do.
+- Before committing your changes to a python file, please make sure it passes
+  pylint with:
+pylint --rcfile=pylintrc yourfile.py
+- If pylint raises a warning or error that you don't agree with, it's probably
+  better to just change your code. If you're sure you have a good reason for
+  doing what you're doing, you can add an exception to our pylintrc.
+
+Creating a release:
+===================
+Note: We are using VERSION="0.3.0" simply as an example.
+
+- Run Gentoolkit's test suite, make sure it passes:
+Note: requires dev-python/snakeoil
+./setup.py test
+
+- Create a tag for the release
+svn copy svn+ssh://<dev>@svn.gentoo.org/var/svnroot/gentoolkit/trunk \
+         svn+ssh://<dev>@svn.gentoo.org/var/svnroot/gentoolkit/tags/gentoolkit-0.3.0 \
+        -m "Tagging the <VERSION> release of gentoolkit."
+svn update to pull the tag from subversion
+cd to the local tags/gentoolkit-0.3.0 directory
+
+- Set the version of the release:
+VERSION="0.3.0" ./setup.py set_version
+
+- Create a source distribution (you need to add VERSION here, too):
+VERSION="0.3.0" ./setup.py sdist
+Transfer dist/gentoolkit-0.3.0.tar.gz to dev.gentoo.org:/space/distfiles-local
+
+- Reset svn version to 'svn' (default):
+./setup.py set_version
+
+- Clean up temporary files:
+./setup.py clean -a
+
diff --git a/gentoolkit/THANKS b/gentoolkit/THANKS
new file mode 100644 (file)
index 0000000..d958556
--- /dev/null
@@ -0,0 +1,8 @@
+* eclean
+  The starting point ideas were found here:
+  http://forums.gentoo.org/viewtopic.php?t=3011
+  
+  Thanks to eswanson and far for their contributions, and to wolf31o2 for his
+  support.  Thanks also to karltk, some of this code was at some point inspired
+  by his "equery" tool.  And thanks to people who had a look on bug #33877: 
+  Benjamin Braatz, fuzzyray, genone, etc.
diff --git a/gentoolkit/TODO b/gentoolkit/TODO
new file mode 100644 (file)
index 0000000..161546b
--- /dev/null
@@ -0,0 +1,60 @@
+- equery:
+ - add --overlay, --portdir to uses
+ - 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
+ - bump versioned files in filesdir
+ - -m for changelog entry
+ - use ~/.gentoo/gentoolkit/ebump.conf
+ - use /etc/gentoolkit/ebump.conf
+
+equery:
+       Tests:
+               +helpers2 (FileOwner._extend_realpaths test probably doesn't clean up)
+       Run pylint and write test to run pylint
+       Write test to compile all modules (full syntax check). Take from portage.
+       Add more --debug stuff
+       Write tests for Dependencies._parser
+       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)
diff --git a/gentoolkit/bin/eclean b/gentoolkit/bin/eclean
new file mode 100755 (executable)
index 0000000..6fee381
--- /dev/null
@@ -0,0 +1,834 @@
+#!/usr/bin/python
+# Copyright 2003-2005 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+from __future__ import with_statement
+
+###############################################################################
+# Meta:
+__author__ = "Thomas de Grenier de Latour (tgl)"
+__email__ = "degrenier@easyconnect.fr"
+__version__ = "svn"
+__productname__ = "eclean"
+__description__ = "A cleaning tool for Gentoo distfiles and binaries."
+
+
+###############################################################################
+# Python imports:
+
+import sys
+import os, stat
+import re
+import time
+import getopt
+import fpformat
+import signal
+
+import portage
+from portage.output import *
+
+listdir = portage.listdir
+
+###############################################################################
+# Misc. shortcuts to some portage stuff:
+port_settings = portage.settings
+distdir = port_settings["DISTDIR"]
+pkgdir = port_settings["PKGDIR"]
+
+
+###############################################################################
+# printVersion:
+def printVersion():
+       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"
+
+
+###############################################################################
+# 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 >>out, "Time specification should be an integer followed by a"+ \
+                               " single letter unit."
+               print >>out, "Available units are: y (years), m (months), w (weeks), "+ \
+                               "d (days) and h (hours)."
+               print >>out, "For instance: \"1y\" is \"one year\", \"2w\" is \"two"+ \
+                               " weeks\", etc. "
+               return
+       if error == 'size':
+               eerror("Wrong size specification")
+               print >>out, "Size specification should be an integer followed by a"+ \
+                               " single letter unit."
+               print >>out, "Available units are: G, M, K and B."
+               print >>out, "For instance: \"10M\" is \"ten megabytes\", \"200K\" "+ \
+                               "is \"two hundreds kilobytes\", etc."
+               return
+       if error in ('global-options', 'packages-options', 'distfiles-options', \
+                       'merged-packages-options', 'merged-distfiles-options',):
+               eerror("Wrong option on command line.")
+               print >>out
+       elif error == 'actions':
+               eerror("Wrong or missing action name on command line.")
+               print >>out
+       print >>out, white("Usage:")
+       if error in ('actions','global-options', 'packages-options', \
+       'distfiles-options') or help == 'all':
+               print >>out, " "+turquoise(__productname__), \
+                       yellow("[global-option] ..."), \
+                       green("<action>"), \
+                       yellow("[action-option] ...")
+       if error == 'merged-distfiles-options' or help in ('all','distfiles'):
+               print >>out, " "+turquoise(__productname__+'-dist'), \
+                       yellow("[global-option, distfiles-option] ...")
+       if error == 'merged-packages-options' or help in ('all','packages'):
+               print >>out, " "+turquoise(__productname__+'-pkg'), \
+                       yellow("[global-option, packages-option] ...")
+       if error in ('global-options', 'actions'):
+               print >>out, " "+turquoise(__productname__), \
+                       yellow("[--help, --version]")
+       if help == 'all':
+               print >>out, " "+turquoise(__productname__+"(-dist,-pkg)"), \
+                       yellow("[--help, --version]")
+       if error == 'merged-packages-options' or help == 'packages':
+               print >>out, " "+turquoise(__productname__+'-pkg'), \
+                       yellow("[--help, --version]")
+       if error == 'merged-distfiles-options' or help == 'distfiles':
+               print >>out, " "+turquoise(__productname__+'-dist'), \
+                       yellow("[--help, --version]")
+       print >>out
+       if error in ('global-options', 'merged-packages-options', \
+       'merged-distfiles-options') or help:
+               print >>out, "Available global", yellow("options")+":"
+               print >>out, yellow(" -C, --nocolor")+ \
+                       "             - turn off colors on output"
+               print >>out, yellow(" -d, --destructive")+ \
+                       "         - only keep the minimum for a reinstallation"
+               print >>out, yellow(" -e, --exclude-file=<path>")+ \
+                       " - path to the exclusion file"
+               print >>out, yellow(" -i, --interactive")+ \
+                       "         - ask confirmation before deletions"
+               print >>out, yellow(" -n, --package-names")+ \
+                       "       - protect all versions (when --destructive)"
+               print >>out, yellow(" -p, --pretend")+ \
+                       "             - only display what would be cleaned"
+               print >>out, yellow(" -q, --quiet")+ \
+                       "               - be as quiet as possible"
+               print >>out, yellow(" -t, --time-limit=<time>")+ \
+                       "   - don't delete files modified since "+yellow("<time>")
+               print >>out, "   "+yellow("<time>"), "is a duration: \"1y\" is"+ \
+                               " \"one year\", \"2w\" is \"two weeks\", etc. "
+               print >>out, "   "+"Units are: y (years), m (months), w (weeks), "+ \
+                               "d (days) and h (hours)."
+               print >>out, yellow(" -h, --help")+ \
+                       "                - display the help screen"
+               print >>out, yellow(" -V, --version")+ \
+                       "             - display version info"
+               print >>out
+       if error == 'actions' or help == 'all':
+               print >>out, "Available", green("actions")+":"
+               print >>out, green(" packages")+ \
+                       "      - clean outdated binary packages from:"
+               print >>out, "                  ",teal(pkgdir)
+               print >>out, green(" distfiles")+ \
+                       "     - clean outdated packages sources files from:"
+               print >>out, "                  ",teal(distdir)
+               print >>out
+       if error in ('packages-options','merged-packages-options') \
+       or help in ('all','packages'):
+               print >>out, "Available", yellow("options"),"for the", \
+                               green("packages"),"action:"
+               print >>out, yellow(" NONE  :)")
+               print >>out
+       if error in ('distfiles-options', 'merged-distfiles-options') \
+       or help in ('all','distfiles'):
+               print >>out, "Available", yellow("options"),"for the", \
+                               green("distfiles"),"action:"
+               print >>out, yellow(" -f, --fetch-restricted")+ \
+                       "   - protect fetch-restricted files (when --destructive)"
+               print >>out, yellow(" -s, --size-limit=<size>")+ \
+                       "  - don't delete distfiles bigger than "+yellow("<size>")
+               print >>out, "   "+yellow("<size>"), "is a size specification: "+ \
+                               "\"10M\" is \"ten megabytes\", \"200K\" is"
+               print >>out, "   "+"\"two hundreds kilobytes\", etc.  Units are: "+ \
+                               "G, M, K and B."
+               print >>out
+       print >>out, "More detailed instruction can be found in", \
+                       turquoise("`man %s`" % __productname__)
+
+
+###############################################################################
+# 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 >>sys.stderr,prefix,message
+
+
+###############################################################################
+# 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"]
+       approx = 0
+       while len(units) and size >= 1000:
+               approx = 1
+               size = size / 1024.
+               units.pop()
+       sizestr = fpformat.fix(size,approx)+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 = raw_input()
+       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
+
+###############################################################################
+# 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 os.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 = 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 len(clean_dict.keys()):
+               # 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, 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, 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)
+
+
+###############################################################################
+# actually call main() if launched as a script
+if __name__ == "__main__":
+       try: main()
+       except KeyboardInterrupt:
+               print "Aborted."
+               sys.exit(130)
+       sys.exit(0)
+
diff --git a/gentoolkit/bin/epkginfo b/gentoolkit/bin/epkginfo
new file mode 100755 (executable)
index 0000000..650a870
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+#
+# Copyright 2009 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2 or later
+#
+# $Header$
+
+"""Shortcut to equery meta"""
+
+__authors__ = (
+       'Douglas Anderson <douglasjanderson@gmail.com>: equery meta',
+       'Ned Ludd <solar@gentoo.org>: first full implimentation'
+       'Eldad Zack <eldad@gentoo.org>: earch',
+       'Eric Olinger <EvvL AT RustedHalo DOT net>: metadata'
+       )
+
+import sys
+
+from gentoolkit import equery, errors
+from gentoolkit.equery import mod_usage
+from gentoolkit.equery.meta import main, print_help
+
+def print_epkginfo_help():
+       print mod_usage(mod_name="epkginfo")
+       print
+       print_help(with_usage=False)
+
+equery.initialize_configuration()
+args = sys.argv[1:]
+if not args or set(('-h', '--help')).intersection(args):
+       print_epkginfo_help()
+else:
+       try:
+               main(args)
+       except errors.GentoolkitException, err:
+               from gentoolkit import pprinter as pp
+               pp.die(1, str(err))
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/bin/equery b/gentoolkit/bin/equery
new file mode 100755 (executable)
index 0000000..e7bb6ce
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+#
+# Copyright 2002-2009 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2 or later
+#
+# $Header$
+
+"""equery is a flexible utility for Gentoo linux which can display various
+information about packages, such as the files they own, their USE flags,
+the MD5 sum of each file owned by a given package, and many other things.
+"""
+
+import sys
+# This block ensures that ^C interrupts are handled quietly.
+try:
+       import signal
+
+       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)
+
+except KeyboardInterrupt:
+       print
+       sys.exit(1)
+
+from gentoolkit import equery, errors
+
+try:
+       equery.main()
+except errors.GentoolkitException, err:
+       if '--debug' in sys.argv:
+               raise
+       else:
+               from gentoolkit import pprinter as pp
+               sys.stderr.write(pp.error(str(err)))
+               print
+               print "Add '--debug' to global options for traceback."
+               sys.exit(1)
diff --git a/gentoolkit/bin/eread b/gentoolkit/bin/eread
new file mode 100755 (executable)
index 0000000..c6d4de1
--- /dev/null
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+# This is a script to read portage log items from einfo, ewarn etc, new in the
+# portage-2.1 series.
+#
+# Author: Donnie Berkholz <spyderous@gentoo.org>
+# Updated by: Uwe Klosa <uwe.klosa@gmail.com>
+
+# set decent PATH for bug 172969
+
+PATH=/usr/bin:/bin:${PATH}
+
+# Set ELOGDIR
+PORT_LOGDIR="$(portageq envvar PORT_LOGDIR)"
+[ "$PORT_LOGDIR" = "" ] && PORT_LOGDIR="/var/log/portage"
+ELOGDIR="$PORT_LOGDIR/elog"
+
+# Verify that ELOGDIR exists
+if [ ! -d "$ELOGDIR" ]; then
+       echo "ELOG directory: $ELOGDIR does not exist!"
+       exit 1
+fi
+
+# Use the pager from the users environment
+[ -z "$PAGER" ] && PAGER="less"
+
+# Set up select prompt
+PS3="Choice? "
+
+select_loop() {
+       ANY_FILES=$(find . -type f)
+       ANY_FILES=$(echo ${ANY_FILES} | sed -e "s:\./::g")
+
+       if [[ -z ${ANY_FILES} ]]; then
+               echo "No log items to read"
+               break
+       fi
+
+       echo
+       echo "This is a list of portage log items. Choose a number to view that file or type q to quit."
+       echo
+
+       # Pick which file to read
+       select FILE in ${ANY_FILES}; do
+               case ${REPLY} in
+                       q)
+                               echo "Quitting"
+                               QUIT="yes"
+                               break
+                               ;;
+                       *)
+                               if [ -f "$FILE" ]; then
+                                       ${PAGER} ${FILE}
+                                       read -p "Delete file? [y/N] " DELETE
+                                       case ${DELETE} in
+                                               q)
+                                                       echo "Quitting"
+                                                       QUIT="yes"
+                                                       break
+                                                       ;;
+                                               y|Y)
+                                                       rm -f ${FILE}
+                                                       SUCCESS=$?
+                                                       if [[ ${SUCCESS} = 0 ]]; then
+                                                               echo "Deleted ${FILE}"
+                                                       else
+                                                               echo "Unable to delete ${FILE}"
+                                                       fi
+                                                       ;;
+                                               # Empty string defaults to N (save file)
+                                               n|N|"")
+                                                       echo "Saving ${FILE}"
+                                                       ;;
+                                               *)
+                                                       echo "Invalid response. Saving ${FILE}"
+                                                       ;;
+                                       esac
+                               else
+                                       echo
+                                       echo "Invalid response."
+                               fi
+                               ;;
+               esac
+               break
+       done
+}
+
+pushd ${ELOGDIR} > /dev/null
+
+until [[ -n ${QUIT} ]]; do
+       select_loop
+done
+
+popd > /dev/null
diff --git a/gentoolkit/bin/euse b/gentoolkit/bin/euse
new file mode 100755 (executable)
index 0000000..5950888
--- /dev/null
@@ -0,0 +1,551 @@
+#!/bin/bash
+
+# $Header$
+
+# bash replacement for the original euse by Arun Bhanu
+# Author: Marius Mauch <genone@gentoo.org>
+# Licensed under the GPL v2
+
+PROGRAM_NAME=euse
+VERSION="svn"
+
+MAKE_CONF_PATH=/etc/make.conf
+MAKE_GLOBALS_PATH=/etc/make.globals
+MAKE_PROFILE_PATH=/etc/make.profile
+MAKE_CONF_BACKUP_PATH=/etc/make.conf.euse_backup
+
+[ -z "${MODE}" ] && MODE="showhelp"            # available operation modes: showhelp, showversion, showdesc, showflags, modify
+
+parse_arguments() {
+       if [ -z "${1}" ]; then
+               return
+       fi
+       while [ -n "${1}" ]; do
+               case "${1}" in
+                       -h | --help)           MODE="showhelp";;
+                       -V | -v | --version)   MODE="showversion";;
+                       -i | --info)           MODE="showdesc";;
+                       -I | --info-installed) MODE="showinstdesc";;
+                       -l | --local)          SCOPE="local";;
+                       -g | --global)         SCOPE="global";;
+                       -a | --active)         MODE="showflags";;
+                       -E | --enable)         MODE="modify"; ACTION="add";;
+                       -D | --disable)        MODE="modify"; ACTION="remove";;
+                       -P | --prune)          MODE="modify"; ACTION="prune";;
+                       -*)
+                               echo "ERROR: unknown option ${1} specified."
+                               echo
+                               MODE="showhelp"
+                               ;;
+                       "%active")
+                               get_useflags
+                               ARGUMENTS="${ARGUMENTS} ${ACTIVE_FLAGS[9]}"
+                               ;;
+                       *)
+                               ARGUMENTS="${ARGUMENTS} ${1}"
+                               ;;
+               esac
+               shift
+       done
+}
+
+error() {
+       echo "ERROR: ${1}"
+       set +f
+       exit 1
+}
+
+get_real_path() {
+       set -P
+       cd "$1"
+       pwd
+       cd "$OLDPWD"
+       set +P
+}
+
+check_sanity() {
+       # file permission tests
+       local descdir
+       local make_defaults
+
+       descdir="$(get_portdir)/profiles"
+
+       [ ! -r "${MAKE_CONF_PATH}" ] && error "${MAKE_CONF_PATH} is not readable"
+       [ ! -r "${MAKE_GLOBALS_PATH}" ] && error "${MAKE_GLOBALS_PATH} is not readable"
+       [ ! -h "${MAKE_PROFILE_PATH}" ] && error "${MAKE_PROFILE_PATH} is not a symlink"
+       [ -z "$(get_portdir)" ] && error "\$PORTDIR couldn't be determined"
+       [ ! -d "${descdir}" ] && error "${descdir} does not exist or is not a directory"
+       [ ! -r "${descdir}/use.desc" ] && error "${descdir}/use.desc is not readable"
+       [ ! -r "${descdir}/use.local.desc" ] && error "${descdir}/use.local.desc is not readable"
+       for make_defaults in $(get_all_make_defaults); do
+               [ ! -r "$make_defaults" ]  && error "$_make_defaults is not readable"
+       done
+#      [ ! -r "$(get_make_defaults)" ] && error "$(get_make_defaults) is not readable"
+       [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && error ""${MAKE_CONF_PATH}" is not writable"
+}
+
+showhelp() {
+cat << HELP
+${PROGRAM_NAME} (${VERSION})
+
+Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]
+
+Options: -h, --help           - show this message
+         -V, --version        - show version information
+         -i, --info           - show descriptions for the given useflags
+         -I, --info-installed - show descriptions for the given useflags and
+                                their current impact on the installed system
+         -g, --global         - show only global use flags (suboption)
+         -l, --local          - show only local use flags (suboption)
+         -a, --active         - show currently active useflags and their origin
+         -E, --enable         - enable the given useflags
+         -D, --disable        - disable the given useflags
+         -P, --prune          - remove all references to the given flags from
+                                make.conf to revert to default settings
+
+Notes: ${PROGRAM_NAME} currently only works for global flags defined
+       in make.globals, make.defaults or make.conf, it doesn't handle
+       use.defaults, use.mask or package.use yet (see portage(5) for details on
+       these files). It also might have issues with cascaded profiles.
+       If multiple options are specified only the last one will be used.
+HELP
+}
+
+showversion() {
+cat << VER
+${PROGRAM_NAME} (${VERSION})
+Written by Marius Mauch
+
+Copyright (C) 2004-2009 Gentoo Foundation, Inc.
+This is free software; see the source for copying conditions.
+VER
+}
+
+# remove duplicate flags from the given list in both positive and negative forms
+# (but unlike portage always keep the last value even if it's negative)
+# Otherwise the status flags could be incorrect if a flag appers multiple times in
+# one location (like make.conf).
+# Using python here as bash sucks for list handling.
+# NOTE: bash isn't actually that bad at handling lists -- sh is. This may be
+#       worth another look to avoid calling python unnecessariy. Or we could
+#       just write the whole thing in python. ;)
+reduce_incrementals() {
+       echo $@ | python -c "import sys
+r=[]
+for x in sys.stdin.read().split():
+       if x[0] == '-' and x[1:] in r:
+               r.remove(x[1:])
+               r.append(x)
+       elif x[0] != '-' and '-'+x in r:
+               r.remove('-'+x)
+               r.append(x)
+       elif x == '-*':
+               r = []
+               r.append(x)
+       elif x not in r:
+               r.append(x)
+print ' '.join(r)"
+}
+
+# the following function creates a bash array ACTIVE_FLAGS that contains the
+# global use flags, indexed by origin: 0: environment, 1: make.conf,
+# 2: make.defaults, 3: make.globals
+get_useflags() {
+       # only calculate once as calling emerge is painfully slow
+       [ -n "${USE_FLAGS_CALCULATED}" ] && return
+
+       # backup portdir so get_portdir() doesn't give false results later
+       portdir_backup="${PORTDIR}"
+
+       ACTIVE_FLAGS[0]="$(reduce_incrementals ${USE})"
+       USE=""
+       source "${MAKE_CONF_PATH}"
+       ACTIVE_FLAGS[1]="$(reduce_incrementals ${USE})"
+       USE=""
+       for x in $(get_all_make_defaults); do
+               source "${x}"
+               ACTIVE_FLAGS[2]="$(reduce_incrementals ${ACTIVE_FLAGS[2]} ${USE})"
+       done
+       USE=""
+       source "${MAKE_GLOBALS_PATH}"
+       ACTIVE_FLAGS[3]="$(reduce_incrementals ${USE})"
+
+       # restore saved env variables
+       USE="${ACTIVE_FLAGS[0]}"
+       PORTDIR="${portdir_backup}"
+
+       # get the currently active USE flags as seen by portage, this has to be after
+       # restoring USE or portage won't see the original environment
+       ACTIVE_FLAGS[9]="$(emerge --info | grep 'USE=' | cut -b 5- | sed -e 's:"::g')" #'
+       USE_FLAGS_CALCULATED=1
+}
+
+# get the list of all known USE flags by reading use.desc and/or use.local.desc
+# (depending on the value of $SCOPE)
+get_useflaglist() {
+       local descdir
+
+       descdir="$(get_portdir)/profiles"
+
+       if [ -z "${SCOPE}" -o "${SCOPE}" == "global" ]; then
+               egrep "^[^# ]+ +-" "${descdir}/use.desc" | cut -d\  -f 1
+       fi
+       if [ -z "${SCOPE}" -o "${SCOPE}" == "local" ]; then
+               egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" | cut -d: -f 2 | cut -d\  -f 1
+       fi
+}
+
+# get all make.defaults by traversing the cascaded profile directories
+get_all_make_defaults() {
+       local curdir
+       local parent
+       local rvalue
+
+       curdir="${1:-$(get_real_path ${MAKE_PROFILE_PATH})}"
+
+       [ -f "${curdir}/make.defaults" ] && rvalue="${curdir}/make.defaults ${rvalue}"
+       if [ -f "${curdir}/parent" ]; then
+               for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
+                       pdir="$(get_real_path ${curdir}/${parent})"
+                       rvalue="$(get_all_make_defaults ${pdir}) ${rvalue}"
+               done
+       fi
+
+       echo "${rvalue}"
+}
+
+# get the path to make.defaults by traversing the cascaded profile directories
+get_make_defaults() {
+       local curdir
+       local parent
+
+       curdir="${1:-$(get_real_path ${MAKE_PROFILE_PATH})}"
+
+       if [ ! -f "${curdir}/make.defaults" -a -f "${curdir}/parent" ]; then
+               for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
+                       if [ -f "$(get_make_defaults ${curdir}/${parent})" ]; then
+                               curdir="${curdir}/${parent}"
+                               break
+                       fi
+               done
+       fi
+
+       echo "${curdir}/make.defaults"
+}
+
+# little helper function to get the status of a given flag in one of the
+# ACTIVE_FLAGS elements. Arguments are 1: flag to test, 2: index of ACTIVE_FLAGS,
+# 3: echo value for positive (and as lowercase for negative) test result,
+# 4 (optional): echo value for "missing" test result, defaults to blank
+get_flagstatus_helper() {
+       if echo " ${ACTIVE_FLAGS[${2}]} " | grep " ${1} " > /dev/null; then
+               echo -n "${3}"
+       elif echo " ${ACTIVE_FLAGS[${2}]} " | grep " -${1} " > /dev/null; then
+               echo -n "$(echo ${3} | tr [[:upper:]] [[:lower:]])"
+       else
+               echo -n "${4:- }"
+       fi
+}
+
+# prints a status string for the given flag, each column indicating the presence
+# for portage, in the environment, in make.conf, in make.defaults and in make.globals.
+# full positive value would be "[+ECDG]", full negative value would be [-ecdg],
+# full missing value would be "[-    ]" (portage only sees present or not present)
+get_flagstatus() {
+       get_useflags
+
+       echo -n '['
+       get_flagstatus_helper "${1}" 9 "+" "-"
+       get_flagstatus_helper "${1}" 0 "E"
+       get_flagstatus_helper "${1}" 1 "C"
+       get_flagstatus_helper "${1}" 2 "D"
+       get_flagstatus_helper "${1}" 3 "G"
+       echo -n '] '
+}
+
+# faster replacement to `portageq portdir`
+get_portdir() {
+       if [ -z "${PORTDIR}" ]; then
+               use_backup="${USE}"
+               source "${MAKE_GLOBALS_PATH}"
+               for x in $(get_all_make_defaults); do
+                       source "${x}"
+               done
+               source "${MAKE_CONF_PATH}"
+               USE="${use_backup}"
+       fi
+       echo "${PORTDIR}"
+}
+
+# This function takes a list of use flags and shows the status and
+# the description for each one, honoring $SCOPE
+showdesc() {
+       local descdir
+       local current_desc
+       local found_one
+       local args
+
+       args="${*:-*}"
+
+       if [ -z "${SCOPE}" ]; then
+               SCOPE="global" showdesc ${args}
+               echo
+               SCOPE="local" showdesc ${args}
+               return
+       fi
+
+       descdir="$(get_portdir)/profiles"
+
+       [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
+       [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
+       echo "************************************************************"
+
+       if [ "${args}" == "*" ]; then
+               args="$(get_useflaglist | sort -u)"
+       fi
+
+       set ${args}
+
+       foundone=0
+       while [ -n "${1}" ]; do
+               if [ "${SCOPE}" == "global" ]; then
+                       if grep "^${1}  *-" "${descdir}/use.desc" > /dev/null; then
+                               get_flagstatus "${1}"
+                               foundone=1
+                       fi
+                       grep "^${1}  *-" "${descdir}/use.desc"
+               fi
+               # local flags are a bit more complicated as there can be multiple
+               # entries per flag and we can't pipe into printf
+               if [ "${SCOPE}" == "local" ]; then
+                       if grep ":${1}  *-" "${descdir}/use.local.desc" > /dev/null; then
+                               foundone=1
+                       fi
+                       grep ":${1}  *-" "${descdir}/use.local.desc" \
+                               | sed -e "s/^\([^:]\+\):\(${1}\) *- *\(.\+\)/\1|\2|\3/g" \
+                               | while read line; do
+                                       pkg="$(echo $line | cut -d\| -f 1)"
+                                       flag="$(echo $line | cut -d\| -f 2)"
+                                       desc="$(echo $line | cut -d\| -f 3)"
+                                       get_flagstatus "${flag}"
+                                       printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc}"
+                               done
+               fi
+               shift
+       done
+
+       if [ ${foundone} == 0 ]; then
+               echo "no matching entries found"
+       fi
+}
+
+# Works like showdesc() but displays only descriptions of which the appropriate
+# ebuild is installed and prints the name of those packages.
+showinstdesc() {
+       local descdir
+       local current_desc
+       local args
+       local -i foundone=0
+       local OIFS="$IFS"
+
+       args=("${@:-*}")
+
+       case "${SCOPE}" in
+               "global") echo "global use flags (searching: ${args})";;
+                "local") echo "local use flags (searching: ${args})";;
+                      *) SCOPE="global" showinstdesc "${args[@]}"
+                         echo
+                         SCOPE="local" showinstdesc "${args[@]}"
+                         return;;
+       esac
+
+       descdir="$(get_portdir)/profiles"
+       echo "************************************************************"
+
+       if [ "${args}" = "*" ]; then
+               args="$(get_useflaglist | sort -u)"
+       fi
+
+       set "${args[@]}"
+
+       while [ -n "${1}" ]; do
+               case "${SCOPE}" in
+                       "global")
+                               if desc=$(grep "^${1}  *-" "${descdir}/use.desc"); then
+                                       get_flagstatus "${1}"
+                                       echo "$desc"
+                                       # get list of installed packages matching this USE flag.
+                                       IFS=$'\n'
+                                       packages=($(equery -q -C hasuse -i "${1}" | awk '{ print $(NF-1) }' | sort))
+                                       foundone+=${#packages[@]}
+                                       printf "\nInstalled packages matching this USE flag: "
+                                       if [ ${foundone} -gt 0 ]; then
+                                               echo $'\n'"${packages[*]}"
+                                       else
+                                               echo "none"
+                                       fi
+                               fi
+                       ;;
+                       "local")
+                               # local flags are a bit more complicated as there can be multiple
+                               # entries per flag and we can't pipe into printf
+                               IFS=': ' # Use a space instead of a dash because dashes occur in cat/pkg
+                               while read pkg flag desc; do
+                                       # print name only if package is installed
+                                       # NOTE: If we implement bug #114086 's enhancement we can just use the
+                                       #       exit status of equery instead of a subshell and pipe to wc -l
+                                       if [ $(equery -q -C list -i -e "${pkg}" | wc -l) -gt 0 ]; then
+                                               foundone=1
+                                               IFS="$OIFS"
+                                               get_flagstatus "${flag}"
+                                               IFS=': '
+                                               printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc#- }"
+                                       fi
+                               done < <(grep ":${1}  *-" "${descdir}/use.local.desc")
+                       ;;
+               esac
+               shift
+       done
+
+       if [ ${foundone} -lt 1 ]; then
+               echo "no matching entries found"
+       fi
+       IFS="$OIFS"
+}
+
+# show a list of all currently active flags and where they are activated
+showflags() {
+       local args
+
+       get_useflags
+
+       args="${*:-*}"
+
+       if [ "${args}" == "*" ]; then
+               args="$(get_useflaglist | sort -u)"
+       fi
+
+       set ${args}
+
+       while [ -n "${1}" ]; do
+               if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
+                       printf "%-20s" ${1}
+                       get_flagstatus ${1}
+                       echo
+               fi
+               shift
+       done
+}
+
+# two small helpers to add or remove a flag from a USE string
+add_flag() {
+       NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
+}
+
+remove_flag() {
+       NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
+}
+
+# USE flag modification function. Mainly a loop with calls to add_flag and
+# remove_flag to create a new USE string which is then inserted into make.conf.
+modify() {
+       if [ -z "${*}" ]; then
+               if [ "${ACTION}" != "prune" ]; then
+                       echo "WARNING: no USE flags listed for modification, do you really"
+                       echo "         want to ${ACTION} *all* known USE flags?"
+                       echo "         If you don't please press Ctrl-C NOW!!!"
+                       sleep 5
+                       set $(get_useflaglist | sort -u)
+               fi
+       fi
+
+       get_useflags
+
+       NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
+
+       while [ -n "${1}" ]; do
+               if [ "${ACTION}" == "add" ]; then
+                       if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+                               shift
+                       elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+                               remove_flag "-${1}"
+                       else
+                               add_flag "${1}"
+                               shift
+                       fi
+               elif [ "${ACTION}" == "remove" ]; then
+                       if echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+                               shift
+                       elif echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+                               remove_flag "${1}"
+                       else
+                               add_flag "-${1}"
+                               shift
+                       fi
+               elif [ "${ACTION}" == "prune" ]; then
+                       if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+                               remove_flag "${1}"
+                       elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+                               remove_flag "-${1}"
+                       fi
+                       shift
+               fi
+       done
+
+       #echo "old flags:"
+       #echo ${ACTIVE_FLAGS[1]}
+       #echo
+       #echo "new flags:"
+       #echo ${NEW_MAKE_CONF_USE}
+
+       # a little loop to add linebreaks so we don't end with one ultra-long line
+       NEW_MAKE_CONF_USE_2=""
+       for x in ${NEW_MAKE_CONF_USE}; do
+               if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
+                       NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n     $x "
+               else
+                       NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
+               fi
+       done
+
+       # make a backup just in case the user doesn't like the new make.conf
+       cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
+
+       # as sed doesn't really work with multi-line patterns we have to replace USE
+       # on our own here. Basically just skip everything between USE=" and the
+       # closing ", printing our new USE line there instead.
+       inuse=0
+       had_use=0
+       x=0
+       (while [ "$x" -eq "0" ]; do
+               read -r line
+               x="$?"
+               [[ "${x}" -ne "0" ]] && break
+               [ "${line:0:4}" == "USE=" ] && inuse=1
+               [ "${inuse}" == "0" ] && echo -E "${line}"
+               if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
+                       echo -n 'USE="'
+                       echo -ne "${NEW_MAKE_CONF_USE_2%% }"
+                       echo '"'
+                       inuse=0
+                       had_use=1
+               fi
+       done
+       if [ ${had_use} -eq 0 ]; then
+               echo -n 'USE="'
+               echo -ne "${NEW_MAKE_CONF_USE_2%% }"
+               echo '"'
+       fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
+
+       echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
+}
+
+##### main program comes now #####
+
+# disable globbing as it fucks up with args=*
+set -f
+parse_arguments "$@"
+check_sanity
+
+eval ${MODE} ${ARGUMENTS}
+set +f
diff --git a/gentoolkit/bin/glsa-check b/gentoolkit/bin/glsa-check
new file mode 100755 (executable)
index 0000000..ab83471
--- /dev/null
@@ -0,0 +1,396 @@
+#!/usr/bin/python
+
+# $Header: $
+# This program is licensed under the GPL, version 2
+
+import os
+import sys
+import codecs
+
+import portage
+from portage.output import *
+
+from getopt import getopt, GetoptError
+
+__program__ = "glsa-check"
+__author__ = "Marius Mauch <genone@gentoo.org>"
+__version__ = "svn"
+
+optionmap = [
+["-l", "--list", "list the GLSAs"],
+["-d", "--dump", "--print", "show all information about the GLSAs"],
+["-t", "--test", "test if this system is affected by the GLSAs"],
+["-p", "--pretend", "show the necessary steps to apply the GLSAs"],
+["-f", "--fix", "try to auto-apply the GLSAs (experimental)"],
+["-i", "--inject", "inject the given GLSA into the glsa_injected file"],
+["-n", "--nocolor", "disable colors (option)"],
+["-e", "--emergelike", "upgrade to latest version (not least-change, option)"],
+["-h", "--help", "show this help message"],
+["-V", "--version", "some information about this tool"],
+["-v", "--verbose", "print more information (option)"],
+["-c", "--cve", "show CVE ids in listing mode (option)"],
+["-q", "--quiet", "be less verbose and do not send empty mail (option)"],
+["-m", "--mail", "send a mail with the given GLSAs to the administrator"],
+]
+
+# print a warning as this is beta code (but proven by now, so no more warning)
+#sys.stderr.write("WARNING: This tool is completely new and not very tested, so it should not be\n")
+#sys.stderr.write("used on production systems. It's mainly a test tool for the new GLSA release\n")
+#sys.stderr.write("and distribution system, it's functionality will later be merged into emerge\n")
+#sys.stderr.write("and equery.\n")
+#sys.stderr.write("Please read http://www.gentoo.org/proj/en/portage/glsa-integration.xml\n")
+#sys.stderr.write("before using this tool AND before reporting a bug.\n\n")
+
+# option parsing
+args = []
+params = []
+try:
+       args, params = getopt(sys.argv[1:], "".join([o[0][1] for o in optionmap]), \
+               [x[2:] for x in reduce(lambda x,y: x+y, [z[1:-1] for z in optionmap])])
+       args = [a for a,b in args]
+
+       for option in ["--nocolor", "-n"]:
+               if option in args:
+                       nocolor()
+                       args.remove(option)
+
+       verbose = False
+       for option in ["--verbose", "-v"]:
+               if option in args:
+                       verbose = True
+                       args.remove(option)
+
+       list_cve = False
+       for option in ["--cve", "-c"]:
+               if option in args:
+                       list_cve = True
+                       args.remove(option)
+
+       least_change = True
+       for option in ["--emergelike", "-e"]:
+               if option in args:
+                       least_change = False
+                       args.remove(option)
+
+       quiet = False
+       for option in ["--quiet", "-q"]:
+               if option in args:
+                       quiet = True
+                       args.remove(option)
+
+
+       # sanity checking
+       if len(args) <= 0:
+               sys.stderr.write("no option given: what should I do ?\n")
+               mode = "HELP"
+       elif len(args) > 1:
+               sys.stderr.write("please use only one command per call\n")
+               mode = "HELP"
+       else:
+               # in what mode are we ?
+               args = args[0]
+               for m in optionmap:
+                       if args in [o for o in m[:-1]]:
+                               mode = m[1][2:]
+
+except GetoptError, e:
+       sys.stderr.write("unknown option given: ")
+       sys.stderr.write(str(e)+"\n")
+       mode = "HELP"
+
+# we need a set of glsa for most operation modes
+if len(params) <= 0 and mode in ["fix", "test", "pretend", "dump", "inject", "mail"]:
+       sys.stderr.write("\nno GLSA given, so we'll do nothing for now. \n")
+       sys.stderr.write("If you want to run on all GLSA please tell me so \n")
+       sys.stderr.write("(specify \"all\" as parameter)\n\n")
+       mode = "HELP"
+elif len(params) <= 0 and mode == "list":
+       params.append("affected")
+
+# show help message
+if mode == "help" or mode == "HELP":
+       msg = "Syntax: glsa-check <option> [glsa-list]\n\n"
+       for m in optionmap:
+               msg += m[0] + "\t" + m[1] + "   \t: " + m[-1] + "\n"
+               for o in m[2:-1]:
+                       msg += "\t" + o + "\n"
+       msg += "\nglsa-list can contain an arbitrary number of GLSA ids, \n"
+       msg += "filenames containing GLSAs or the special identifiers \n"
+       msg += "'all' and 'affected'\n"
+       if mode == "help":
+               sys.stdout.write(msg)
+               sys.exit(0)
+       else:
+               sys.stderr.write("\n" + msg)
+               sys.exit(1)
+
+# we need root privileges for write access
+if mode in ["fix", "inject"] and os.geteuid() != 0:
+       sys.stderr.write(__program__ + ": root access is needed for \""+mode+"\" mode\n")
+       sys.exit(2)
+
+# show version and copyright information
+if mode == "version":
+       sys.stderr.write("%(program)s (%(version)s)\n" % {
+               "program": __program__,
+               "version": __version__
+       })
+       sys.stderr.write("Author: %s\n" % __author__)
+       sys.stderr.write("This program is licensed under the GPL, version 2\n")
+       sys.exit(0)
+
+# delay this for speed increase
+from gentoolkit.glsa import *
+
+glsaconfig = checkconfig(portage.config(clone=portage.settings))
+
+if quiet:
+    glsaconfig["EMERGE_OPTS"] += " --quiet"
+
+vardb = portage.db["/"]["vartree"].dbapi
+portdb = portage.db["/"]["porttree"].dbapi
+
+# Check that we really have a glsa dir to work on
+if not (os.path.exists(glsaconfig["GLSA_DIR"]) and os.path.isdir(glsaconfig["GLSA_DIR"])):
+       sys.stderr.write(red("ERROR")+": GLSA_DIR %s doesn't exist. Please fix this.\n" % glsaconfig["GLSA_DIR"])
+       sys.exit(1)
+
+# build glsa lists
+completelist = get_glsa_list(glsaconfig["GLSA_DIR"], glsaconfig)
+
+if os.access(glsaconfig["CHECKFILE"], os.R_OK):
+       checklist = [line.strip() for line in open(glsaconfig["CHECKFILE"], "r").readlines()]
+else:
+       checklist = []
+todolist = [e for e in completelist if e not in checklist]
+
+glsalist = []
+if "new" in params:
+       params.remove("new")
+       sys.stderr.write("Warning: The 'new' glsa-list target has been removed, using 'affected'.\n")
+       params.append("affected")
+
+if "all" in params:
+       glsalist = completelist
+       params.remove("all")
+
+if "affected" in params:
+       for x in todolist:
+               try:
+                       myglsa = Glsa(x, glsaconfig)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (x, e)))
+                       continue
+               if myglsa.isVulnerable():
+                       glsalist.append(x)
+       params.remove("affected")
+
+# remove invalid parameters
+for p in params[:]:
+       if not (p in completelist or os.path.exists(p)):
+               sys.stderr.write(("(removing %s from parameter list as it isn't a valid GLSA specification)\n" % p))
+               params.remove(p)
+
+glsalist.extend([g for g in params if g not in glsalist])
+
+def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr, encoding="utf-8"):
+       fd1 = codecs.getwriter(encoding)(fd1)
+       fd2 = codecs.getwriter(encoding)(fd2)
+       if not quiet:
+               fd2.write(white("[A]")+" means this GLSA was marked as applied (injected),\n")
+               fd2.write(green("[U]")+" means the system is not affected and\n")
+               fd2.write(red("[N]")+" indicates that the system might be affected.\n\n")
+
+       myglsalist.sort()
+       for myid in myglsalist:
+               try:
+                       myglsa = Glsa(myid, glsaconfig)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               fd2.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+                       continue
+               if myglsa.isInjected():
+                       status = "[A]"
+                       color = white
+               elif myglsa.isVulnerable():
+                       status = "[N]"
+                       color = red
+               else:
+                       status = "[U]"
+                       color = green
+
+               if verbose:
+                       access = ("[%-8s] " % myglsa.access)
+               else:
+                       access=""
+
+               fd1.write(color(myglsa.nr) + " " + color(status) + " " + color(access) + myglsa.title + " (")
+               if not verbose:
+                       for pkg in myglsa.packages.keys()[:3]:
+                               fd1.write(" " + pkg + " ")
+                       if len(myglsa.packages) > 3:
+                               fd1.write("... ")
+               else:
+                       for pkg in myglsa.packages.keys():
+                               mylist = vardb.match(portage.dep_getkey(str(pkg)))
+                               if len(mylist) > 0:
+                                       pkg = color(" ".join(mylist))
+                               fd1.write(" " + pkg + " ")
+
+               fd1.write(")")
+               if list_cve:
+                       fd1.write(" "+(",".join([r[:13] for r in myglsa.references if r[:4] in ["CAN-", "CVE-"]])))
+               fd1.write("\n")
+       return 0
+
+if mode == "list":
+       sys.exit(summarylist(glsalist))
+
+# dump, fix, inject and fix are nearly the same code, only the glsa method call differs
+if mode in ["dump", "fix", "inject", "pretend"]:
+       for myid in glsalist:
+               try:
+                       myglsa = Glsa(myid, glsaconfig)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+                       continue
+               if mode == "dump":
+                       myglsa.dump()
+               elif mode == "fix":
+                       sys.stdout.write("Fixing GLSA "+myid+"\n")
+                       if not myglsa.isVulnerable():
+                               sys.stdout.write(">>> no vulnerable packages installed\n")
+                       else:
+                               mergelist = myglsa.getMergeList(least_change=least_change)
+                               if mergelist == []:
+                                       sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n")
+                                       sys.exit(2)
+                               for pkg in mergelist:
+                                       sys.stdout.write(">>> merging "+pkg+"\n")
+                                       # using emerge for the actual merging as it contains the dependency
+                                       # code and we want to be consistent in behaviour. Also this functionality
+                                       # will be integrated in emerge later, so it shouldn't hurt much.
+                                       emergecmd = "emerge --oneshot " + glsaconfig["EMERGE_OPTS"] + " =" + pkg
+                                       if verbose:
+                                               sys.stderr.write(emergecmd+"\n")
+                                       exitcode = os.system(emergecmd)
+                                       # system() returns the exitcode in the high byte of a 16bit integer
+                                       if exitcode >= 1<<8:
+                                               exitcode >>= 8
+                                       if exitcode:
+                                               sys.exit(exitcode)
+                               if len(mergelist):
+                                       sys.stdout.write("\n")
+               elif mode == "pretend":
+                       sys.stdout.write("Checking GLSA "+myid+"\n")
+                       if not myglsa.isVulnerable():
+                               sys.stdout.write(">>> no vulnerable packages installed\n")
+                       else:
+                               mergedict = {}
+                               for (vuln, update) in myglsa.getAffectionTable(least_change=least_change):
+                                       mergedict.setdefault(update, []).append(vuln)
+
+                               # first, extract the atoms that cannot be upgraded (where key == "")
+                               no_upgrades = []
+                               if "" in mergedict:
+                                       no_upgrades = mergedict[""]
+                                       del mergedict[""]
+
+                               # see if anything is left that can be upgraded
+                               if mergedict:
+                                       sys.stdout.write(">>> Updates that will be performed:\n")
+                                       for (upd, vuln) in mergedict.iteritems():
+                                               sys.stdout.write("     " + green(upd) + " (vulnerable: " + red(", ".join(vuln)) + ")\n")
+
+                               if no_upgrades:
+                                       sys.stdout.write(">>> No upgrade path exists for these packages:\n")
+                                       sys.stdout.write("     " + red(", ".join(no_upgrades)) + "\n")
+                       sys.stdout.write("\n")
+               elif mode == "inject":
+                       sys.stdout.write("injecting " + myid + "\n")
+                       myglsa.inject()
+       sys.exit(0)
+
+# test is a bit different as Glsa.test() produces no output
+if mode == "test":
+       outputlist = []
+       for myid in glsalist:
+               try:
+                       myglsa = Glsa(myid, glsaconfig)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+                       continue
+               if myglsa.isVulnerable():
+                       outputlist.append(str(myglsa.nr))
+       if len(outputlist) > 0:
+               sys.stderr.write("This system is affected by the following GLSAs:\n")
+               if verbose:
+                       summarylist(outputlist)
+               else:
+                       sys.stdout.write("\n".join(outputlist)+"\n")
+       else:
+               sys.stderr.write("This system is not affected by any of the listed GLSAs\n")
+       sys.exit(0)
+
+# mail mode as requested by solar
+if mode == "mail":
+       try:
+               import portage.mail as portage_mail
+       except ImportError:
+               import portage_mail
+
+       import socket
+       from StringIO import StringIO
+       try:
+               from email.mime.text import MIMEText
+       except ImportError:
+               from email.MIMEText import MIMEText
+
+       # color doesn't make any sense for mail
+       nocolor()
+
+       if "PORTAGE_ELOG_MAILURI" in glsaconfig:
+               myrecipient = glsaconfig["PORTAGE_ELOG_MAILURI"].split()[0]
+       else:
+               myrecipient = "root@localhost"
+
+       if "PORTAGE_ELOG_MAILFROM" in glsaconfig:
+               myfrom = glsaconfig["PORTAGE_ELOG_MAILFROM"]
+       else:
+               myfrom = "glsa-check"
+
+       mysubject = "[glsa-check] Summary for %s" % socket.getfqdn()
+
+       # need a file object for summarylist()
+       myfd = StringIO()
+       myfd.write("GLSA Summary report for host %s\n" % socket.getfqdn())
+       myfd.write("(Command was: %s)\n\n" % " ".join(sys.argv))
+       summarylist(glsalist, fd1=myfd, fd2=myfd)
+       summary = str(myfd.getvalue())
+       myfd.close()
+
+       myattachments = []
+       for myid in glsalist:
+               try:
+                       myglsa = Glsa(myid, glsaconfig)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+                       continue
+               myfd = StringIO()
+               myglsa.dump(outstream=myfd)
+               myattachments.append(MIMEText(str(myfd.getvalue()), _charset="utf8"))
+               myfd.close()
+
+        if glsalist or not quiet:
+               mymessage = portage_mail.create_message(myfrom, myrecipient, mysubject, summary, myattachments)
+               portage_mail.send_mail(glsaconfig, mymessage)
+
+       sys.exit(0)
+
+# something wrong here, all valid paths are covered with sys.exit()
+sys.stderr.write("nothing more to do\n")
+sys.exit(2)
diff --git a/gentoolkit/bin/revdep-rebuild b/gentoolkit/bin/revdep-rebuild
new file mode 100755 (executable)
index 0000000..9027a58
--- /dev/null
@@ -0,0 +1,1159 @@
+#!/bin/bash
+# Copyright 1999-2008 Gentoo Foundation
+
+# revdep-rebuild: Reverse dependency rebuilder.
+# Original Author: Stanislav Brabec
+# Rewrite Author: Michael A. Smith
+# Current Maintainer: Paul Varner <fuzzyray@gentoo.org>
+
+# TODO:
+# - Use more /etc/init.d/functions.sh
+# - Try to reduce the number of global vars
+
+##
+# Global Variables:
+
+# Must-be-blank:
+unset GREP_OPTIONS
+
+# Readonly variables:
+declare -r APP_NAME="${0##*/}" # The name of this application
+declare -r VERSION="svn"
+declare -r OIFS="$IFS"         # Save the IFS
+declare -r     ENV_FILE=0_env.rr     # Contains environment variables
+declare -r   FILES_FILE=1_files.rr   # Contains a list of files to search
+declare -r  LDPATH_FILE=2_ldpath.rr  # Contains the LDPATH
+declare -r  BROKEN_FILE=3_broken.rr  # Contains the list of broken files
+declare -r  ERRORS_FILE=3_errors.rr  # Contains the ldd error output
+declare -r     RAW_FILE=4_raw.rr     # Contains the raw list of packages
+declare -r  OWNERS_FILE=4_owners.rr  # Contains the file owners
+declare -r    PKGS_FILE=4_pkgs.rr    # Contains the unsorted bare package names
+declare -r EBUILDS_FILE=4_ebuilds.rr # Contains the unsorted atoms
+                                     # (Appropriately slotted or versioned)
+declare -r   ORDER_FILE=5_order.rr   # Contains the sorted atoms
+declare -r  STATUS_FILE=6_status.rr  # Contains the ldd error output
+declare -ra FILES=(
+       "$ENV_FILE"
+       "$FILES_FILE"
+       "$LDPATH_FILE"
+       "$BROKEN_FILE"
+       "$ERRORS_FILE"
+       "$RAW_FILE"
+       "$OWNERS_FILE"
+       "$PKGS_FILE"
+       "$EBUILDS_FILE"
+       "$ORDER_FILE"
+       "$STATUS_FILE"
+)
+
+# "Boolean" variables: Considered "true" if it has any value at all
+# "True" indicates we should...
+declare FULL_LD_PATH           # ...search across the COMPLETE_LD_LIBRARY_PATH
+declare KEEP_TEMP              # ...not delete tempfiles from the current run
+declare ORDER_PKGS             # ...sort the atoms in deep dependency order
+declare PACKAGE_NAMES          # ...emerge by slot, not by versionated atom
+declare RM_OLD_TEMPFILES       # ...remove tempfiles from prior runs
+declare SEARCH_BROKEN          # ...search for broken libraries and binaries
+declare VERBOSE                # ...give verbose output
+
+# Globals that impact portage directly:
+declare EMERGE_DEFAULT_OPTS    # String of options portage assumes to be set
+declare EMERGE_OPTIONS         # Array of options to pass to portage
+declare PORTAGE_NICENESS       # Renice to this value
+declare PORTAGE_ROOT           # The root path for portage
+
+# Customizable incremental variables:
+# These variables can be prepended to either by setting the variable in
+# your environment prior to execution, or by placing an entry in
+# /etc/make.conf.
+#
+# An entry of "-*" means to clear the variable from that point forward.
+# Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS
+# to contain only /usr/bin
+declare LD_LIBRARY_MASK  # Mask of specially evaluated libraries
+declare SEARCH_DIRS      # List of dirs to search for executables and libraries
+declare SEARCH_DIRS_MASK # List of dirs not to search
+
+# Other globals:
+declare OLDPROG                # Previous pass through the progress meter
+declare EXACT_PKG              # Versionated atom to emerge
+declare HEAD_TEXT              # Feedback string about the search
+declare NOCOLOR                # Set to "true" not to output term colors
+declare OK_TEXT                # Feedback about a search which found no errors
+declare RC_NOCOLOR             # Hack to insure we respect NOCOLOR
+declare REBUILD_LIST           # Array of atoms to emerge
+declare SKIP_LIST              # Array of atoms that cannot be emerged (masked?)
+declare SONAME                 # Soname/soname path pattern given on commandline
+declare SONAME_SEARCH          # Value of SONAME modified to match ldd's output
+declare WORKING_TEXT           # Feedback about the search
+declare WORKING_DIR            # Working directory where cache files are kept
+
+main() {
+       # preliminary setup
+       portage_settings
+       get_opts "$@"
+       setup_portage
+       setup_search_paths_and_masks
+       get_search_env
+       echo
+
+       # Search for broken binaries
+       get_files
+       get_ldpath
+       main_checks
+
+       # Associate broken binaries with packages to rebuild
+       if [[ $PACKAGE_NAMES ]]; then
+               get_packages
+               clean_packages
+               assign_packages_to_ebuilds
+       else
+               get_exact_ebuilds
+       fi
+
+       # Rebuild packages owning broken binaries
+       get_build_order
+       rebuild
+
+       # All done
+       cleanup
+}
+##
+# Refuse to delete anything before we cd to our tmpdir
+# (See mkdir_and_cd_to_tmpdir()
+rm() {
+       eerror "I was instructed to rm '$@'"
+       die 1 "Refusing to delete anything before changing to temporary directory."
+}
+: <<'EW'
+##
+# GNU find has -executable, but if our users' finds do not have that flag
+# we emulate it with this function. Also emulates -writable and -readable.
+# Usage: find PATH ARGS -- use find like normal, except use -executable instead
+# of various versions of -perm /+ blah blah and hacks
+find() {
+       hash find || { die 1 'find not found!'; }
+       # We can be pretty sure find itself should be executable.
+       local testsubject="$(type -P find)"
+       if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
+               unset -f find # We can just use the command find
+       elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
+               find() {
+                       a=(${@//-executable/-perm \/u+x})
+                       a=(${a[@]//-writable/-perm \/u+w})
+                       a=(${a[@]//-readable/-perm \/r+w})
+                       command find "${a[@]}"
+               }
+       elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
+               find() {
+                       a=(${@//-executable/-perm +u+x})
+                       a=(${a[@]//-writable/-perm +u+w})
+                       a=(${a[@]//-readable/-perm +r+w})
+                       command find "${a[@]}"
+               }
+       else # Last resort
+               find() {
+                       a=(${@//-executable/-exec test -x '{}' \; -print})
+                       a=(${a[@]//-writable/-exec test -w '{}' \; -print})
+                       a=(${a[@]//-readable/-exec test -r '{}' \; -print})
+                       command find "${a[@]}"
+               }
+       fi
+       find "$@"
+}
+EW
+
+print_usage() {
+cat << EOF
+${APP_NAME}: (${VERSION})
+
+Copyright (C) 2003-2009 Gentoo Foundation, Inc.
+This is free software; see the source for copying conditions.
+
+Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
+
+Broken reverse dependency rebuilder.
+
+  -C, --nocolor        Turn off colored output
+  -d, --debug          Print way too much information (uses bash's set -xv)
+  -e, --exact          Emerge based on exact package version
+  -h, --help           Print this usage
+  -i, --ignore         Ignore temporary files from previous runs
+  -k, --keep-temp      Do not delete temporary files on exit
+  -L, --library NAME   Emerge existing packages that use the library with NAME
+      --library=NAME   NAME can be a full path to the library or a basic
+                       regular expression (man grep)
+  -l, --no-ld-path     Do not set LD_LIBRARY_PATH
+  -o, --no-order       Do not check the build order
+                       (Saves time, but may cause breakage.)
+  -p, --pretend        Do a trial run without actually emerging anything
+                       (also passed to emerge command)
+  -P, --no-progress    Turn off the progress meter
+  -q, --quiet          Be less verbose (also passed to emerge command)
+  -v, --verbose        Be more verbose (also passed to emerge command)
+
+Calls emerge, options after -- are ignored by $APP_NAME
+and passed directly to emerge.
+
+Report bugs to <http://bugs.gentoo.org>
+
+EOF
+}
+##
+# Usage: progress i n
+#        i: current item
+#        n: total number of items to process
+progress() {
+       if [[ -t 1 ]]; then
+               progress() {
+                       local curProg=$(( $1 * 100 / $2 ))
+                       (( curProg == OLDPROG )) && return # no change, output nothing
+                       OLDPROG="$curProg" # must be a global variable
+                       (( $1 == $2 )) && local lb=$'\n'
+                       echo -ne '\r                         \r'"[ $curProg% ] $lb"
+               }
+               progress $@
+       else # STDOUT is not a tty. Disable progress meter.
+               progress() { :; }
+       fi
+}
+##
+# Usage: countdown n
+#        n: number of seconds to count
+countdown() {
+       local i
+       for ((i=1; i<$1; i++)); do
+               echo -ne '\a.'
+               ((i<$1)) && sleep 1
+       done
+       echo -e '\a.'
+}
+##
+# Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
+# (If any libs have whitespace in their filenames, someone needs punishment.)
+clean_var() {
+       gawk 'BEGIN         {RS="[[:space:]]"}
+            /-\*/         {exit}
+            /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
+}
+##
+# Exit and optionally output to sterr
+die() {
+       local status=$1
+       shift
+       eerror "$@"
+       exit $status
+}
+##
+# What to do when dynamic linking is consistent
+clean_exit() {
+       if [[ ! $KEEP_TEMP ]]; then
+               rm -f "${FILES[@]}"
+               if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
+                       # Remove the working directory
+                       builtin cd; rmdir "$WORKING_DIR"
+               fi
+       fi
+       echo
+       einfo "$OK_TEXT... All done. "
+       exit 0
+}
+##
+# Get the name of the package that owns a file or list of files given as args.
+# NOTE: depends on app-misc/realpath!
+get_file_owner() {
+       local IFS=$'\n'
+
+       rpath=$(realpath "${*}" 2>/dev/null)
+       # To ensure we always have something in rpath...
+       [[ -z $rpath ]] && rpath=${*}
+
+       # Workaround for bug 280341
+       mlib=$(echo ${*}|sed 's:/lib/:/lib64/:')
+       [[ "${*}" == "${mlib}" ]] && mlib=$(echo ${*}|sed 's:/lib64/:/lib/:')
+
+       # Add a space to the end of each object name to prevent false
+       # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
+       # The same for "${rpath} ".
+       find /var/db/pkg -type f -name CONTENTS -print0 |
+               xargs -0 grep -m 1 -Fl -e "${*} " -e "${rpath} " -e "${mlib} " |
+               sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
+}
+##
+# Normalize some EMERGE_OPTIONS
+normalize_emerge_opts() {
+       # Normalize some EMERGE_OPTIONS
+       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
+       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
+       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
+}
+##
+# Use the color preference from portage
+setup_color() {
+       # This should still work if NOCOLOR is set by the -C flag or in the user's
+       # environment.
+       [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
+       . /etc/init.d/functions.sh
+}
+##
+# Die if an argument is missing.
+die_if_missing_arg() {
+       [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
+}
+##
+# Die because an option is not recognized.
+die_invalid_option() {
+       # Can't use eerror and einfo because this gets called before function.sh
+       # is sourced
+       echo
+       echo  "Encountered unrecognized option $1." >&2
+       echo
+       echo  "$APP_NAME no longer automatically passes unrecognized options to portage."
+       echo  "Separate emerge-only options from revdep-rebuild options with the -- flag."
+       echo
+       echo  "For example, $APP_NAME -v -- --ask"
+       echo
+       echo  "See the man page or $APP_NAME -h for more detail."
+       echo
+       exit 1
+}
+##
+# Warn about deprecated options.
+warn_deprecated_opt() {
+       # Can't use eerror and einfo because this gets called before function.sh
+       # is sourced
+       echo
+       echo "Encountered deprecated option $1." >&2
+       [[ $2 ]] && echo "Please use $2 instead." >&2
+}
+##
+# Get whole-word commandline options preceded by two dashes.
+get_longopts() {
+       case $1 in
+                                              --nocolor) export NOCOLOR="yes";;
+                                             --no-color) warn_deprecated_opt "$1" "--nocolor"
+                                                         export NOCOLOR="yes";;
+                                                --debug) set -xv;;
+                                                --exact) unset PACKAGE_NAMES;;
+                                                 --help) print_usage
+                                                         exit 0;;
+                                               --ignore) RM_OLD_TEMPFILES=1;;
+                                            --keep-temp) KEEP_TEMP=1;;
+                                            --library=*) # TODO: check for invalid values
+                                                         SONAME="${1#*=}"
+                                                         unset SEARCH_BROKEN;;
+                           --soname=*|--soname-regexp=*) # TODO: check for invalid values
+                                                         warn_deprecated_opt "${1%=*}" "--library"
+                                                         SONAME="${1#*=}"
+                                                         unset SEARCH_BROKEN;;
+                                              --library) # TODO: check for invalid values
+                                                         die_if_missing_arg $1 $2
+                                                         shift
+                                                         SONAME="$1"
+                                                         unset SEARCH_BROKEN;;
+                               --soname|--soname-regexp) # TODO: check for invalid values
+                                                         warn_deprecated_opt "$1" "--library"
+                                                         die_if_missing_arg $1 $2
+                                                         shift
+                                                         SONAME="$1"
+                                                         unset SEARCH_BROKEN;;
+                                           --no-ld-path) unset FULL_LD_PATH;;
+                                             --no-order) unset ORDER_PKGS;;
+                                          --no-progress) progress() { :; };;
+                                              --pretend) EMERGE_OPTIONS+=("--pretend");;
+                                                --quiet) echo_v() { :; }
+                                                         progress() { :; }
+                                                         quiet=1
+                                                         EMERGE_OPTIONS+=($1);;
+                                              --verbose) VERBOSE=1
+                                                         EMERGE_OPTIONS+=("--verbose");;
+                                        --extra-verbose) warn_deprecated_opt "$1" "--verbose"
+                                                         VERBOSE=1
+                                                         EMERGE_OPTIONS+=("--verbose");;
+                                        --package-names) # No longer used, since it is the
+                                                         # default. We accept it for
+                                                         # backwards compatibility.
+                                                         warn_deprecated_opt "$1"
+                                                         PACKAGE_NAMES=1;;
+                                                      *) die_invalid_option $1;;
+       esac
+}
+
+##
+# Get single-letter commandline options preceded by a single dash.
+get_shortopts() {
+       local OPT OPTSTRING OPTARG OPTIND
+       while getopts ":CdehikL:loPpqu:vX" OPT; do
+               case "$OPT" in
+                       C) # TODO: Match syntax with the rest of gentoolkit
+                          export NOCOLOR="yes";;
+                       d) set -xv;;
+                       e) unset PACKAGE_NAMES;;
+                       h) print_usage
+                          exit 0;;
+                       i) RM_OLD_TEMPFILES=1;;
+                       k) KEEP_TEMP=1;;
+                       L) # TODO: Check for invalid values
+                          SONAME="${OPTARG#*=}"
+                          unset SEARCH_BROKEN;;
+                       l) unset FULL_LD_PATH;;
+                       o) unset ORDER_PKGS;;
+                       P) progress() { :; };;
+                       p) EMERGE_OPTIONS+=("--pretend");;
+                       q) echo_v() { :; }
+                          progress() { :; }
+                          quiet=1
+                          EMERGE_OPTIONS+=("--quiet");;
+                       v) VERBOSE=1
+                          EMERGE_OPTIONS+=("--verbose");;
+                       X) # No longer used, since it is the default.
+                          # We accept it for backwards compatibility.
+                          warn_deprecated_opt "-X"
+                          PACKAGE_NAMES=1;;
+                       *) die_invalid_option "-$OPTARG";;
+               esac
+       done
+}
+##
+# Get command-line options.
+get_opts() {
+       local avoid_utils
+       local -a args
+       echo_v() { ewarn "$@"; }
+       unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
+       ORDER_PKGS=1
+       PACKAGE_NAMES=1
+       SONAME="not found"
+       SEARCH_BROKEN=1
+       FULL_LD_PATH=1
+       while [[ $1 ]]; do
+               case $1 in
+                       --) shift
+                           EMERGE_OPTIONS+=("$@")
+                           break;;
+                       -*) while true; do
+                             args+=("$1")
+                             shift
+                             [[ ${1:--} = -* ]] && break
+                           done
+                           if [[ ${args[0]} = --* ]]; then
+                             get_longopts  "${args[@]}"
+                           else
+                             get_shortopts "${args[@]}"
+                           fi;;
+                        *) die_invalid_option "$1";;
+               esac
+               unset args
+       done
+
+       setup_color
+       normalize_emerge_opts
+
+       # If the user is not super, add --pretend to EMERGE_OPTIONS
+       if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
+               ewarn "You are not superuser. Adding --pretend to emerge options."
+               EMERGE_OPTIONS+=(--pretend)
+       fi
+}
+##
+# Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
+is_real_merge() {
+       [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
+          ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
+}
+##
+# Clean up temporary files and exit
+cleanup_and_die() {
+       rm -f "$@"
+       die 1 "  ...terminated. Removing incomplete $@."
+}
+##
+# Clean trap
+clean_trap() {
+       trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+       rm -f "$@"
+}
+##
+# Returns 0 if the first arg is found in the remaining args, 1 otherwise
+# (Returns 2 if given fewer than 2 arguments)
+has() {
+       (( $# > 1 )) || return 2
+       local IFS=$'\a' target="$1"
+       shift
+       [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
+}
+##
+# Dies when it can't change directories
+cd() {
+       if builtin cd -P "$@"; then
+               if [[ $1 != $PWD ]]; then
+                       # Some symlink malfeasance is going on
+                       die 1 "Working directory expected to be $1, but it is $PWD"
+               fi
+       else
+               die 1 "Unable to change working directory to '$@'"
+       fi
+}
+##
+# Tries not to delete any files or directories it shouldn't
+setup_rm() {
+       ##
+       # Anything in the FILES array in tmpdir is fair game for removal
+       rm() {
+               local i IFS=$'\a'
+               [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
+               case $@ in
+                       */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
+               esac
+               for i; do
+                       # Don't delete files that are not listed in the array
+                       # Allow no slashes or recursive deletes at all.
+                       case $i in
+                               */*|-*r*|-*R*) :;;        # Not OK
+                                          -*) continue;; # OK
+                       esac
+                       has "$i" "${FILES[@]}" && continue
+                       die 1 "Oops, I'm not allowed to delete that. ($@)"
+               done
+               command rm "$@"
+       }
+       # delete this setup function so it's harmless to re-run
+       setup_rm() { :; }
+}
+##
+# Make our temporary files directory
+# $1 - directory name
+# $2 - user name
+verify_tmpdir() {
+       if [[ ! $1 ]]; then
+               die 1 'Temporary file path is unset! (This is a bug.)'
+       elif [[ -d $1 ]]; then
+               cd "$1"
+       else
+               die 1 "Unable to find a satisfactory location for temporary files ($1)"
+       fi
+       [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
+       setup_rm
+}
+get_search_env() {
+       local new_env
+       local old_env
+       local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
+       # Find a place to put temporary files
+       if [[ "$uid" == "root" ]]; then
+               local tmp_target="/var/cache/${APP_NAME}"
+       else
+               local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
+       fi
+
+       # From here on all work is done inside the temporary directory
+       verify_tmpdir "$tmp_target"
+       WORKING_DIR="$tmp_target"
+
+       if [[ $SEARCH_BROKEN ]]; then
+               SONAME_SEARCH="$SONAME"
+               HEAD_TEXT="broken by a package update"
+               OK_TEXT="Dynamic linking on your system is consistent"
+               WORKING_TEXT="consistency"
+       else
+               # first case is needed to test against /path/to/foo.so
+               if [[ $SONAME = /* ]]; then
+                       # Set to "<space>$SONAME<space>"
+                       SONAME_SEARCH=" $SONAME "
+                       # Escape the "/" characters
+                       SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
+               else
+                       # Set to "<tab>$SONAME<space>"
+                       SONAME_SEARCH=$'\t'"$SONAME "
+               fi
+               HEAD_TEXT="using $SONAME"
+               OK_TEXT="There are no dynamic links to $SONAME"
+               unset WORKING_TEXT
+       fi
+
+       # If any of our temporary files are older than 1 day, remove them all
+       if [[ ! $KEEP_TEMP ]]; then
+               while read; do
+                       RM_OLD_TEMPFILES=1
+                       break
+               done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
+       fi
+
+       # Compare old and new environments
+       # Don't use our previous files if environment doesn't match
+       new_env=$(
+               # We do not care if these emerge options change
+               EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
+               EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
+               EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
+               cat <<- EOF
+                       SEARCH_DIRS="$SEARCH_DIRS"
+                       SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
+                       LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
+                       PORTAGE_ROOT="$PORTAGE_ROOT"
+                       EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
+                       ORDER_PKGS="$ORDER_PKGS"
+                       FULL_LD_PATH="$FULL_LD_PATH"
+               EOF
+       )
+       if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
+               old_env=$(<"$ENV_FILE")
+               if [[ $old_env != $new_env ]]; then
+                       ewarn 'Environment mismatch from previous run, deleting temporary files...'
+                       RM_OLD_TEMPFILES=1
+               fi
+       else
+               # No env file found, silently delete any other tempfiles that may exist
+               RM_OLD_TEMPFILES=1
+       fi
+
+       # If we should remove old tempfiles, do so
+       if [[ $RM_OLD_TEMPFILES ]]; then
+               rm -f "${FILES[@]}"
+       else
+               for file in "${FILES[@]}"; do
+                       if [ -e "$file" ]; then
+                               chown ${uid}:portage "$file"
+                               chmod 700 "$file"
+                       fi
+               done
+       fi
+
+       # Save the environment in a file for next time
+       echo "$new_env" > "$ENV_FILE"
+
+       [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
+
+       echo
+       einfo "Checking reverse dependencies"
+       einfo "Packages containing binaries and libraries $HEAD_TEXT"
+       einfo "will be emerged."
+}
+
+get_files() {
+       einfo "Collecting system binaries and libraries"
+       if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
+               einfo "Found existing $FILES_FILE"
+       else
+               # Be safe and remove any extraneous temporary files
+               # Don't remove 0_env.rr - The first file in the array
+               rm -f "${FILES[@]:1}"
+
+               clean_trap "$FILES_FILE"
+
+               if [[ $SEARCH_DIRS_MASK ]]; then
+                       findMask=($SEARCH_DIRS_MASK)
+                       findMask="${findMask[@]/#/-o -path }"
+                       findMask="( ${findMask#-o } ) -prune -o"
+               fi
+               # TODO: Check this -- afaict SEARCH_DIRS isn't an array, so this should just be $SEARCH_DIRS?
+               find ${SEARCH_DIRS[@]} $findMask -type f \( -perm -u+x -o -perm -g+x -o -perm -o+x -o \
+                       -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
+                       sort -u > "$FILES_FILE" ||
+                       die $? "find failed to list binary files (This is a bug.)"
+               einfo "Generated new $FILES_FILE"
+       fi
+}
+get_ldpath() {
+       local COMPLETE_LD_LIBRARY_PATH
+       [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
+       einfo 'Collecting complete LD_LIBRARY_PATH'
+       if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
+               einfo "Found existing $LDPATH_FILE."
+       else
+               clean_trap "$LDPATH_FILE"
+               # Ensure that the "trusted" lib directories are at the start of the path
+               COMPLETE_LD_LIBRARY_PATH=(
+                       /lib*
+                       /usr/lib*
+                       $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
+                       $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
+               )
+               IFS=':'
+               COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
+               IFS="$OIFS"
+               echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
+               einfo "Generated new $LDPATH_FILE"
+       fi
+}
+main_checks() {
+       local target_file
+       local -a files
+       local i=0
+       local ldd_output
+       local ldd_status
+       local numFiles
+       local COMPLETE_LD_LIBRARY_PATH
+       if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
+               [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
+                       die 1 "Unable to find $LDPATH_FILE"
+               COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
+       fi
+       einfo "Checking dynamic linking $WORKING_TEXT"
+       if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
+               einfo "Found existing $BROKEN_FILE."
+       else
+               clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
+               files=($(<"$FILES_FILE"))
+               numFiles="${#files[@]}"
+               for target_file in "${files[@]}"; do
+                       if [[ $target_file != *.la ]]; then
+                               # Note: double checking seems to be faster than single with complete path
+                               # (special add ons are rare).
+                               ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
+                               ldd_status=$? # TODO: Check this for problems with sort
+                               # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
+                               if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
+                                       grep -q "$SONAME_SEARCH"; then
+                                       if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
+                                               if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
+                                                       grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
+                                                       # FIXME: I hate duplicating code
+                                                       # Only build missing direct dependencies
+                                                       MISSING_LIBS=$(
+                                                               expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
+                                                               sed -n "$expr" <<< "$ldd_output"
+                                                       )
+                                                       REQUIRED_LIBS=$(
+                                                               expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
+                                                               objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
+                                                       )
+                                                       MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
+                                                       if [[ $MISSING_LIBS ]]; then
+                                                               echo "obj $target_file" >> "$BROKEN_FILE"
+                                                               echo_v "  broken $target_file (requires $MISSING_LIBS)"
+                                                       fi
+                                               fi
+                                       else
+                                               # FIXME: I hate duplicating code
+                                               # Only rebuild for direct dependencies
+                                               MISSING_LIBS=$(
+                                                       expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
+                                                       sort -u <<< "$ldd_output" | sed -n "$expr"
+                                               )
+                                               REQUIRED_LIBS=$(
+                                                       expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
+                                                       objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
+                                               )
+                                               MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
+                                               if [[ $MISSING_LIBS ]]; then
+                                                       echo "obj $target_file" >> "$BROKEN_FILE"
+                                                       if [[ $SEARCH_BROKEN ]]; then
+                                                               echo_v "  broken $target_file (requires $MISSING_LIBS)"
+                                                       else
+                                                               echo_v "  found $target_file"
+                                                       fi
+                                               fi
+                                       fi
+                               fi
+                       elif [[ $SEARCH_BROKEN ]]; then
+                               # Look for broken .la files
+                               la_SEARCH_DIRS="$SEARCH_DIRS"
+                               la_search_dir=""
+                               la_broken=""
+                               la_lib=""
+                               for depend in $(
+                                       gawk -F"[=']" '/^dependency_libs/{
+                                               print $3
+                                       }' "$target_file"
+                               ); do
+                                       if [[ $depend = /* && ! -e $depend ]]; then
+                                               echo "obj $target_file" >> "$BROKEN_FILE"
+                                               echo_v "  broken $target_file (requires $depend)"
+                                       elif [[ $depend = -[LR]/* ]]; then
+                                               if ! [[ $'\n'${la_SEARCH_DIRS}$'\n' == *$'\n'${depend#-?}$'\n'* ]]; then
+                                                       la_SEARCH_DIRS+=$'\n'"${depend#-?}"
+                                               fi
+                                       elif [[ $depend = "-l"* ]]; then
+                                               la_lib="lib${depend#-l}"
+                                               la_broken="yes"
+                                               IFS=$'\n'
+                                               for la_search_dir in $la_SEARCH_DIRS; do
+                                                       if [[ -e ${la_search_dir}/${la_lib}.so || -e ${la_search_dir}/${la_lib}.a ]]; then
+                                                               la_broken="no"
+                                                       fi
+                                               done
+                                               IFS="$OIFS"
+                                               if [[ $la_broken = yes ]]; then
+                                                       echo "obj $target_file" >> "$BROKEN_FILE"
+                                                       echo_v "  broken $target_file (requires $depend)"
+                                               fi
+                                       fi
+                               done
+                               unset la_SEARCH_DIRS la_search_dir la_broken la_lib
+                       fi
+                       [[ $VERBOSE ]] &&
+                               progress $((++i)) $numFiles $target_file ||
+                               progress $((++i)) $numFiles
+               done
+               if [[ $SEARCH_BROKEN ]]; then
+                       # Look for missing version
+                       while read target_file; do
+                               echo "obj $target_file" >> "$BROKEN_FILE"
+                               echo_v "  broken $target_file (no version information available)"
+                       done < <(
+                               # Regexify LD_LIBRARY_MASK. Exclude it from the search.
+                               LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
+                               gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
+                                       /no version information available/ && $0 !~ ldmask {
+                                               gsub(/[()]/, "", $NF)
+                                               if (seen[$NF]++)  next
+                                               print $NF
+                                       }' "$ERRORS_FILE"
+                       )
+               fi
+               [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
+               sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
+               einfo "Generated new $BROKEN_FILE"
+       fi
+}
+get_packages() {
+       local target_file
+       local EXACT_PKG
+       local PKG
+       local obj
+       einfo 'Assigning files to packages'
+       if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
+               einfo "Found existing $RAW_FILE"
+       else
+               clean_trap "$RAW_FILE" "$OWNERS_FILE"
+               while read obj target_file; do
+                       EXACT_PKG=$(get_file_owner $target_file)
+                       if [[ $EXACT_PKG ]]; then
+                               # Strip version information
+                               PKG="${EXACT_PKG%%-r[[:digit:]]*}"
+                               PKG="${PKG%-*}"
+                               echo "$EXACT_PKG" >> "$RAW_FILE"
+                               echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
+                               echo_v "  $target_file -> $PKG"
+                       else
+                               ewarn " !!! $target_file not owned by any package is broken !!!"
+                               echo "$target_file -> (none)" >> "$OWNERS_FILE"
+                               echo_v "  $target_file -> (none)"
+                       fi
+               done < "$BROKEN_FILE"
+               einfo "Generated new $RAW_FILE and $OWNERS_FILE"
+       fi
+       # if we find '(none)' on every line, exit out
+       if ! grep -qvF '(none)' "$OWNERS_FILE"; then
+               ewarn "Found some broken files, but none of them were associated with known packages"
+               ewarn "Unable to proceed with automatic repairs."
+               ewarn "The broken files are listed in $OWNERS_FILE"
+               if [[ $VERBOSE ]]; then
+                       ewarn "The broken files are:"
+                       while read filename junk; do
+                               ewarn "  $filename"
+                       done < "$OWNERS_FILE"
+               fi
+               exit 0 # FIXME: Should we exit 1 here?
+       fi
+}
+clean_packages() {
+       einfo 'Cleaning list of packages to rebuild'
+       if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
+               einfo "Found existing $PKGS_FILE"
+       else
+               sort -u "$RAW_FILE" > "$PKGS_FILE"
+               einfo "Generated new $PKGS_FILE"
+       fi
+}
+assign_packages_to_ebuilds() {
+       local EXACT_PKG
+       local PKG
+       local SLOT
+       einfo 'Assigning packages to ebuilds'
+       if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
+               einfo "Found existing $EBUILDS_FILE"
+       elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
+                       clean_trap "$EBUILDS_FILE"
+                       while read EXACT_PKG; do
+                               # Get the slot
+                               PKG="${EXACT_PKG%%-r[[:digit:]]*}"
+                               PKG="${PKG%-*}"
+                               SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
+                               echo "$PKG:$SLOT"
+                       done < "$PKGS_FILE" > "$EBUILDS_FILE"
+                       einfo "Generated new $EBUILDS_FILE"
+       else
+               einfo 'Nothing to rebuild.'
+               die 1 '(The program should have already quit, so this is a minor bug.)'
+       fi
+}
+get_exact_ebuilds() {
+       einfo 'Assigning files to ebuilds'
+       if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
+               einfo "Found existing $EBUILDS_FILE"
+       elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
+               rebuildList=" $(<"$BROKEN_FILE") "
+               rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
+               get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
+               einfo "Generated new $EBUILDS_FILE"
+       else
+               einfo 'Nothing to rebuild.'
+               die 1 '(The program should have already quit, so this is a minor bug.)'
+       fi
+}
+list_skipped_packages() {
+       ewarn
+       ewarn 'Portage could not find any version of the following packages it could build:'
+       ewarn "${SKIP_LIST[@]}"
+       ewarn
+       ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
+       ewarn 'Try to emerge them manually.'
+       ewarn
+}
+get_build_order() {
+       local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
+       local RAW_REBUILD_LIST
+       local REBUILD_GREP
+       local i
+       if [[ ! $ORDER_PKGS ]]; then
+               einfo 'Skipping package ordering'
+               return
+       fi
+       einfo 'Evaluating package order'
+       if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
+               einfo "Found existing $ORDER_FILE"
+       else
+               clean_trap "$ORDER_FILE"
+               RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
+               if [[ $RAW_REBUILD_LIST ]]; then
+                       export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
+                       RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
+                       # If PACKAGE_NAMES is defined we're using slots, not versions
+                       if [[ $PACKAGE_NAMES ]]; then
+                               # Eliminate atoms that can't be built
+                               for i in "${!RAW_REBUILD_LIST[@]}"; do
+                                       if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
+                                               portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
+                                               SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
+                                       fi
+                                       unset RAW_REBUILD_LIST[i]
+                               done
+                               # If RAW_REBUILD_LIST is empty, then we have nothing to build.
+                               if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
+                                       if (( ${#SKIP_LIST[@]} == 0 )); then
+                                               ewarn "The list of packages to skip is empty, but there are no"
+                                               ewarn "packages listed to rebuild either. (This is a bug.)"
+                                       else
+                                               list_skipped_packages
+                                       fi
+                                       die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
+                               fi
+                       fi
+                       RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
+                       REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
+                       if (( ${PIPESTATUS[0]} == 0 )); then
+                               emerge --deep $RAW_REBUILD_LIST |
+                                       sed 's/\[[^]]*\]//g' |
+                                       grep -F "$REBUILD_GREP" > "$ORDER_FILE"
+                       fi
+
+                       # Here we use the PIPESTATUS from the second emerge, the --deep one.
+                       if (( ${PIPESTATUS[0]} != 0 )); then
+                                       eerror
+                                       eerror 'Warning: Failed to resolve package order.'
+                                       eerror 'Will merge in arbitrary order'
+                                       eerror
+                                       cat <<- EOF
+                                               Possible reasons:
+                                               - An ebuild is no longer in the portage tree.
+                                               - An ebuild is masked, use /etc/portage/packages.keyword
+                                                       and/or /etc/portage/package.unmask to unmask it
+                                       EOF
+                                       countdown 5
+                                       rm -f "$ORDER_FILE"
+                       fi
+                       export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
+               else
+                       einfo 'Nothing to rebuild.'
+                       die 1 '(The program should have already quit, so this is a minor bug.)'
+               fi
+       fi
+       [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
+}
+
+show_unowned_files() {
+       if grep -qF '(none)' "$OWNERS_FILE"; then
+               ewarn "Found some broken files that weren't associated with known packages"
+               ewarn "The broken files are:"
+               while read filename junk; do
+                       [[ $junk = *none* ]] && ewarn "  $filename"
+               done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
+       fi
+}
+
+# Get multiple portage variables at once to speedup revdep-rebuild.
+portage_settings() {
+       local results=()
+
+       local query_vars=(
+               ROOT
+               PORTAGE_NICENESS
+               EMERGE_DEFAULT_OPTS
+               NOCOLOR
+               SEARCH_DIRS
+               SEARCH_DIRS_MASK
+               LD_LIBRARY_MASK
+       )
+
+       # one value per line
+       IFS=$'\n'
+       results=( $(unset SEARCH_DIRS; unset SEARCH_DIRS_MASK; unset LD_LIBRARY_MASK; portageq envvar ${query_vars[*]}) )
+       IFS=$OIFS
+
+       PORTAGE_ROOT=${results[0]}
+       PORTAGE_NICENESS=${results[1]}
+       EMERGE_DEFAULT_OPTS=${results[2]}
+       export NOCOLOR=${results[3]}
+       SEARCH_DIRS+=" "${results[4]}
+       SEARCH_DIRS_MASK+=" "${results[5]}
+       LD_LIBRARY_MASK+=" "${results[6]}
+}
+
+##
+# Setup portage and the search paths
+setup_portage() {
+       # Obey PORTAGE_NICENESS (which is incremental to the current nice value)
+       if [[ $PORTAGE_NICENESS ]]; then
+               current_niceness=$(nice)
+               let PORTAGE_NICENESS=${current_niceness}+${PORTAGE_NICENESS}
+               renice $PORTAGE_NICENESS $$ > /dev/null
+               # Since we have already set our nice value for our processes,
+               # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
+               export PORTAGE_NICENESS="0"
+       fi
+
+       PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
+}
+
+##
+# Setup the paths to search (and filter the ones to avoid)
+setup_search_paths_and_masks() {
+       local configfile sdir mdir skip_me filter_SEARCH_DIRS
+
+       einfo "Configuring search environment for $APP_NAME"
+
+       # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
+       # portage, and the environment
+
+       # Read the incremental variables from environment and portage
+       # Until such time as portage supports these variables as incrementals
+       # The value will be what is in /etc/make.conf
+#      SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
+#      SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
+#      LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
+
+       # Add the defaults
+       if [[ -d /etc/revdep-rebuild ]]; then
+               for configfile in /etc/revdep-rebuild/*; do
+                       SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
+                       SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
+                       LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
+               done
+       else
+               SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
+               SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
+               LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
+       fi
+
+       # Get the ROOTPATH and PATH from /etc/profile.env
+       if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
+               SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
+       fi
+
+       # Get the directories from /etc/ld.so.conf
+       if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
+               SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
+       fi
+
+       # Set the final variables
+       SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
+       SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
+       LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
+       # Filter masked paths from SEARCH_DIRS
+       for sdir in ${SEARCH_DIRS} ; do
+               skip_me=
+               for mdir in ${SEARCH_DIRS_MASK}; do
+                       [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
+               done
+               [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
+       done
+       SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
+       [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
+}
+##
+# Rebuild packages owning broken binaries
+rebuild() {
+       if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
+               REBUILD_LIST=( $(<"$LIST.5_order") )
+               REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
+       else
+               REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
+       fi
+
+       trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+
+       einfo 'All prepared. Starting rebuild'
+       echo "emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST"
+
+       is_real_merge && countdown 10
+
+       # Link file descriptor #6 with stdin so --ask will work
+       exec 6<&0
+
+       # Run in background to correctly handle Ctrl-C
+       {
+               emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST <&6
+               echo $? > "$STATUS_FILE"
+       } &
+       wait
+
+       # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
+       exec 0<&6 6<&-
+}
+##
+# Finish up
+cleanup() {
+       if (( $(<"$STATUS_FILE") != 0 )); then
+               ewarn
+               ewarn "$APP_NAME failed to emerge all packages."
+               ewarn 'you have the following choices:'
+               einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
+               einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
+               einfo "  (and remove $ORDER_FILE to be evaluated again)"
+               einfo '- Modify the above emerge command and run it manually.'
+               einfo '- Compile or unmerge unsatisfied packages manually,'
+               einfo '  remove temporary files, and try again.'
+               einfo '  (you can edit package/ebuild list first)'
+               einfo
+               einfo 'To remove temporary files, please run:'
+               einfo "rm ${WORKING_DIR}/*.rr"
+               show_unowned_files
+               exit $EMERGE_STATUS
+       elif is_real_merge; then
+               trap_cmd() {
+                       eerror "terminated. Please remove the temporary files manually:"
+                       eerror "rm ${WORKING_DIR}/*.rr"
+                       exit 1
+               }
+               [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
+               trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+               einfo 'Build finished correctly. Removing temporary files...'
+               einfo
+               einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
+               einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
+               einfo '  orphaned files'
+               einfo '  deep dependencies'
+               einfo "  packages installed outside of portage's control"
+               einfo '  specially-evaluated libraries'
+               if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
+                       show_unowned_files
+               fi
+               [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
+       else
+               einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'
+       fi
+}
+
+main "$@"
diff --git a/gentoolkit/data/99gentoolkit-env b/gentoolkit/data/99gentoolkit-env
new file mode 100644 (file)
index 0000000..3933874
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_PROTECT_MASK="/etc/revdep-rebuild"
diff --git a/gentoolkit/data/eclean/distfiles.exclude b/gentoolkit/data/eclean/distfiles.exclude
new file mode 100644 (file)
index 0000000..a31be55
--- /dev/null
@@ -0,0 +1,5 @@
+# /etc/eclean/distfiles.exclude
+# In this file you can list some categories or cat/pkg-name for which you want
+# to protect distfiles from "ecleaning". You can also name some specific files.
+# See `man eclean` for syntax details.
+metadata.dtd
diff --git a/gentoolkit/data/eclean/packages.exclude b/gentoolkit/data/eclean/packages.exclude
new file mode 100644 (file)
index 0000000..8277155
--- /dev/null
@@ -0,0 +1,4 @@
+# /etc/eclean/packages.exclude
+# In this file you can list some categories or cat/pkg-name for which you want
+# to protect binary packages from "ecleaning".
+# See `man eclean` for syntax details.
diff --git a/gentoolkit/data/revdep-rebuild/99revdep-rebuild b/gentoolkit/data/revdep-rebuild/99revdep-rebuild
new file mode 100644 (file)
index 0000000..bdaecc7
--- /dev/null
@@ -0,0 +1,21 @@
+# Default revdep-rebuild configuration file
+#
+# revdep-rebuild no longer uses hardcoded paths. To change the default 
+# behavior the following variables can be changed:
+#
+# LD_LIBRARY_MASK - Mask of specially evaluated libraries
+#
+# SEARCH_DIRS - List of directories to search for executables and libraries
+#      Use this for directories that are not included in PATH or ld.so.conf.
+#      An application should normally not have to set this variable
+#
+# SEARCH_DIRS_MASK - List of directories to not search
+#      Use this for directories that should not be searched by revdep-rebuild
+#      This is normally used by binary packages such as openoffice-bin
+#
+# Note: This file is sourced using bash by the revdep-rebuild script
+
+LD_LIBRARY_MASK="libodbcinst.so libodbc.so libjava.so libjvm.so"
+SEARCH_DIRS="/bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
+SEARCH_DIRS_MASK="/lib*/modules"
+
diff --git a/gentoolkit/man/eclean.1 b/gentoolkit/man/eclean.1
new file mode 100644 (file)
index 0000000..19fb709
--- /dev/null
@@ -0,0 +1,173 @@
+.TH "eclean" "1" "0.4.1" "gentoolkit"
+.SH "NAME"
+eclean \- A cleaning tool for Gentoo distfiles and binary packages.
+.SH "SYNOPSIS"
+.LP
+.B eclean \fR[\fIglobal\-options\fR] ... <\fIactions\fR> \fR[\fIaction\-options\fR] ...
+.LP
+.B eclean\-dist \fR[\fIglobal\-options, distfiles\-options\fR] ...
+.LP
+.B eclean\-pkg \fR[\fIglobal\-options, packages\-options\fR] ...
+.LP
+.B eclean(-dist,-pkg) \fR[\fI\-\-help, \-\-version\fR]
+.SH "DESCRIPTION"
+\fBeclean\fP is small tool to remove obsolete portage sources files and binary packages.  
+Used on a regular basis, it prevents your DISTDIR and PKGDIR directories to 
+infinitely grow, while not deleting files which may still be useful.
+.PP
+By default, eclean will protect all distfiles or binary packages corresponding to some
+ebuilds available in the Portage tree.  This is the safest mode, since it will protect
+whatever may still be useful, for instance to downgrade a package without downloading
+its sources for the second time, or to reinstall a package you unmerge by mistake
+without recompiling it.  Sure, it's also a mode in which your DISTDIR and PKGDIR will
+stay rather big (although still not growing infinitely).  For the 'distfiles', this
+mode is also quit slow mode because it requiries some access to the whole Portage tree.
+.PP
+If you use the \-\-destructive option, eclean will only protect files corresponding to
+some currently installed package (taking their exact version into account).  It will
+save much more space, while still preserving sources files around for minor revision
+bumps, and binaries for reinstallation of corrupted packages.  But it won't keep files
+for less usual operations like downgrading or reinstalling an unmerged package.  This
+is also the fastest execution mode (big difference for distfiles), and the one used by
+most other cleaning scripts around like yacleaner (at least in its version 0.3).
+.PP
+Somewhere in the middle, adding the \-\-package\-names option when using \-\-destructive
+will protect files corresponding to all existing versions of installed packages.  It will
+allow easy downgrading without recompilation or redownloading in case of trouble, but
+won't protect you against package uninstallation.
+.PP
+In addition to this main modes, some options allow to declare a few special cases file 
+protection rules:
+.IP o
+\-\-time-limit is useful to protect files which are more recent than a given amount of time. 
+.IP o
+\-\-size-limit (for distfiles only) is useful if you want to protect files bigger than  a given size.
+.IP o
+\-\-fetch-restricted (for distfiles only) is useful to protect manually downloaded files.
+But it's also very slow (again, it's a reading of the whole Portage tree data)...
+.IP o
+Finally, you can list some categories or package names to protect in exclusion files (see
+\fBEXCLUSION FILES\fP below).
+.SH "PARAMETERS"
+.SS "Global options"
+.TP
+\fB\-C, \-\-nocolor\fP                         turn off colors on output
+.TP
+\fB\-d, \-\-destructive\fP                     only keep the minimum for a reinstallation
+.TP
+\fB\-e, \-\-exclude\-file=<path>\fP    path to the exclusion file
+\fB<path>\fP is the absolute path to the exclusion file you want to use.
+When this option is not used, default paths are /etc/eclean/{packages,distfiles}.exclude
+(if they exist).  Use /dev/null if you have such a file at it standard location and
+you want to temporary ignore it.
+.TP
+\fB\-i, \-\-interactive\fP          ask confirmation before deleting
+.TP
+\fB\-n, \-\-package\-names\fP       protect all versions (\-\-destructive only)
+.TP
+\fB\-p, \-\-pretend\fP              only display what would be cleaned
+.TP
+\fB\-q, \-\-quiet\fP                be as quiet as possible, only display errors
+.TP
+\fB\-t, \-\-time-limit=<time>\fP    don't delete files modified since <time>
+\fB<time>\fP is an amount of time: "1y" is "one year", "2w" is "two weeks", etc.
+.br
+Units are: y (years), m (months), w (weeks), d (days) and h (hours).
+.TP
+\fB\-h, \-\-help\fP                 display the help screen
+.TP
+\fB\-V, \-\-version\fP              display version informations
+.SS "Actions"
+.TP
+\fBdistfiles\fR
+Clean files from /usr/portage/distfiles (or whatever else is your DISTDIR in /etc/make.conf).
+This action should be useful to almost any Gentoo user, we all have to big DISTDIRs sometime...
+.br
+\fBeclean\-dist\fP is a shortcut to call eclean with the "distfiles" action, for simplified
+command\-line.
+.TP
+\fBpackages\fR
+Clean files from /usr/portage/packages (or whatever else is your PKGDIR in /etc/make.conf).
+This action is in particular useful for people who use the "buildpkg" or "buildsyspkg"
+FEATURES flags.
+.br
+\fBeclean\-pkg\fP is a shortcut to call eclean with the "packages" action, for simplified
+command\-line.
+.SS "Options for the 'distfiles' action"
+.TP
+\fB\-f, \-\-fetch-restricted\fP                protect fetch-restricted files (\-\-destructive only)
+.TP
+\fB\-s, \-\-size-limit=<size>\fP       don't delete distfiles bigger than <size>
+<size> is a size specification: "10M" is "ten megabytes", "200K" is "two hundreds kilobytes",
+etc.
+.br
+Units are: G, M, K and B.
+.SS "Options for the 'packages' action"
+.TP
+There is no specific option for this action.
+.SH "EXCLUSION FILES"
+Exclusions files are lists of packages names or categories you want to protect
+in particular.  This may be useful to protect more binary packages for some system
+related packages for instance.  Syntax is the following:
+.IP o
+blank lines and lines starting with a "#" (comments) are ignored.
+.IP o
+only one entry per line is allowed.
+.IP o
+if a line contains a category name, like "sys\-apps", then all packages from this 
+category will be protected.  "sys\-apps/*" is also allowed for aesthetic reasons, but
+that does NOT mean that wildcard are supported in any way for any other usage.
+.IP o
+if a line contains a package name ("app\-shells/bash"), then this package will be
+protected.  Versioned atoms like ">=app\-shells/bash\-3" are NOT supported.  Also, the
+full package name (with category) is mandatory.
+.IP o
+if a line contains a package name with an exclamation mark in front ("!sys\-apps/portage"),
+then this package will be excluded from protection.  This is only useful if the category 
+itself was protected.
+.IP o
+for distfiles protection, a line can also a filename to protect. This is useful if you have
+there some files which are not registered by the ebuilds, like OpenOffice.org i18n files
+("helpcontent_33_unix.tgz" for instance).
+.LP
+By default, if it exists, /etc/eclean/packages.exclude (resp. distfiles.exclude) will be use
+when action is "packages" (resp. "distfiles").  This can be overide with the \-\-exclude\-file
+option.
+.SH "EXAMPLES"
+.LP
+Clean distfiles only, with per file confirmation prompt:
+.br
+.B # eclean \-i distfiles
+.LP
+Check which binary packages could be removed, with a no-color display:
+.br
+.B # eclean \-Cp packages
+.LP
+Clean binary packages of uninstalled packages, but keep all versions of installed ones:
+.br
+.B # eclean-pkg \-d \-n
+.LP
+Clean all distfiles except for installed packages (exact version), those which
+are less than one month old, bigger than 50MB, or fetch-restricted:
+.br
+.B # eclean-dist \-d \-t1m -s50M -f
+.LP
+From a crontab, silently clean packages in the safest mode, and then distfiles in destructive
+mode but protecting files less than a week old, every sunday at 1am:
+.br
+.B 0 1 * * sun \ \ eclean \-C \-q packages ; eclean \-C \-q \-d \-t1w distfiles
+.".SH "BUGS"
+.".TP
+."The policy used to decide wether a distfile can be removed or not relies on the SRC_URI variables ."of ebuilds.  It means that if an ebuild uses files that are not part of its SRC_URI, eclean will ."probably remove them.  This are ebuilds bugs, please report them as such on ."http://bugs.gentoo.org. 
+.".TP
+."In safest mode (default, without the \-\-destructive option), this script can be very slow.  There
+."is not much to do about it without hacking outside of the portage API.
+.SH "SEE ALSO"
+.TP
+The Gentoo forum thread that gave birth to eclean:
+.B http://forums.gentoo.org/viewtopic.php?t=3011
+.TP
+The bug report requesting eclean inclusion in gentoolkit:
+.B http://bugs.gentoo.org/show_bug.cgi?id=33877
+.SH "AUTHORS"
+Thomas de Grenier de Latour (tgl) <degrenier@easyconnect.fr>
diff --git a/gentoolkit/man/epkginfo.1 b/gentoolkit/man/epkginfo.1
new file mode 100644 (file)
index 0000000..bfab2d7
--- /dev/null
@@ -0,0 +1,38 @@
+.TH "EPKGINFO" "1" "August 2009" "GENTOOLKIT"
+
+.SH "NAME"
+epkginfo \- Displays metadata information from packages in portage
+
+.SH "SYNOPSIS"
+.BI "epkginfo " "[options] " "package name or atom"
+
+.SH "DESCRIPTION"
+.B Epkginfo
+is a shortcut to \fBequery meta\fP. For all available options and more
+examples, see \fBman equery\fP.
+
+.SH "EXAMPLES"
+.nf
+  * app-portage/gentoolkit [portage]
+Location:    /usr/portage/app-portage/gentoolkit
+Herd:        tools-portage (tools-portage@gentoo.org)
+Maintainer:  None specified
+Upstream:    None specified
+Keywords:    0.2.4.5: alpha amd64 arm hppa ia64 m68k ~mips ppc ppc64
+                      s390 sh sparc x86 -x86-fbsd
+Keywords:    0.3.0_rc5: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips
+                        ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd
+                        ~x86-fbsd
+Keywords:    0.3.0_rc6: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips
+                        ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd
+                        ~x86-fbsd
+Keywords:    0.3.0_rc7: ~alpha ~amd64 ~arm ~hppa ~ia64 ~m68k ~mips
+                        ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~sparc-fbsd
+                        ~x86-fbsd
+.fi
+
+.SH "AUTHORS"
+.LP
+Douglas Anderson <douglasjanderson@gmail.com>
+.SH "BUGS"
+Please report any bugs to http://bugs.gentoo.org
diff --git a/gentoolkit/man/equery.1 b/gentoolkit/man/equery.1
new file mode 100644 (file)
index 0000000..ec07c0d
--- /dev/null
@@ -0,0 +1,538 @@
+.TH "EQUERY" "1" "August 2009" "GENTOOLKIT"
+.SH "NAME"
+equery \- Gentoo Package Query Tool
+
+.SH "SYNOPSIS"
+.BI "equery " "[global-options] " "module " "[local-options]"
+
+.SH "DESCRIPTION"
+.B Equery
+is a collection of modules for querying the state of Gentoo packages, files and USE flags.
+
+.SH "GLOBAL OPTIONS"
+.HP
+.B \-h, \-\-help
+.br
+Output a help message.
+.HP
+.B \-q, \-\-quiet
+.br
+Be less verbose where possible. In some modules, this option can increase the output speed.
+.HP
+.B \-C, \-\-no-color
+.br
+Do not colorize output.
+.HP
+.B \-N, \-\-no\-pipe
+.br
+Turn off automatic pipe detection. Use this option if you do not want
+.B equery
+to detect if the output is being directed to the screen or to another program and adjust color and verbosity accordingly.
+.HP
+.B \-V, \-\-version
+.br
+Display \fBGentoolkit\fP's version. Please include this in all bug reports. (see
+.B BUGS
+below)
+
+.SH "MODULES"
+.B Equery
+uses a system of modules. Each module has both a long and short name. The list below uses the notation "\fBmodule (m)\fP", where \fIm\fP is the short name and \fImodule\fP is the long name.
+.P
+You can view the
+.B help
+message for a specific module by using
+.BR "-h" ", " "--help "
+as either a global option (after
+.B equery
+and before the module name) or as a local option (after the module name).
+
+.SS
+.BI "belongs (b) [OPTIONS] " "FILE"
+List the package that owns \fIFILE\fP.
+.P
+.BI Note:
+Normally, only one package will own \fIFILE\fP. If multiple packages own the
+same file it should be reported. (see
+.B BUGS
+below)
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-f, \-\-full-regex
+.br
+The supplied query is a regular expression.
+.HP
+.B \-e, \-\-early-out
+.br
+Stop when the first match is found. This is generally a safe optimization when searching for the owner of a single file.
+.HP
+.B \-n, \-\-name-only
+.br
+Do not print the version.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery belongs --early-out /usr/bin/euse
+.EE
+.br
+Find out what package installed a certain command.
+.EX
+.HP
+emerge -p $(equery -q belongs -nf '^/usr/bin/g?vim.*')
+.EE
+.br
+Tell
+.B emerge
+to reinstall or update any package that installed a file matching a regular expression.
+
+.SS
+.BI "changes (c) [OPTIONS] " "PKG"
+Display the Gentoo ChangeLog entry for the latest installable version of \fIPKG\fP.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-l, \-\-latest
+.br
+Display only the latest ChangeLog entry. It's not uncommon for changes to be prepended to the ChangeLog without a version header if the changes did not require a version bump. Use this option to display such entries.
+.HP
+.B \-f, \-\-full
+.br
+Display the full ChangeLog.
+.br
+\fBHint\fP: Try piping (|) the output to a pager, like 'less'.
+.HP
+.BI "\-\-limit=" "NUM"
+.br
+Limit the \fINUM\fP of entries displayed. Use  this option in conjunction with \fB--full\fP. \fB--limit=3\fP would display the three latest entries.
+.HP
+.BI "\-\-from=" "VER"
+.br
+Set which \fIVER\fP to display from. Using this option by itself is equivalent to passing \fBchanges\fP a ranged package atom, e.g., '>=foo/bar-1.5'. It can be used in conjunction with \fB--to\fP to request a more complex range of entries.
+.HP
+.BI "\-\-to=" "VER"
+.br
+Set which \fIVER\fP to display to. (See \fB--from\fP)
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery changes portage
+.EE
+.br
+Display the Gentoo ChangeLog entry for the latest installable version of Portage.
+.EX
+.HP
+equery changes '=sys-apps/portage-2.1.6*'
+.EE
+.br
+Use Portage's atom syntax. (See \fBman 5 ebuild\fP)
+.EX
+.HP
+equery changes portage --from=2.2_rc1 --to=2.2
+.EE
+.br
+Display any ChangeLog entry within a range of versions.
+
+.SS
+.BI "check (k) [OPTIONS] " "PKG"
+Check timestamps and MD5 sums for files owned by \fIPKG\fP, where \fIPKG\fP is an installed package.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-f, \-\-full-regex
+.br
+The supplied query is a regular expression.
+.HP
+.B \-o, \-\-only-failures
+.br
+Only display packages which don't pass all checks.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery check --only-failures '*'
+.EE
+.br
+Verify timestamps and MD5 sums for all installed packages and show only packages which fail checks.
+.EX
+.HP
+equery check 'dev-python/*' dev-lang/python
+.EE
+.br
+Verify every installed package in the \fBdev-python\fP category, and Python itself.
+
+.SS
+.BI "depends (d) [OPTIONS] " "PKG"
+List all packages that depend on \fIPKG\fP.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-a, \-\-all-packages
+.br
+Include dependencies that are not installed. This can take a while.
+.HP
+.B \-D, \-\-indirect
+.br
+Search for both direct and indirect dependencies.
+.HP
+.BI "\-\-depth=" "NUM"
+.br
+Limit the indirect dependency tree to a depth of \fINUM\fP. \fB--depth=0\fP is equivalent to not using \fB--indirect\fP.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery depends --indirect xulrunner
+.EE
+.br
+Figure out why a package got installed on your system.
+
+.SS
+.BI "depgraph (g) [OPTIONS] " "PKG"
+Display a direct dependency graph for every matching version of \fIPKG\fP. A dependency graph is an
+indented tree showing the relationship between packages and their dependencies.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-A, \-\-no-atom
+.br
+Do not show the dependency atom that match the package.
+.HP
+.B \-U, \-\-no-useflags
+.br
+Do not show USE flags.
+.HP
+.B \-l, \-\-linear
+.br
+Do not format the graph by indenting dependencies. This option will print the
+recursion depth in square brackets before the package name for easier viewing
+in narrow terminals.
+.HP
+.BI "\-\-depth=" "NUM"
+.br
+Limit the dependency graph to a depth of \fINUM\fP. \fB--depth=0\fP means no
+maximum depth. Default depth is set to 1.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery depgraph --depth=0 portage
+.EE
+.br
+View a full tree of all direct and indirect compile-time, run-time, and post-merge dependencies for a package.
+
+.SS
+.BI "files (f) [OPTIONS] " "PKG"
+List files and directories installed by \fIPKG\fP.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-m, \-\-md5sum
+.br
+Include the file's MD5 sum in the output.
+.HP
+.B \-s, \-\-timestamp
+.br
+Include the file's timestamp in the output.
+.HP
+.B \-t, \-\-type
+.br
+Include the file type in the output.
+.HP
+.B \-\-tree
+.br
+Display files in a tree format. This option turns off all other local options.
+.HP
+.BI "\-f, \-\-filter=" "RULES"
+.br
+Filter output by file type.
+.HP
+RULES
+.br
+A comma-separated list (no spaces); choose from:
+.br
+.B dir, obj, sym, dev, path, conf, cmd, doc, man, info
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery files --tree vlc
+.EE
+.br
+View a file tree of all files installed by a package.
+.EX
+.HP
+equery files --filter=cmd vlc
+.EE
+.br
+Find out where a package installed its executables.
+
+.SS
+.BI "hasuse (h) [OPTIONS] " "USE"
+List all installed packages that have a given \fIPKG\fP flag.
+
+\fBNote\fP: \fBhasuse\fP does not currently have the ability to display if packages are built with the given USE flag or not. It can only list which packages have the flag as an option. (See \fIEXAMPLES\fP)
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-I, \-\-exclude-installed
+.br
+Exclude installed packages from being output.
+.HP
+.B \-o, \-\-overlay-tree
+.br
+Include package from overlays in the search path.
+.HP
+.B \-p, \-\-portage-tree
+.br
+Include all packages from the Portage tree in the search path. Use this option to search through all standard Gentoo packages, including those that are not installed.
+.P
+.IR "OUTPUT" ":"
+.HP
+(See \fIOUTPUT\fP for \fBlist\fP below)
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery hasuse -pI perl
+.EE
+.br
+View all Gentoo packages that have the "perl" USE flag, excluding installed packages.
+.EX
+.HP
+USE="perl"; for PKG in $(equery -q hasuse $USE); do echo $PKG: $(equery -q uses $PKG |grep $USE); done
+.EE
+.br
+This Bash one-liner uses \fBhasuse\fP to find a list of packages that have a certain USE flag, and \fBuses\fP to check whether the flag is enabled or disabled. Modify \fBUSE="perl"\fP to change the query.
+
+.SS
+.BI "list (l) [OPTIONS] " "PKG"
+List installed versions of \fIPKG\fP or all packages matching the query pattern.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-d, \-\-duplicates
+.br
+List only packages with more than one version installed.
+.HP
+.B \-f, \-\-full-regex
+.br
+The supplied query is a regular expression.
+.HP
+.B \-m, \-\-mask-reason
+.br
+Print the reason why a package was masked.
+.HP
+.B \-I, \-\-exclude-installed
+.br
+Exclude installed packages from being output.
+.HP
+.B \-o, \-\-overlay-tree
+.br
+Include package from overlays in the search path.
+.HP
+.B \-p, \-\-portage-tree
+.br
+Include all packages from the Portage tree in the search path. Use this option to search through all standard Gentoo packages, including those that are not installed.
+.P
+.IR "OUTPUT" ":"
+
+.EX
+$ equery list binutils
+ * Searching for binutils ...
+ [I--] [XX] sys-devel/binutils-2.18-r1:i686-pc-linux-gnu-2.18
+ [IP-] [ ~] sys-devel/binutils-2.19.1-r1:i686-pc-linux-gnu-2.19.1
+.EE
+.HP
+Location field (\fB[IPO-]\fP):
+.br
+The first field shows the location and install status of the package. It consists of three letters in square brackets. \fBI\fP indicates the package is currently installed. \fBP\fP indicates the package is available in the Portage tree. \fBO\fP indicates the package is available in at least one overlay. \fB-\fP is a place holder and has no meaning. \fB[I-O]\fP would mean that the package is installed and available from an overlay, but not available from the Portage tree.
+.HP
+Mask-status field (\fB[ ~M-XX]\fP):
+.br
+The second field shows the mask status of the package. Empty brackets indicate that the package is unmasked. A \fB~\fP means the package is masked by keyword, e.g., you are running a stable system and the package is marked testing). \fBM\fP means hard masked, e.g., the package maintainer has determined the package is unfit for widespread usage. \fB-\fP means arch masked, e.g., you are running an amd64 system, but this package only works on x86. Lastly, \fBXX\fP only occurs when the location field is \fB[I--]\fP. Together, they indicate that the package was installed from the Portage tree or an overlay, but has since been removed from that tree; therefore \fBequery\fP can not determine a mask status for it.
+.HP
+Package name:
+.br
+The third field is the full package name and version.
+.HP
+Slot:
+.br
+The fourth field, after the colon, is the package's slot. \fB0\fP is the default slot. To find all packages with multiple slots installed, use \fB--duplicates\fP.
+.P
+\fBNote:\fP Because it takes extra processing time to determine the location, mask status and slot, you can speed up output by passing the \fB--quiet\fP global option to \fBequery\fP when you don't care about the extra information.
+
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery list '*'
+.EE
+.br
+List all installed packages. This is equivalent to '\fBequery list\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list -op mozilla-firefox
+.EE
+.br
+List all available versions of the package exactly matching 'mozilla-firefox'. This is equivalent to '\fBequery list --exact-name -o -p mozilla-firefox\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list '*zilla*'
+.EE
+.br
+List all packages that contain (fuzzy match) 'zilla'. This is equivalent to '\fBequery list zilla\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list 'www-client/*'
+.EE
+.br
+List all packages in the category \fBwww-client\fP. This is equivalent to '\fBequery list --category=www-client\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+.EX
+.HP
+equery list --duplicates '*'
+.EE
+.br
+List all packages with more than one version installed. This is equivalent to '\fBequery list --duplicates\fP' in \fBGentoolkit\fP versions prior to 0.3.0.
+
+.SS
+.BI "meta (m) [OPTIONS] " "PKG"
+Display metadata about \fIPKG\fP.
+
+\fBmeta\fP reads a file called metadata.xml which must be included with all Portage tree packages. \fBmeta\fP does not read ebuilds, so it can only return version independent metadata. Since until now there had not been an easy way for users to view metadata.xml, and because package maintainers are only required to fill out a very small portion of the file, there are still many packages without detailed metadata available. For more information about metadata.xml, see:
+.br
+.EX
+http://www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=4
+.EE
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-d, \-\-description
+.br
+Show an extended package description.
+.HP
+.B \-H, \-\-herd
+.br
+Show the herd(s) for the package. When not piping and not passing \fB--quiet\fP as a global option, also show the herd's email address. (shown by default)
+.HP
+.B \-k, \-\-keywords
+.br
+Show keywords for all matching versions. \fBkeywords\fP does not list all keywords for all versions. Instead, it filters the list to make it easier to spot versions that need bumping or are okay to remove from the tree. It filters by slot. For example:
+.br
+Keywords:    1.35.0-r3:\fB0\fP:
+.br
+Keywords:    1.35.0-r5:\fB0\fP: amd64 hppa ppc x86 ~alpha ~arm ~ia64 ~mips ~ppc64 ~s390 ~sh ~sparc
+.br
+In this output from \fBequery meta boost\fP, -r5 is the highest available version in slot 0, so all keywords are listed. The actual keywords for -r3 are "~amd64 ~hppa ~ppc ~x86", but since a higher version in the same slot has the same or more stable keywording, they are filtered out. Arch mask keywords (-*) are always shown.
+.HP
+.B \-m, \-\-maintainer
+.br
+Show the package maintainer(s) email address. If the metadata is available, also show the maintainer's name and/or job description. (shown by default)
+.HP
+.B \-u, \-\-useflags
+.br
+Show per-package USE flag descriptions. Per-package USE flag descriptions are sometimes added to metadata.xml if the affect of the USE flag is unusual, or if the USE flag is rare enough to be undefined in the global definition file. \fBequery uses\fP now displays these same local descriptions as well, so this option is left in \fBmeta\fP for completeness only.
+.HP
+.B \-U, \-\-upstream
+.br
+Show information about the package's upstream project, including the author's email, upstream bug tracker or upstream documentation. At the time of writing, most maintainers do not provide this information. (shown by default)
+.HP
+.B \-x, \-\-xml
+.br
+Dump the plain XML file to the screen.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery meta gnucash
+.EE
+.br
+Show general information about maintainership, including herd, maintainer and upstream.
+.EX
+.HP
+equery meta --description screen
+.EE
+.br
+See if the package maintainer has provided an extended description.
+.EX
+.HP
+equery -N meta -H gnome |grep -o --color=never '[^( ]*@gentoo.org'
+.EE
+.br
+Extract the herd's email address to let them know they're doing a great job. Remember, bug reports should go to bugs.gentoo.org. The above example will extract one or more emails if available, or return nothing if the herd is \fBno-herd\fP.
+
+.SS
+.BI "size (s) [OPTIONS] " "PKG"
+Print total size of files contained in a given \fIPKG\fP.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-b, \-\-bytes
+.br
+Report package size in bytes.
+.HP
+.B \-f, \-\-full-regex
+.br
+The query is a regular expression.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery -q size 'www-client/*'
+.EE
+.br
+Get a one-line summary of the number of files and total size (in bytes) of those files for each installed package in a category.
+
+.SS
+.BI "uses (u) [OPTIONS] " "PKG"
+Display USE flag statuses and desriptions for a given \fRPKG\fP.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-a, \-\-all
+.br
+Display all package versions. Without this option, \fBequery\fP will choose the best available version.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+equery uses app-misc/beagle
+.EE
+.br
+See which USE flags are enabled for a specific package.
+.EX
+.HP
+USE="perl"; for PKG in $(equery -q hasuse $USE); do echo $PKG: $(equery -q uses $PKG |grep $USE); done
+.EE
+.br
+This Bash one-liner uses \fBhasuse\fP to find a list of packages that have a certain USE flag, and \fBuses\fP to check whether the flag is enabled or disabled. Modify \fBUSE="perl"\fP to change the query.
+
+.SS
+.BI "which (w) [OPTIONS] " "PKG"
+Display the path to the ebuild that would be used by Portage with the current configuation.
+
+.IR "LOCAL OPTIONS" ":"
+.HP
+.B \-m, \-\-include-masked
+.br
+Return the path to the hightest version ebuild available.
+.P
+.IR "EXAMPLES" ":"
+.EX
+.HP
+less $(equery which xorg-server)
+.EE
+.br
+View the most recent installable ebuild.
+
+.SH "BUGS"
+Submit bug reports to http://bugs.gentoo.org.
+
+.SH "AUTHORS"
+Karl Trygve Kalleberg <karltk@gentoo.org>, 2003
+.br
+Katerina Barone\-Adesi <katerinab@gmail.com>, 2004
+.br
+Douglas Anderson <douglasjanderson@gmail.com>, 2009
diff --git a/gentoolkit/man/eread.1 b/gentoolkit/man/eread.1
new file mode 100644 (file)
index 0000000..5e18214
--- /dev/null
@@ -0,0 +1,12 @@
+.TH "eread" "1" "1.0" "Donnie Berkholz" "gentoolkit"
+.SH "NAME"
+.LP 
+eread \- Gentoo: Tool to display and manage ELOG files from portage
+.SH "SYNTAX"
+.LP 
+eread
+.SH "DESCRIPTION"
+.LP 
+This tool is used to display and manage ELOG files produced by portage version 2.1 and higher.
+.SH "ENVIRONMENT VARIABLES"
+The eread utility uses the PAGER environment variable to display the ELOG files. If the variable is not set, it defaults to /usr/bin/less.
diff --git a/gentoolkit/man/euse.1 b/gentoolkit/man/euse.1
new file mode 100644 (file)
index 0000000..b5148fd
--- /dev/null
@@ -0,0 +1,102 @@
+.TH "EUSE" "1" "2004-10-17" "Gentoo Linux" "Gentoo Toolkit"
+.SH "NAME"
+euse \- Gentoo: command line USE flag editor
+.SH "SYNOPSIS"
+.B euse
+\fI<option> [suboption] [useflaglist]\fB
+.SH "DESCRIPTION"
+.PP 
+.I euse
+is used to set(disable/enable) USE flags in /etc/make.conf without having to edit
+the file directly. It is also used to get detail information about use flags
+like description, status of flags(enabled/disabled), type of flag(global/local)
+etc.
+.SH "OPTIONS "
+.TP 
+\fB\-E, \-\-enable\fI
+Enables USE flag(s) in make.conf. It accepts one or more space seperated 
+USE flags as parameters.
+.TP 
+\fB\-D, \-\-disable\fI
+Disables USE flag(s) in make.conf. Puts a '\-' sign in front of the USE flag
+and appends it to the USE setting in make.conf. It accepts one or more 
+space seperated USE flags as parameters.
+.TP 
+\fB\-P, \-\-prune\fI
+Removes USE flag(s) in make.conf. Removes all positive and negative references to
+the given USE flags from make.conf.
+.TP 
+\fB\-i, \-\-info\fI
+Prints detail information about the USE flag(s). If no arguments are given then
+it assumes you want information for all USE flags. If one or more
+arguments are given (space separated) then only information for those flags is
+printed.
+.TP 
+\fB\-I, \-\-info\-installed\fI
+Same as \-\-info, except that it will also list the currently installed packages that are utilizing the flag.
+.sp
+.RS
+The output is in the following format:
+.br 
+\fB[\- cD ]\fI alpha \- indicates that architecture ...
+.br 
+\fB[\-   ]\fI moznocompose (net\-www/mozilla):
+.br 
+Disable building of mozilla's web page composer 
+.br 
+The indicators in the first column are:
+.IP is_active
++ if the flag is seen as active by portage, \- if not
+.IP is_in_env
+E if the flag is enabled in the environment, e if it is
+disabled in the environment, nothing if it's not affected
+by the environment
+.IP is_in_make_conf
+C if the flag is enabled in make.conf, c if it is
+disabled in make.conf, nothing if it's not affected
+by make.conf
+.IP is_in_make_defaults
+D if the flag is enabled in make.defaults, d if it is
+disabled in make.defaults, nothing if it's not affected
+by make.defaults
+.IP is_in_make_globals
+G if the flag is enabled in make.globals, g if it is
+disabled in make.globals, nothing if it's not affected
+by make.globals
+.br 
+Then follows the name of the flag, for local flags the
+package name and then the description (on a new line for
+local flags).
+.TP 
+\fB\-a, \-\-active\fI
+Shows all currently active USE flags and where they are activated (see 
+description for \fB\-\-info\fI).
+.TP 
+\fB\-h, \-\-help\fI
+Show the help message listing all the available flags and a short description
+.TP 
+\fB\-v, \-\-version\fI
+Show the version information
+.SH "FILES"
+/etc/make.conf
+.br 
+/etc/make.profile/make.defaults
+.br 
+/etc/make.globals
+.br 
+$PORTDIR/profiles/use.desc
+.br 
+$PORTDIR/profiles/use.local.desc
+.br 
+
+.SH "AUTHOR"
+Original version by Arun Bhanu <codebear@gentoo.org>
+.br 
+Updated for rewritten euse by Marius Mauch <genone@gentoo.org>
+.SH "BUGS"
+euse doesn't handle USE flags enabled or disabled by use.defaults, use.mask
+or package.use yet. It also doesn't completely understand the \-* flag.
+.SH "SEE ALSO"
+.BR ufed(8), 
+.TP 
+The \fI/usr/bin/euse\fR script. 
diff --git a/gentoolkit/man/glsa-check.1 b/gentoolkit/man/glsa-check.1
new file mode 100644 (file)
index 0000000..be15992
--- /dev/null
@@ -0,0 +1,66 @@
+.TH "glsa-check" "1" "0.3" "Marius Mauch" "gentoolkit"
+.SH "NAME"
+.LP 
+glsa\-check \- Gentoo: Tool to locally monitor and manage GLSAs
+.SH "SYNTAX"
+.LP 
+glsa\-check <\fIoption\fP> [\fIglsa\-list\fP]
+
+[\fIglsa\-list\fR] can contain an arbitrary number of GLSA ids, filenames containing GLSAs or the special identifiers 'all' and 'affected'
+.SH "DESCRIPTION"
+.LP 
+This tool is used to locally monitor and manage Gentoo Linux Security Advisories.
+Please read:
+.br 
+http://www.gentoo.org/security
+.br 
+before reporting a bug.
+.LP
+Note: In order for this tool to be effective, you must regularly sync your local portage tree.
+.SH "OPTIONS"
+.LP 
+.TP 
+.B \-l, \-\-list
+list the a summary for all GLSAs in glsa-list and whether they affect the system
+.TP 
+.B \-d, \-\-dump, \-\-print
+show all information about the GLSAs in glsa-list
+.TP 
+.B \-t, \-\-test
+test if this system is affected by the GLSAs in glsa-list and output the GLSA IDs
+.TP 
+.B \-p, \-\-pretend
+show the necessary steps to apply the GLSAs in glsa-list
+.TP 
+.B \-f, \-\-fix
+try to auto\-apply the GLSAs in in glsa-list using emerge. This will only upgrade packages to later version, but not remove packages when no upgrade path exists (experimental)
+.TP 
+.B \-i, \-\-inject
+inject the given GLSA into the glsa_injected file
+.TP 
+.B \-n, \-\-nocolor
+disable colors (option)
+.TP 
+.B \-h, \-\-help
+show this help message
+.TP 
+.B \-V, \-\-version
+some information about this tool
+.TP 
+.B \-v, \-\-verbose
+print more messages (option)
+.TP
+.B \-c, \-\-cve
+show CVE ids in listing mode (option)
+.TP
+.B \-q, \-\-quiet
+be less verbose and do not send empty mail (option)
+.TP 
+.B \-m, \-\-mail
+send a mail with the given GLSAs to the administrator
+.SH "FILES"
+.LP 
+.TP 
+.B /var/lib/portage/glsa_injected
+List of GLSA ids that have been injected and will never show up as 'affected' on this system.
+The file must contain one GLSA id (e.g. '200804-02') per line.
diff --git a/gentoolkit/man/revdep-rebuild.1 b/gentoolkit/man/revdep-rebuild.1
new file mode 100644 (file)
index 0000000..a03ed58
--- /dev/null
@@ -0,0 +1,137 @@
+.TH "revdep\-rebuild" "1" "" "gentoolkit" ""
+.SH "NAME"
+revdep\-rebuild \- Gentoo: Reverse Dependency Rebuilder
+.SH "SYNOPSIS"
+.B revdep\-rebuild
+[OPTIONS] [\-\-] [EMERGE OPTIONS]
+.SH "DESCRIPTION"
+revdep\-rebuild scans libraries and binaries for missing shared library dependencies and attempts to fix them by re\-emerging those broken binaries and shared libraries.  It is useful when an upgraded package breaks other software packages that are dependent upon the upgraded package.
+.SH "OPTIONS"
+.TP 
+.B \-C | \-\-nocolor
+Turn off colored output. (This option is also passed to portage.)
+.TP
+.B \-d | \-\-debug
+Print way too much information (uses bash's set -xv)
+.TP      
+.B \-e | \-\-exact
+Emerge the most recent version of found packages, without regard to SLOT.
+.TP 
+.B \-h | \-\-help
+Print usage.
+.TP
+.B \-i | \-\-ignore
+Delete temporary files from previous runs.
+.TP
+.B \-k | \-\-keep\-temp
+Force revdep\-rebuild not to delete temporary files after it successfully rebuilds packages. This option will NOT prevent revdep\-rebuild from deleting inconsistent or out\-of\-date temporary files.
+.TP
+.B \-\-library NAME | -L NAME
+Search for reverse dependencies for a particular library or group of libraries, rather than every library on the system. Emerge packages that use the named library. NAME can be a full path to a library or basic regular expression.  (See regex(7).)
+.TP 
+.B \-l | \-\-no\-ld\-path
+Do not set LD_LIBRARY_PATH. \fBNote:\fR Using this option will cause revdep-rebuild to report some false positives.
+.TP 
+.B \-o | \-\-no-order
+Do not check the build order against the deep dependency list.  This will make revdep-rebuild faster, but it can cause emerge failures.  Please try revdep\-rebuild without \-o before reporting any bugs.
+.TP 
+.B \-p | \-\-pretend
+Do a dry-run.  Do not delete temporary files.  (\-k \-p is redundant, but harmless.)  \-\-pretend is assumed when not running revdep\-rebuild as root.
+.TP 
+.B \-P | \-\-no\-progress
+Turn off the progress meter
+.TP 
+.B \-q | \-\-quiet
+Print less output and disable the progress meter.  (This option is also passed to portage.)
+.TP
+.B \-v | \-\-verbose
+More output.  (Prints the revdep\-rebuild search environment.)
+.TP
+.B Options after -- are ignored by revdep-rebuild and passed directly to emerge.
+.SH "CONFIGURATION"
+revdep\-rebuild no longer uses hardcoded paths. To change the default behavior the following variables can be changed by the user.
+
+LD_LIBRARY_MASK \- Mask of specially evaluated libraries
+.LP 
+SEARCH_DIRS \- List of directories to search for executables and libraries
+.LP 
+SEARCH_DIRS_MASK \- List of directories to not search
+
+You can prepend to these variables by setting the variable in your environment prior to execution, by placing an entry in /etc/make.conf, or by placing a file in /etc/revdep\-rebuild containing the appropriate variables.
+
+The variables are read and set in the following order:
+
+environment settings \- one time changes by user
+.br 
+/etc/make.conf \- persistent changes by user
+.br 
+/etc/revdep\-rebuild/* \- persistent changes by ebuild authors
+
+While a user can edit and modify the files in the /etc/revdep\-rebuild directory, please be aware that the /etc/revdep\-rebuild directory is not under configuration protection and files can be removed and/or overwritten by an ebuild. To change this add /etc/revdep\-rebuild to the CONFIG_PROTECT variable in /etc/make.conf.
+
+An entry of "\-*" means to clear the variable from that point forward.
+Example: SEARCH_DIRS="/usr/bin \-*" will set SEARCH_DIRS to contain only /usr/bin
+
+revdep\-rebuild honors the NOCOLOR and PORTAGE_NICENESS variables from /etc/make.conf
+.SH "EXAMPLES"
+It is recommended that when running revdep\-rebuild that the following command be used initially:
+.br 
+\fBrevdep\-rebuild \-\-ignore \-\-pretend\fR
+
+To search the entire system, while excluding /mnt and /home:
+.br 
+\fBenv SEARCH_DIRS="/ \-*" SEARCH_DIRS_MASK="/mnt /home" revdep\-rebuild\fR
+
+To rebuild packages that depend on libkdecore.so.4 from KDE 3.3:
+.br 
+\fBrevdep\-rebuild \-\-library /usr/kde/3.3/lib/libkdecore.so.4\fR
+
+To rebuild packages that depend upon libImlib.so and libImlib2.so:
+.br 
+\fBrevdep\-rebuild \-\-library libImlib[2]*.so.*\fR
+
+.SH "FILES"
+.P
+revdep\-rebuild keeps several pseudo-temporary files in /var/cache/revdep\-rebuild/. Deleting these files can improve accuracy at the cost of speed:
+.TP 15
+.I 0_env.rr
+Contains environment variables
+.TP
+.I 1_files.rr
+Contains a list of files to search
+.TP
+.I 2_ldpath.rr
+Contains the LDPATH
+.TP
+.I 3_broken.rr
+Contains the list of broken files
+.TP
+.I 3_errors.rr
+Contains the ldd error output
+.TP
+.I 4_raw.rr
+Contains the raw list of packages
+.TP
+.I 4_owners.rr
+Contains the file owners
+.TP
+.I 4_pkgs.rr
+Contains the unsorted bare package names
+.TP
+.I 4_ebuilds.rr
+Contains the unsorted atoms
+.TP
+.I 5_order.rr
+Contains the sorted atoms
+.TP
+.I 6_status.rr
+Contains the ldd error output
+
+.SH "EXIT STATUS"
+revdep\-rebuild returns a zero exit status if it \fBand emerge\fR succeeds, and a nonzero exit status otherwise.
+.SH "BUGS"
+.LP 
+Report bugs to <http://bugs.gentoo.org>. Please do not report emerge failures caused by \-o or \-e. Please include your files from /var/cache/revdep\-rebuild/, your emerge \-\-info, and patches. ;)
+
+.SH "SEE ALSO"
+emerge(1), portage(5), regex(7)
diff --git a/gentoolkit/pylintrc b/gentoolkit/pylintrc
new file mode 100644 (file)
index 0000000..863a3f8
--- /dev/null
@@ -0,0 +1,321 @@
+# lint Python modules using external checkers.
+#
+# This is the main checker controlling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+ignore=.svn
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories (IRCWEF).
+#enable-msg-cat=
+
+# Disable all messages in the listed categories (IRCWEF).
+disable-msg-cat=I
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+# :W0704: *Except doesn't do anything*
+# :W0231: *__init__ method from base class %r is not called*
+# :R0903: *Too few public methods (%s/%s)*
+disable-msg=W0704,W0231,R0903
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=colorized
+
+# Include message's id in output
+include-ids=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=yes
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assignment
+#
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branches, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+# was no-docstring-rgx=__.*__
+no-docstring-rgx=_.*
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+# was attr-rgx=[a-z_][a-z0-9_]{2,30}$
+attr-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression which should only match correct argument names
+# was argument-rgx=[a-z_][a-z0-9_]{2,30}$
+argument-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression which should only match correct variable names
+# was variable-rgx=[a-z_][a-z0-9_]{2,30}$
+variable-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+#
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existent members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Maximum number of arguments for function / method
+# was max-args=5
+max-args=10
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+# was max-returns=6
+max-returns=10
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+# was max-attributes=7
+max-attributes=10
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='\t'
+#indent-string='    '
diff --git a/gentoolkit/pym/gentoolkit/__init__.py b/gentoolkit/pym/gentoolkit/__init__.py
new file mode 100644 (file)
index 0000000..03382b7
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+#
+# Copyright 2003-2004 Karl Trygve Kalleberg
+# Copyright 2003-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+"""Gentoolkit is a collection of administration scripts for Gentoo"""
+
+import sys
+
+CONFIG = {
+    # Color handling: -1: Use Portage settings, 0: Force off, 1: Force on
+    'color': -1,
+    # Guess piping output:
+    'piping': False if sys.stdout.isatty() else True,
+    # Set some defaults:
+    'quiet': False,
+    'debug': False
+}
+
+# vim: set ts=8 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/atom.py b/gentoolkit/pym/gentoolkit/atom.py
new file mode 100644 (file)
index 0000000..3523d60
--- /dev/null
@@ -0,0 +1,357 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Subclasses portage.dep.Atom to provide methods on a Gentoo atom string."""
+
+__all__ = ('Atom',)
+
+# =======
+# Imports
+# =======
+
+import weakref
+
+import portage
+
+from gentoolkit.cpv import CPV
+from gentoolkit.versionmatch import VersionMatch
+from gentoolkit import errors
+
+# =======
+# Classes
+# =======
+
+class Atom(portage.dep.Atom, CPV):
+       """Portage's Atom class with an improvements from pkgcore.
+
+       Gentoolkit's Atom is not backwards compatible with Portage's because we set
+       parts and combinations of parts of cpv as attributes on Atom.cpv instead of
+       putting them directly in Atom's namespace like Portage does, for one.
+       For example:
+       Gentoolkit.Atom: str(atom.cpv)     # cpv string
+                                        atom.cpv.category # category
+       Portage.Atom: atom.cpv      # cpv string
+                                 atom.category # category
+
+       Also, Portage's Atom.slot is a string, whereas
+       Gentoolkit's Atom.slot is a tuple as in pkgcore, since multiple slots are
+               OK
+
+       portage.dep.Atom provides the following instance variables:
+
+       @type operator: str
+       @ivar operator: one of ('=', '=*', '<', '>', '<=', '>=', '~', None)
+       @type cp: str
+       @ivar cp: cat/pkg
+       @type cpv: str
+       @ivar cpv: cat/pkg-ver (if ver)
+       @type slot: str or None (modified to tuple if not None)
+       @ivar slot: slot passed in as cpv:#
+       """
+
+       # Necessary for Portage versions < 2.1.7
+       _atoms = weakref.WeakValueDictionary()
+
+       def __init__(self, atom):
+               self.atom = atom
+               self.operator = self.blocker = self.use = self.slot = None
+
+               try:
+                       portage.dep.Atom.__init__(self, atom)
+               except portage.exception.InvalidAtom:
+                       raise errors.GentoolkitInvalidAtom(atom)
+
+               # Make operator compatible with intersects
+               if self.operator is None:
+                       self.operator = ''
+
+               self.cpv = CPV(self.cpv)
+
+               # use_conditional is USE flag condition for this Atom to be required:
+               # For: !build? ( >=sys-apps/sed-4.0.5 ), use_conditional = '!build'
+               self.use_conditional = None
+
+       def __eq__(self, other):
+               if not isinstance(other, self.__class__):
+                       err = "other isn't of %s type, is %s"
+                       raise TypeError(err % (self.__class__, other.__class__))
+
+               if self.operator != other.operator:
+                       return False
+
+               if self.cpv != other.cpv:
+                       return False
+
+               if bool(self.blocker) != bool(other.blocker):
+                       return False
+
+               if self.blocker and other.blocker:
+                       if self.blocker.overlap.forbid != other.blocker.overlap.forbid:
+                               return False
+
+               # Don't believe Portage has something like this
+               #c = cmp(self.negate_vers, other.negate_vers)
+               #if c:
+               #       return c
+
+               if self.slot != other.slot:
+                       return False
+
+               this_use = None
+               if self.use is not None:
+                       this_use = sorted(self.use.tokens)
+               that_use = None
+               if other.use is not None:
+                       that_use = sorted(other.use.tokens)
+               if this_use != that_use:
+                       return False
+
+               # Not supported by Portage Atom yet
+               #return cmp(self.repo_id, other.repo_id)
+               return True
+
+       def __ne__(self, other):
+               if not isinstance(other, self.__class__):
+                       err = "other isn't of %s type, is %s"
+                       raise TypeError(err % (self.__class__, other.__class__))
+
+               return not self == other
+
+       def __lt__(self, other):
+               if not isinstance(other, self.__class__):
+                       err = "other isn't of %s type, is %s"
+                       raise TypeError(err % (self.__class__, other.__class__))
+
+               if self.operator != other.operator:
+                       return self.operator < other.operator
+
+               if self.cpv != other.cpv:
+                       return self.cpv < other.cpv
+
+               if bool(self.blocker) != bool(other.blocker):
+                       # We want non blockers, then blockers, so only return True
+                       # if self.blocker is True and other.blocker is False.
+                       return bool(self.blocker) > bool(other.blocker)
+
+               if self.blocker and other.blocker:
+                       if self.blocker.overlap.forbid != other.blocker.overlap.forbid:
+                               # we want !! prior to !
+                               return (self.blocker.overlap.forbid <
+                                       other.blocker.overlap.forbid)
+
+               # Don't believe Portage has something like this
+               #c = cmp(self.negate_vers, other.negate_vers)
+               #if c:
+               #       return c
+
+               if self.slot != other.slot:
+                       return self.slot < other.slot
+
+               this_use = None
+               if self.use is not None:
+                       this_use = sorted(self.use.tokens)
+               that_use = None
+               if other.use is not None:
+                       that_use = sorted(other.use.tokens)
+               if this_use != that_use:
+                       return this_use < that_use
+
+               # Not supported by Portage Atom yet
+               #return cmp(self.repo_id, other.repo_id)
+
+               return False
+
+       def __gt__(self, other):
+               if not isinstance(other, self.__class__):
+                       err = "other isn't of %s type, is %s"
+                       raise TypeError(err % (self.__class__, other.__class__))
+
+               return not self <= other
+
+       def __le__(self, other):
+               if not isinstance(other, self.__class__):
+                       raise TypeError("other isn't of %s type, is %s" % (
+                               self.__class__, other.__class__)
+                       )
+               return self < other or self == other
+
+       def __ge__(self, other):
+               if not isinstance(other, self.__class__):
+                       raise TypeError("other isn't of %s type, is %s" % (
+                               self.__class__, other.__class__)
+                       )
+               return self > other or self == other
+
+       def __repr__(self):
+               uc = self.use_conditional
+               uc = "%s? " % uc if uc is not None else ''
+               return "<%s %r>" % (self.__class__.__name__, "%s%s" % (uc, self.atom))
+
+       def __setattr__(self, name, value):
+               object.__setattr__(self, name, value)
+
+       #R0911:121:Atom.intersects: Too many return statements (20/6)
+       #R0912:121:Atom.intersects: Too many branches (23/12)
+       # pylint: disable-msg=R0911,R0912
+       def intersects(self, other):
+               """Check if a passed in package atom "intersects" this atom.
+
+               Lifted from pkgcore.
+
+               Two atoms "intersect" if a package can be constructed that
+               matches both:
+                 - if you query for just "dev-lang/python" it "intersects" both
+                       "dev-lang/python" and ">=dev-lang/python-2.4"
+                 - if you query for "=dev-lang/python-2.4" it "intersects"
+                       ">=dev-lang/python-2.4" and "dev-lang/python" but not
+                       "<dev-lang/python-2.3"
+
+               @type other: L{gentoolkit.atom.Atom} or
+                       L{gentoolkit.versionmatch.VersionMatch}
+               @param other: other package to compare
+               @see: L{pkgcore.ebuild.atom}
+               """
+               # Our "cp" (cat/pkg) must match exactly:
+               if self.cpv.cp != other.cpv.cp:
+                       # Check to see if one is name only:
+                       # Avoid slow partitioning if we're definitely not matching
+                       # (yes, this is hackish, but it's faster):
+                       if self.cpv.cp[-1:] != other.cpv.cp[-1:]:
+                               return False
+
+                       if ((not self.cpv.category and self.cpv.name == other.cpv.name) or
+                               (not other.cpv.category and other.cpv.name == self.cpv.name)):
+                               return True
+                       return False
+
+               # Slot dep only matters if we both have one. If we do they
+               # must be identical:
+               this_slot = getattr(self, 'slot', None)
+               that_slot = getattr(other, 'slot', None)
+               if (this_slot is not None and that_slot is not None and
+                       this_slot != that_slot):
+                       return False
+
+               # TODO: Uncomment when Portage's Atom supports repo
+               #if (self.repo_id is not None and other.repo_id is not None and
+               #       self.repo_id != other.repo_id):
+               #       return False
+
+               # Use deps are similar: if one of us forces a flag on and the
+               # other forces it off we do not intersect. If only one of us
+               # cares about a flag it is irrelevant.
+
+               # Skip the (very common) case of one of us not having use deps:
+               this_use = getattr(self, 'use', None)
+               that_use = getattr(other, 'use', None)
+               if this_use and that_use:
+                       # Set of flags we do not have in common:
+                       flags = set(this_use.tokens) ^ set(that_use.tokens)
+                       for flag in flags:
+                               # If this is unset and we also have the set version we fail:
+                               if flag[0] == '-' and flag[1:] in flags:
+                                       return False
+
+        # Remaining thing to check is version restrictions. Get the
+        # ones we can check without actual version comparisons out of
+        # the way first.
+
+               # If one of us is unversioned we intersect:
+               if not self.operator or not other.operator:
+                       return True
+
+               # If we are both "unbounded" in the same direction we intersect:
+               if (('<' in self.operator and '<' in other.operator) or
+                       ('>' in self.operator and '>' in other.operator)):
+                       return True
+
+               # If one of us is an exact match we intersect if the other matches it:
+               if self.operator == '=':
+                       if other.operator == '=*':
+                               return self.cpv.fullversion.startswith(other.cpv.fullversion)
+                       return VersionMatch(other.cpv, op=other.operator).match(self.cpv)
+               if other.operator == '=':
+                       if self.operator == '=*':
+                               return other.cpv.fullversion.startswith(self.cpv.fullversion)
+                       return VersionMatch(self.cpv, op=self.operator).match(other.cpv)
+
+               # If we are both ~ matches we match if we are identical:
+               if self.operator == other.operator == '~':
+                       return (self.cpv.version == other.cpv.version and
+                               self.cpv.revision == other.cpv.revision)
+
+               # If we are both glob matches we match if one of us matches the other.
+               if self.operator == other.operator == '=*':
+                       return (self.cpv.fullversion.startswith(other.cpv.fullversion) or
+                               other.cpv.fullversion.startswith(self.cpv.fullversion))
+
+               # If one of us is a glob match and the other a ~ we match if the glob
+               # matches the ~ (ignoring a revision on the glob):
+               if self.operator == '=*' and other.operator == '~':
+                       return other.cpv.fullversion.startswith(self.cpv.version)
+               if other.operator == '=*' and self.operator == '~':
+                       return self.cpv.fullversion.startswith(other.cpv.version)
+
+               # If we get here at least one of us is a <, <=, > or >=:
+               if self.operator in ('<', '<=', '>', '>='):
+                       # pylint screwup:
+                       # E0601: Using variable 'ranged' before assignment
+                       # pylint: disable-msg=E0601
+                       ranged, ranged.operator = self, self.operator
+               else:
+                       ranged, ranged.operator = other, other.operator
+                       other, other.operator = self, self.operator
+
+               if '<' in other.operator or '>' in other.operator:
+                       # We are both ranged, and in the opposite "direction" (or
+                       # we would have matched above). We intersect if we both
+                       # match the other's endpoint (just checking one endpoint
+                       # is not enough, it would give a false positive on <=2 vs >2)
+                       return (
+                               VersionMatch(other.cpv, op=other.operator).match(ranged.cpv) and
+                               VersionMatch(ranged.cpv, op=ranged.operator).match(other.cpv)
+                       )
+
+               if other.operator == '~':
+                       # Other definitely matches its own version. If ranged also
+                       # does we're done:
+                       if VersionMatch(ranged.cpv, op=ranged.operator).match(other.cpv):
+                               return True
+                       # The only other case where we intersect is if ranged is a
+                       # > or >= on other's version and a nonzero revision. In
+                       # that case other will match ranged. Be careful not to
+                       # give a false positive for ~2 vs <2 here:
+                       return (ranged.operator in ('>', '>=') and
+                               VersionMatch(other.cpv, op=other.operator).match(ranged.cpv))
+
+               if other.operator == '=*':
+                       # a glob match definitely matches its own version, so if
+                       # ranged does too we're done:
+                       if VersionMatch(ranged.cpv, op=ranged.operator).match(other.cpv):
+                               return True
+                       if '<' in ranged.operator:
+                               # If other.revision is not defined then other does not
+                               # match anything smaller than its own fullversion:
+                               if other.cpv.revision:
+                                       return False
+
+                               # If other.revision is defined then we can always
+                               # construct a package smaller than other.fullversion by
+                               # tagging e.g. an _alpha1 on.
+                               return ranged.cpv.fullversion.startswith(other.cpv.version)
+                       else:
+                               # Remaining cases where this intersects: there is a
+                               # package greater than ranged.fullversion and
+                               # other.fullversion that they both match.
+                               return ranged.cpv.fullversion.startswith(other.cpv.version)
+
+               # Handled all possible ops.
+               raise NotImplementedError(
+                       'Someone added an operator without adding it to intersects')
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/cpv.py b/gentoolkit/pym/gentoolkit/cpv.py
new file mode 100644 (file)
index 0000000..9b3c2d7
--- /dev/null
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Provides attributes and methods for a category/package-version string."""
+
+__all__ = ('CPV',)
+
+# =======
+# Imports
+# =======
+
+from portage.versions import catpkgsplit, vercmp
+
+from gentoolkit import errors
+
+# =======
+# Classes
+# =======
+
+class CPV(object):
+       """Provides methods on a category/package-version string.
+
+       Will also correctly split just a package or package-version string.
+
+       Example usage:
+               >>> from gentoolkit.cpv import CPV
+               >>> cpv = CPV('sys-apps/portage-2.2-r1')
+               >>> cpv.category, cpv.name, cpv.fullversion
+               ('sys-apps', 'portage', '2.2-r1')
+               >>> str(cpv)
+               'sys-apps/portage-2.2-r1'
+               >>> # An 'rc' (release candidate) version is less than non 'rc' version:
+               ... CPV('sys-apps/portage-2') > CPV('sys-apps/portage-2_rc10')
+               True
+       """
+
+       def __init__(self, cpv):
+               self.scpv = 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:
+                       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
+
+       def __eq__(self, other):
+               if not isinstance(other, self.__class__):
+                       raise TypeError("other isn't of %s type, is %s" % (
+                               self.__class__, other.__class__)
+                       )
+               return self.scpv == other.scpv
+
+       def __ne__(self, other):
+               if not isinstance(other, self.__class__):
+                       raise TypeError("other isn't of %s type, is %s" % (
+                               self.__class__, other.__class__)
+                       )
+               return not self == other
+
+       def __lt__(self, other):
+               if not isinstance(other, self.__class__):
+                       raise TypeError("other isn't of %s type, is %s" % (
+                               self.__class__, other.__class__)
+                       )
+
+               if self.category != other.category:
+                       return self.category < other.category
+               elif self.name != other.name:
+                       return self.name < other.name
+               else:
+                       # FIXME: this cmp() hack is for vercmp not using -1,0,1
+                       # See bug 266493; this was fixed in portage-2.2_rc31
+                       #return vercmp(self.fullversion, other.fullversion)
+                       result = cmp(vercmp(self.fullversion, other.fullversion), 0)
+                       if result == -1:
+                               return True
+                       else:
+                               return False
+
+       def __gt__(self, other):
+               if not isinstance(other, self.__class__):
+                       raise TypeError("other isn't of %s type, is %s" % (
+                               self.__class__, other.__class__)
+                       )
+               return not self <= other
+
+       def __le__(self, other):
+               if not isinstance(other, self.__class__):
+                       raise TypeError("other isn't of %s type, is %s" % (
+                               self.__class__, other.__class__)
+                       )
+               return self < other or self == other
+
+       def __ge__(self, other):
+               if not isinstance(other, self.__class__):
+                       raise TypeError("other isn't of %s type, is %s" % (
+                               self.__class__, other.__class__)
+                       )
+               return self > other or self == other
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, str(self))
+
+       def __str__(self):
+               return self.scpv
+
+
+# =========
+# Functions
+# =========
+
+def split_cpv(cpv):
+       """Split a cpv into category, name, version and revision.
+
+       Inlined from helpers because of circular imports.
+
+       @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
+       @rtype: tuple
+       @return: (category, pkg_name, version, revision)
+               Each tuple element is a string or empty string ("").
+       """
+
+       result = catpkgsplit(cpv)
+
+       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)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/dbapi.py b/gentoolkit/pym/gentoolkit/dbapi.py
new file mode 100644 (file)
index 0000000..2866214
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+#
+# Copyright 2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+"""Provides access to Portage database api"""
+
+import portage
+
+#bindb = portage.db[portage.root]["bintree"].dbapi
+PORTDB = portage.db[portage.root]["porttree"].dbapi
+VARDB = portage.db[portage.root]["vartree"].dbapi
+#virtuals = portage.db[portage.root]["virtuals"]
+
+# vim: set ts=8 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/dependencies.py b/gentoolkit/pym/gentoolkit/dependencies.py
new file mode 100644 (file)
index 0000000..9ff7f90
--- /dev/null
@@ -0,0 +1,320 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Provides a class for easy calculating dependencies for a given CPV."""
+
+__docformat__ = 'epytext'
+__all__ = ('Dependencies',)
+
+# =======
+# Imports
+# =======
+
+import portage
+from portage.dep import paren_reduce
+
+from gentoolkit import errors
+from gentoolkit.atom import Atom
+from gentoolkit.cpv import CPV
+from gentoolkit.helpers import find_best_match, uniqify
+from gentoolkit.dbapi import PORTDB, VARDB
+
+# =======
+# Classes
+# =======
+
+class Dependencies(CPV):
+       """Access a package's dependencies and reverse dependencies.
+
+       Example usage:
+               >>> from gentoolkit.dependencies import Dependencies
+               >>> portage = Dependencies('sys-apps/portage-2.1.6.13')
+               >>> portage
+               <Dependencies 'sys-apps/portage-2.1.6.13'>
+               >>> # All methods return gentoolkit.atom.Atom instances
+               ... portage.get_depend()
+               [<Atom '>=dev-lang/python-2.5'>, <Atom '<dev-lang/python-3.0'>, ...]
+
+       """
+       def __init__(self, cpv, op='', parser=None):
+               if isinstance(cpv, CPV):
+                       self.cpv = cpv
+               else:
+                       self.cpv = CPV(cpv)
+
+               self.operator = op
+               self.atom = self.operator + str(self.cpv)
+               self.use = []
+               self.depatom = str()
+
+               # Allow a custom parser function:
+               self.parser = parser if parser else self._parser
+
+       def __eq__(self, other):
+               if self.atom != other.atom:
+                       return False
+               else:
+                       return True
+
+       def __ne__(self, other):
+               return not self == other
+
+       def __hash__(self):
+               return hash((self.atom, self.depatom, tuple(self.use)))
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, self.atom)
+
+       def environment(self, envvars):
+               """Returns predefined env vars DEPEND, SRC_URI, etc."""
+
+               # Try to use the Portage tree first, since emerge only uses the tree
+               # when calculating dependencies
+               try:
+                       result = PORTDB.aux_get(str(self.cpv), envvars)
+               except KeyError:
+                       result = VARDB.aux_get(str(self.cpv), envvars)
+               return result
+
+       def get_depend(self):
+               """Get the contents of DEPEND and parse it with self.parser."""
+
+               try:
+                       return self.parser(self.environment(('DEPEND',))[0])
+               except portage.exception.InvalidPackageName, err:
+                       raise errors.GentoolkitInvalidCPV(err)
+
+       def get_pdepend(self):
+               """Get the contents of PDEPEND and parse it with self.parser."""
+
+               try:
+                       return self.parser(self.environment(('PDEPEND',))[0])
+               except portage.exception.InvalidPackageName, err:
+                       raise errors.GentoolkitInvalidCPV(err)
+
+       def get_rdepend(self):
+               """Get the contents of RDEPEND and parse it with self.parser."""
+
+               try:
+                       return self.parser(self.environment(('RDEPEND',))[0])
+               except portage.exception.InvalidPackageName, err:
+                       raise errors.GentoolkitInvalidCPV(err)
+
+       def get_all_depends(self):
+               """Get the contents of ?DEPEND and parse it with self.parser."""
+
+               env_vars = ('DEPEND', 'PDEPEND', 'RDEPEND')
+               try:
+                       return self.parser(' '.join(self.environment(env_vars)))
+               except portage.exception.InvalidPackageName, err:
+                       raise errors.GentoolkitInvalidCPV(err)
+
+       def graph_depends(
+               self,
+               max_depth=1,
+               printer_fn=None,
+               # The rest of these are only used internally:
+               depth=0,
+               seen=None,
+               depcache=None,
+               result=None
+       ):
+               """Graph direct dependencies for self.
+
+               Optionally gather indirect dependencies.
+
+               @type max_depth: int
+               @keyword max_depth: Maximum depth to recurse if.
+                       <1 means no maximum depth
+                       >0 means recurse only this depth;
+               @type printer_fn: callable
+               @keyword printer_fn: If None, no effect. If set, it will be applied to
+                       each result.
+               @rtype: list
+               @return: [(depth, pkg), ...]
+               """
+               if seen is None:
+                       seen = set()
+               if depcache is None:
+                       depcache = dict()
+               if result is None:
+                       result = list()
+
+               pkgdep = None
+               deps = self.get_all_depends()
+               for dep in deps:
+                       if dep.atom in depcache:
+                               continue
+                       try:
+                               pkgdep = depcache[dep.atom]
+                       except KeyError:
+                               pkgdep = find_best_match(dep.atom)
+                               depcache[dep.atom] = pkgdep
+                       if pkgdep and str(pkgdep.cpv) in seen:
+                               continue
+                       if depth < max_depth or max_depth <= 0:
+
+                               if printer_fn is not None:
+                                       printer_fn(depth, pkgdep, dep)
+                               if not pkgdep:
+                                       continue
+
+                               seen.add(str(pkgdep.cpv))
+                               result.append((
+                                       depth,
+                                       pkgdep.deps.graph_depends(
+                                               max_depth=max_depth,
+                                               printer_fn=printer_fn,
+                                               # The rest of these are only used internally:
+                                               depth=depth+1,
+                                               seen=seen,
+                                               depcache=depcache,
+                                               result=result
+                                       )
+                               ))
+
+               if depth == 0:
+                       return result
+               return pkgdep
+
+       def graph_reverse_depends(
+               self,
+               pkgset=None,
+               max_depth=-1,
+               only_direct=True,
+               printer_fn=None,
+               # The rest of these are only used internally:
+               depth=0,
+               depcache=None,
+               seen=None,
+               result=None
+       ):
+               """Graph direct reverse dependencies for self.
+
+               Example usage:
+                       >>> from gentoolkit.dependencies import Dependencies
+                       >>> ffmpeg = Dependencies('media-video/ffmpeg-0.5_p20373')
+                       >>> # I only care about installed packages that depend on me:
+                       ... from gentoolkit.helpers import get_installed_cpvs
+                       >>> # I want to pass in a sorted list. We can pass strings or
+                       ... # Package or Atom types, so I'll use Package to sort:
+                       ... from gentoolkit.package import Package
+                       >>> installed = sorted(Package(x) for x in get_installed_cpvs())
+                       >>> deptree = ffmpeg.graph_reverse_depends(
+                       ...     only_direct=False,  # Include indirect revdeps
+                       ...     pkgset=installed)   # from installed pkgset
+                       >>> len(deptree)
+                       44
+
+               @type pkgset: iterable
+               @keyword pkgset: sorted pkg cpv strings or anything sublassing
+                       L{gentoolkit.cpv.CPV} to use for calculate our revdep graph.
+               @type max_depth: int
+               @keyword max_depth: Maximum depth to recurse if only_direct=False.
+                       -1 means no maximum depth;
+                        0 is the same as only_direct=True;
+                       >0 means recurse only this many times;
+               @type only_direct: bool
+               @keyword only_direct: to recurse or not to recurse
+               @type printer_fn: callable
+               @keyword printer_fn: If None, no effect. If set, it will be applied to
+                       each L{gentoolkit.atom.Atom} object as it is added to the results.
+               @rtype: list
+               @return: L{gentoolkit.dependencies.Dependencies} objects
+               """
+               if not pkgset:
+                       err = ("%s kwarg 'pkgset' must be set. "
+                               "Can be list of cpv strings or any 'intersectable' object.")
+                       raise errors.GentoolkitFatalError(err % (self.__class__.__name__,))
+
+               if depcache is None:
+                       depcache = dict()
+               if seen is None:
+                       seen = set()
+               if result is None:
+                       result = list()
+
+               if depth == 0:
+                       pkgset = tuple(Dependencies(x) for x in pkgset)
+
+               pkgdep = None
+               for pkgdep in pkgset:
+                       try:
+                               all_depends = depcache[pkgdep]
+                       except KeyError:
+                               all_depends = uniqify(pkgdep.get_all_depends())
+                               depcache[pkgdep] = all_depends
+
+                       dep_is_displayed = False
+                       for dep in all_depends:
+                               # TODO: Add ability to determine if dep is enabled by USE flag.
+                               #       Check portage.dep.use_reduce
+                               if dep.intersects(self):
+                                       pkgdep.depth = depth
+                                       pkgdep.matching_dep = dep
+                                       if printer_fn is not None:
+                                               printer_fn(pkgdep, dep_is_displayed=dep_is_displayed)
+                                       result.append(pkgdep)
+                                       dep_is_displayed = True
+
+                       # if --indirect specified, call ourselves again with the dep
+                       # Do not call if we have already called ourselves.
+                       if (
+                               dep_is_displayed and not only_direct and
+                               str(pkgdep.cpv) not in seen and
+                               (depth < max_depth or max_depth == -1)
+                       ):
+
+                               seen.add(str(pkgdep.cpv))
+                               result.append(
+                                       pkgdep.graph_reverse_depends(
+                                               pkgset=pkgset,
+                                               max_depth=max_depth,
+                                               only_direct=only_direct,
+                                               printer_fn=printer_fn,
+                                               depth=depth+1,
+                                               depcache=depcache,
+                                               seen=seen,
+                                               result=result
+                                       )
+                               )
+
+               if depth == 0:
+                       return result
+               return pkgdep
+
+       def _parser(self, deps, use_conditional=None, depth=0):
+               """?DEPEND file parser.
+
+               @rtype: list
+               @return: L{gentoolkit.atom.Atom} objects
+               """
+               result = []
+
+               if depth == 0:
+                       deps = paren_reduce(deps)
+               for tok in deps:
+                       if tok == '||':
+                               continue
+                       if tok[-1] == '?':
+                               use_conditional = tok[:-1]
+                               continue
+                       if isinstance(tok, list):
+                               sub_r = self._parser(tok, use_conditional, depth=depth+1)
+                               result.extend(sub_r)
+                               use_conditional = None
+                               continue
+                       if tok[0] == '!':
+                               # We're not interested in blockers
+                               continue
+                       atom = Atom(tok)
+                       if use_conditional is not None:
+                               atom.use_conditional = use_conditional
+                       result.append(atom)
+
+               return result
+
+# vim: set ts=4 sw=4 tw=0:
diff --git a/gentoolkit/pym/gentoolkit/deprecated/helpers.py b/gentoolkit/pym/gentoolkit/deprecated/helpers.py
new file mode 100644 (file)
index 0000000..d0fb993
--- /dev/null
@@ -0,0 +1,176 @@
+#!/usr/bin/python2
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+import warnings
+
+import portage
+from gentoolkit import *
+from package import *
+from pprinter import warn
+try:
+       from portage.util import unique_array
+except ImportError:
+       from portage_util import unique_array
+
+def find_packages(search_key, masked=False):
+       """Returns a list of Package objects that matched the search key."""
+       warnings.warn("Deprecated. Use helpers2.find_packages.", DeprecationWarning)
+       try:
+               if masked:
+                       t = portage.db["/"]["porttree"].dbapi.xmatch("match-all", search_key)
+                       t += portage.db["/"]["vartree"].dbapi.match(search_key)
+               else:
+                       t = portage.db["/"]["porttree"].dbapi.match(search_key)
+                       t += portage.db["/"]["vartree"].dbapi.match(search_key)
+       # catch the "amgigous package" Exception
+       except ValueError, e:
+               if isinstance(e[0],list):
+                       t = []
+                       for cp in e[0]:
+                               if masked:
+                                       t += portage.db["/"]["porttree"].dbapi.xmatch("match-all", cp)
+                                       t += portage.db["/"]["vartree"].dbapi.match(cp)
+                               else:
+                                       t += portage.db["/"]["porttree"].dbapi.match(cp)
+                                       t += portage.db["/"]["vartree"].dbapi.match(cp)
+               else:
+                       raise ValueError(e)
+       except portage_exception.InvalidAtom, e:
+               print warn("Invalid Atom: '%s'" % str(e))
+               return []
+       # Make the list of packages unique
+       t = unique_array(t)
+       t.sort()
+       return [Package(x) for x in t]
+
+def find_installed_packages(search_key, masked=False):
+       """Returns a list of Package objects that matched the search key."""
+       warnings.warn("Deprecated. Use helpers2.find_installed_packages.",
+               DeprecationWarning)
+       try:
+                       t = portage.db["/"]["vartree"].dbapi.match(search_key)
+       # catch the "amgigous package" Exception
+       except ValueError, e:
+               if isinstance(e[0],list):
+                       t = []
+                       for cp in e[0]:
+                               t += portage.db["/"]["vartree"].dbapi.match(cp)
+               else:
+                       raise ValueError(e)
+       except portage_exception.InvalidAtom, e:
+               print warn("Invalid Atom: '%s'" % str(e))
+               return []
+       return [Package(x) for x in t]
+
+def find_best_match(search_key):
+       """Returns a Package object for the best available candidate that
+       matched the search key."""
+       warnings.warn("Deprecated. Use helpers2.find_best_match.",
+               DeprecationWarning)
+       t = portage.db["/"]["porttree"].dep_bestmatch(search_key)
+       if t:
+               return Package(t)
+       return None
+
+def find_system_packages(prefilter=None):
+       """Returns a tuple of lists, first list is resolved system packages,
+       second is a list of unresolved packages."""
+       pkglist = settings.packages
+       resolved = []
+       unresolved = []
+       for x in pkglist:
+               cpv = x.strip()
+               if len(cpv) and cpv[0] == "*":
+                       pkg = find_best_match(cpv)
+                       if pkg:
+                               resolved.append(pkg)
+                       else:
+                               unresolved.append(cpv)
+       return (resolved, unresolved)
+
+def find_world_packages(prefilter=None):
+       """Returns a tuple of lists, first list is resolved world packages,
+       seond is unresolved package names."""
+       f = open(portage.root+portage.WORLD_FILE)
+       pkglist = f.readlines()
+       resolved = []
+       unresolved = []
+       for x in pkglist:
+               cpv = x.strip()
+               if len(cpv) and cpv[0] != "#":
+                       pkg = find_best_match(cpv)
+                       if pkg:
+                               resolved.append(pkg)
+                       else:
+                               unresolved.append(cpv)
+       return (resolved,unresolved)
+
+def find_all_installed_packages(prefilter=None):
+       """Returns a list of all installed packages, after applying the prefilter
+       function"""
+       warnings.warn("Deprecated. Use helpers2.get_installed_cpvs.",
+               DeprecationWarning)
+       t = vartree.dbapi.cpv_all()
+       if prefilter:
+               t = filter(prefilter,t)
+       return [Package(x) for x in t]
+
+def find_all_uninstalled_packages(prefilter=None):
+       """Returns a list of all uninstalled packages, after applying the prefilter
+       function"""
+       warnings.warn("Deprecated. Use helpers2.get_uninstalled_cpvs.",
+               DeprecationWarning)
+       alist = find_all_packages(prefilter)
+       return [x for x in alist if not x.is_installed()]
+
+def find_all_packages(prefilter=None):
+       """Returns a list of all known packages, installed or not, after applying
+       the prefilter function"""
+       warnings.warn("Deprecated. Use helpers2.get_cpvs.", DeprecationWarning)
+       t = porttree.dbapi.cp_all()
+       t += vartree.dbapi.cp_all()
+       if prefilter:
+               t = filter(prefilter,t)
+       t = unique_array(t)
+       t2 = []
+       for x in t:
+               t2 += porttree.dbapi.cp_list(x)
+               t2 += vartree.dbapi.cp_list(x)
+       t2 = unique_array(t2)
+       return [Package(x) for x in t2]
+
+def split_package_name(name):
+       """Returns a list on the form [category, name, version, revision]. Revision will
+       be 'r0' if none can be inferred. Category and version will be empty, if none can
+       be inferred."""
+       warnings.warn("Deprecated. Just use portage.catpkgsplit or apply "
+               "gentoolkit.package.Package to access pkg.category, pkg.revision, etc.",
+               DeprecationWarning)
+       r = portage.catpkgsplit(name)
+       if not r:
+               r = name.split("/")
+               if len(r) == 1:
+                       return ["", name, "", "r0"]
+               else:
+                       return r + ["", "r0"]
+       else:
+               r = list(r)
+       if r[0] == 'null':
+               r[0] = ''
+       return r
+
+# XXX: Defunct: use helpers2.compare_package_strings
+#def sort_package_list(pkglist):
+#      """Returns the list ordered in the same way portage would do with lowest version
+#      at the head of the list."""
+#      pkglist.sort(Package.compare_version)
+#      return pkglist
+
+if __name__ == "__main__":
+       print "This module is for import only"
diff --git a/gentoolkit/pym/gentoolkit/equery/__init__.py b/gentoolkit/pym/gentoolkit/equery/__init__.py
new file mode 100644 (file)
index 0000000..84e8ced
--- /dev/null
@@ -0,0 +1,351 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Gentoo package query tool"""
+
+# Move to Imports section after Python 2.6 is stable
+from __future__ import with_statement
+
+__all__ = (
+       'format_options',
+       'format_package_names',
+       'mod_usage'
+)
+__docformat__ = 'epytext'
+# version is dynamically set by distutils sdist
+__version__ = "svn"
+
+# =======
+# Imports
+# =======
+
+import errno
+import os
+import sys
+import time
+from getopt import getopt, GetoptError
+
+import portage
+
+import gentoolkit
+from gentoolkit import CONFIG
+from gentoolkit import errors
+from gentoolkit import pprinter as pp
+from gentoolkit.textwrap_ import TextWrapper
+
+__productname__ = "equery"
+__authors__ = (
+       'Karl Trygve Kalleberg - Original author',
+       'Douglas Anderson - 0.3.0 author'
+)
+
+# =======
+# Globals
+# =======
+
+NAME_MAP = {
+       'b': 'belongs',
+       'c': 'changes',
+       'k': 'check',
+       'd': 'depends',
+       'g': 'depgraph',
+       'f': 'files',
+       'h': 'hasuse',
+       'l': 'list_',
+       'm': 'meta',
+       's': 'size',
+       'u': 'uses',
+       'w': 'which'
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @param with_description (bool): Option to print module's __doc__ or not
+       """
+
+       if with_description:
+               print __doc__
+       print main_usage()
+       print
+       print pp.globaloption("global options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -q, --quiet", "minimal output"),
+               (" -C, --no-color", "turn off colors"),
+               (" -N, --no-pipe", "turn off pipe detection"),
+               (" -V, --version", "display version info")
+       ))
+       print
+       print pp.command("modules") + " (" + pp.command("short name") + ")"
+       print format_options((
+               (" (b)elongs", "list what package FILES belong to"),
+               (" (c)hanges", "list changelog entries for ATOM"),
+               (" chec(k)", "verify checksums and timestamps for PKG"),
+               (" (d)epends", "list all packages directly depending on ATOM"),
+               (" dep(g)raph", "display a tree of all dependencies for PKG"),
+               (" (f)iles", "list all files installed by PKG"),
+               (" (h)asuse", "list all packages that have USE flag"),
+               (" (l)ist", "list package matching PKG"),
+               (" (m)eta", "display metadata about PKG"),
+               (" (s)ize", "display total size of all files owned by PKG"),
+               (" (u)ses", "display USE flags for PKG"),
+               (" (w)hich", "print full path to ebuild for PKG")
+       ))
+
+
+def expand_module_name(module_name):
+       """Returns one of the values of NAME_MAP or raises KeyError"""
+
+       if module_name == 'list':
+               # list is a Python builtin type, so we must rename our module
+               return 'list_'
+       elif module_name in NAME_MAP.values():
+               return module_name
+       else:
+               return NAME_MAP[module_name]
+
+
+def format_options(options):
+       """Format module options.
+
+       @type options: list
+       @param options: [('option 1', 'description 1'), ('option 2', 'des... )]
+       @rtype: str
+       @return: formatted options string
+       """
+
+       result = []
+       twrap = TextWrapper(width=CONFIG['termWidth'])
+       opts = (x[0] for x in options)
+       descs = (x[1] for x in options)
+       for opt, desc in zip(opts, descs):
+               twrap.initial_indent = pp.emph(opt.ljust(25))
+               twrap.subsequent_indent = " " * 25
+               result.append(twrap.fill(desc))
+
+       return '\n'.join(result)
+
+
+def format_filetype(path, fdesc, show_type=False, show_md5=False,
+               show_timestamp=False):
+       """Format a path for printing.
+
+       @type path: str
+       @param path: the path
+       @type fdesc: list
+       @param fdesc: [file_type, timestamp, MD5 sum/symlink target]
+               file_type is one of dev, dir, obj, sym.
+               If file_type is dir, there is no timestamp or MD5 sum.
+               If file_type is sym, fdesc[2] is the target of the symlink.
+       @type show_type: bool
+       @param show_type: if True, prepend the file's type to the formatted string
+       @type show_md5: bool
+       @param show_md5: if True, append MD5 sum to the formatted string
+       @type show_timestamp: bool
+       @param show_timestamp: if True, append time-of-creation after pathname
+       @rtype: str
+       @return: formatted pathname with optional added information
+       """
+
+       ftype = fpath = stamp = md5sum = ""
+
+       if fdesc[0] == "obj":
+               ftype = "file"
+               fpath = path
+               stamp = format_timestamp(fdesc[1])
+               md5sum = fdesc[2]
+       elif fdesc[0] == "dir":
+               ftype = "dir"
+               fpath = pp.path(path)
+       elif fdesc[0] == "sym":
+               ftype = "sym"
+               stamp = format_timestamp(fdesc[1])
+               tgt = fdesc[2].split()[0]
+               if CONFIG["piping"]:
+                       fpath = path
+               else:
+                       fpath = pp.path_symlink(path + " -> " + tgt)
+       elif fdesc[0] == "dev":
+               ftype = "dev"
+               fpath = path
+       else:
+               sys.stderr.write(
+                       pp.error("%s has unknown type: %s" % (path, fdesc[0]))
+               )
+
+       result = ""
+       if show_type:
+               result += "%4s " % ftype
+       result += fpath
+       if show_timestamp:
+               result += "  " + stamp
+       if show_md5:
+               result += "  " + md5sum
+
+       return result
+
+
+def format_timestamp(timestamp):
+       """Format a timestamp into, e.g., '2009-01-31 21:19:44' format"""
+
+       return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(timestamp)))
+
+
+def initialize_configuration():
+       """Setup the standard equery config"""
+
+       # Get terminal size
+       term_width = pp.output.get_term_size()[1]
+       if term_width == -1:
+               # get_term_size() failed. Set a sane default width:
+               term_width = 80
+
+       # Terminal size, minus a 1-char margin for text wrapping
+       CONFIG['termWidth'] = term_width - 1
+
+       # Guess color output
+       if (CONFIG['color'] == -1 and (not sys.stdout.isatty() or
+               os.getenv("NOCOLOR") in ("yes", "true")) or CONFIG['color'] == 0):
+               pp.output.nocolor()
+
+       CONFIG['verbose'] = not CONFIG['piping']
+
+
+def main_usage():
+       """Return the main usage message for equery"""
+
+       return "%(usage)s %(product)s [%(g_opts)s] %(mod_name)s [%(mod_opts)s]" % {
+               'usage': pp.emph("Usage:"),
+               'product': pp.productname(__productname__),
+               'g_opts': pp.globaloption("global-options"),
+               'mod_name': pp.command("module-name"),
+               'mod_opts': pp.localoption("module-options")
+       }
+
+
+def mod_usage(mod_name="module", arg="pkgspec", optional=False):
+       """Provide a consistent usage message to the calling module.
+
+       @type arg: string
+       @param arg: what kind of argument the module takes (pkgspec, filename, etc)
+       @type optional: bool
+       @param optional: is the argument optional?
+       """
+
+       return "%(usage)s: %(mod_name)s [%(opts)s] %(arg)s" % {
+               'usage': pp.emph("Usage"),
+               'mod_name': pp.command(mod_name),
+               'opts': pp.localoption("options"),
+               'arg': ("[%s]" % pp.emph(arg)) if optional else pp.emph(arg)
+       }
+
+
+def parse_global_options(global_opts, args):
+       """Parse global input args and return True if we should display help for
+       the called module, else False (or display help and exit from here).
+       """
+
+       need_help = False
+       opts = (opt[0] for opt in global_opts)
+       for opt in opts:
+               if opt in ('-h', '--help'):
+                       if args:
+                               need_help = True
+                       else:
+                               print_help()
+                               sys.exit(0)
+               elif opt in ('-q','--quiet'):
+                       CONFIG['quiet'] = True
+               elif opt in ('-C', '--no-color', '--nocolor'):
+                       CONFIG['color'] = 0
+                       pp.output.nocolor()
+               elif opt in ('-N', '--no-pipe'):
+                       CONFIG['piping'] = False
+                       CONFIG['verbose'] = True
+               elif opt in ('-V', '--version'):
+                       print_version()
+                       sys.exit(0)
+               elif opt in ('--debug'):
+                       CONFIG['debug'] = True
+
+       return need_help
+
+
+def print_version():
+       """Print the version of this tool to the console."""
+
+       print "%(product)s (%(version)s) - %(docstring)s" % {
+               "product": pp.productname(__productname__),
+               "version": __version__,
+               "docstring": __doc__
+       }
+
+
+def split_arguments(args):
+       """Separate module name from module arguments"""
+
+       return args.pop(0), args
+
+
+def main():
+       """Parse input and run the program."""
+
+       short_opts = "hqCNV"
+       long_opts = (
+               'help', 'quiet', 'nocolor', 'no-color', 'no-pipe', 'version', 'debug'
+       )
+
+       initialize_configuration()
+
+       try:
+               global_opts, args = getopt(sys.argv[1:], short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Global %s" % err))
+               print_help(with_description=False)
+               sys.exit(2)
+
+       # Parse global options
+       need_help = parse_global_options(global_opts, args)
+
+       # FIXME: There are a few places that make use of both quiet and verbose.
+       #        Consider combining.
+       if CONFIG['quiet']:
+               CONFIG['verbose'] = False
+
+       try:
+               module_name, module_args = split_arguments(args)
+       except IndexError:
+               print_help()
+               sys.exit(2)
+
+       if need_help:
+               module_args.append('--help')
+
+       try:
+               expanded_module_name = expand_module_name(module_name)
+       except KeyError:
+               sys.stderr.write(pp.error("Unknown module '%s'" % module_name))
+               print_help(with_description=False)
+               sys.exit(2)
+
+       try:
+               loaded_module = __import__(
+                       expanded_module_name, globals(), locals(), [], -1
+               )
+               loaded_module.main(module_args)
+       except portage.exception.AmbiguousPackageName, err:
+               raise errors.GentoolkitAmbiguousPackage(err)
+       except IOError, err:
+               if err.errno != errno.EPIPE:
+                       raise
+
+if __name__ == '__main__':
+       main()
diff --git a/gentoolkit/pym/gentoolkit/equery/belongs.py b/gentoolkit/pym/gentoolkit/equery/belongs.py
new file mode 100644 (file)
index 0000000..fcf58ab
--- /dev/null
@@ -0,0 +1,156 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""List all packages owning a particular file
+
+Note: Normally, only one package will own a file. If multiple packages own
+      the same file, it usually constitutes a problem, and should be reported.
+"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit.equery import (format_filetype, format_options, mod_usage,
+       CONFIG)
+from gentoolkit.helpers import FileOwner
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "fullRegex": False,
+       "earlyOut": False,
+       "nameOnly": False
+}
+
+# =======
+# Classes
+# =======
+
+class BelongsPrinter(object):
+       """Outputs a formatted list of packages that claim to own a files."""
+
+       def __init__(self, verbose=True, name_only=False):
+               if verbose:
+                       self.print_fn = self.print_verbose
+               else:
+                       self.print_fn = self.print_quiet
+
+               self.name_only = name_only
+
+       def __call__(self, pkg, cfile):
+               self.print_fn(pkg, cfile)
+
+       # W0613: *Unused argument %r*
+       # pylint: disable-msg=W0613
+       def print_quiet(self, pkg, cfile):
+               "Format for minimal output."
+               if self.name_only:
+                       name = pkg.cpv.cp
+               else:
+                       name = str(pkg.cpv)
+               print name
+
+       def print_verbose(self, pkg, cfile):
+               "Format for full output."
+               file_str = pp.path(format_filetype(cfile, pkg.parsed_contents()[cfile]))
+               if self.name_only:
+                       name = pkg.cpv.cp
+               else:
+                       name = str(pkg.cpv)
+               print pp.cpv(name), "(" + file_str + ")"
+
+
+# =========
+# Functions
+# =========
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       for opt in opts:
+               if opt in ('-h','--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-e', '--early-out', '--earlyout'):
+                       if opt == '--earlyout':
+                               sys.stderr.write(pp.warn("Use of --earlyout is deprecated."))
+                               sys.stderr.write(pp.warn("Please use --early-out."))
+                               print
+                       QUERY_OPTS['earlyOut'] = True
+               elif opt in ('-f', '--full-regex'):
+                       QUERY_OPTS['fullRegex'] = True
+               elif opt in ('-n', '--name-only'):
+                       QUERY_OPTS['nameOnly'] = True
+
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       print mod_usage(mod_name="belongs", arg="filename")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -f, --full-regex", "supplied query is a regex" ),
+               (" -e, --early-out", "stop when first match is found"),
+               (" -n, --name-only", "don't print the version")
+       ))
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "h:fen"
+       long_opts = ('help', 'full-regex', 'early-out', 'earlyout',
+               'name-only')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       if CONFIG['verbose']:
+               print " * Searching for %s ... " % (pp.regexpquery(",".join(queries)))
+
+       printer_fn = BelongsPrinter(
+               verbose=CONFIG['verbose'], name_only=QUERY_OPTS['nameOnly']
+       )
+
+       find_owner = FileOwner(
+               is_regex=QUERY_OPTS['fullRegex'],
+               early_out=QUERY_OPTS['earlyOut'],
+               printer_fn=printer_fn
+       )
+
+       find_owner(queries)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/changes.py b/gentoolkit/pym/gentoolkit/equery/changes.py
new file mode 100644 (file)
index 0000000..28611f9
--- /dev/null
@@ -0,0 +1,205 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""Displays the ChangeLog entry for the latest installable version of an atom"""
+
+# Move to Imports sections when Python 2.6 is stable
+from __future__ import with_statement
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.atom import Atom
+from gentoolkit.equery import format_options, mod_usage
+from gentoolkit.helpers import ChangeLog, find_best_match, find_packages
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       'onlyLatest': False,
+       'showFullLog': False,
+       'limit': None,
+       'from': None,
+       'to': None
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       print mod_usage(mod_name="changes")
+       print
+       print pp.emph("examples")
+       print (" c portage                                # show latest visible "
+              "version's entry")
+       print " c portage --full --limit=3               # show 3 latest entries"
+       print " c '=sys-apps/portage-2.1.6*'             # use atom syntax"
+       print " c portage --from=2.2_rc20 --to=2.2_rc30  # use version ranges"
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -l, --latest", "display only the latest ChangeLog entry"),
+               (" -f, --full", "display the full ChangeLog"),
+               ("     --limit=NUM",
+                       "limit the number of entries displayed (with --full)"),
+               ("     --from=VER", "set which version to display from"),
+               ("     --to=VER", "set which version to display to"),
+       ))
+
+
+def get_match(query):
+       """Find a valid package from which to get the ChangeLog path.
+
+       @raise GentoolkitNoMatches: if no matches found
+       """
+
+       match = matches = None
+       match = find_best_match(query)
+
+       if not match:
+               matches = find_packages(query, include_masked=True)
+       else:
+               matches = [match]
+
+       if not matches:
+               raise errors.GentoolkitNoMatches(query)
+
+       return matches[0]
+
+
+def is_ranged(atom):
+       """Return True if an atom string appears to be ranged, else False."""
+
+       return atom.startswith(('~', '<', '>')) or atom.endswith('*')
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       posargs = (x[1] for x in module_opts)
+       for opt, posarg in zip(opts, posargs):
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-f', '--full'):
+                       QUERY_OPTS['showFullLog'] = True
+               elif opt in ('-l', '--latest'):
+                       QUERY_OPTS['onlyLatest'] = True
+               elif opt in ('--limit',):
+                       set_limit(posarg)
+               elif opt in ('--from',):
+                       QUERY_OPTS['from'] = posarg
+               elif opt in ('--to',):
+                       QUERY_OPTS['to'] = posarg
+
+
+def print_entries(entries):
+       """Print entries and strip trailing whitespace from the last entry."""
+
+       len_entries = len(entries)
+       for i, entry in enumerate(entries):    # , start=1): in py2.6
+               i += 1
+               if i < len_entries:
+                       print entry
+               else:
+                       print entry.strip()
+
+
+def set_limit(posarg):
+       """Set a limit in QUERY_OPTS on how many ChangeLog entries to display.
+
+       Die if posarg is not an integer.
+       """
+
+       if posarg.isdigit():
+               QUERY_OPTS['limit'] = int(posarg)
+       else:
+               err = "Module option --limit requires integer (got '%s')"
+               sys.stderr.write(pp.error(err % posarg))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "hlf"
+       long_opts = ('help', 'full', 'from=', 'latest', 'limit=', 'to=')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       first_run = True
+       for query in queries:
+               if not first_run:
+                       print
+
+               match = get_match(query)
+               changelog_path = os.path.join(match.package_path(), 'ChangeLog')
+               changelog = ChangeLog(changelog_path)
+
+               #
+               # Output
+               #
+
+               if (QUERY_OPTS['onlyLatest'] or (
+                       changelog.entries and not changelog.indexed_entries
+               )):
+                       print changelog.latest.strip()
+               else:
+                       end = QUERY_OPTS['limit'] or len(changelog.indexed_entries)
+                       if QUERY_OPTS['to'] or QUERY_OPTS['from']:
+                               print_entries(
+                                       changelog.entries_matching_range(
+                                               from_ver=QUERY_OPTS['from'],
+                                               to_ver=QUERY_OPTS['to']
+                                       )[:end]
+                               )
+                       elif QUERY_OPTS['showFullLog']:
+                               print_entries(changelog.full[:end])
+                       else:
+                               # Raises GentoolkitInvalidAtom here if invalid
+                               atom = Atom(query) if is_ranged(query) else '=' + str(match.cpv)
+                               print_entries(changelog.entries_matching_atom(atom)[:end])
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/check.py b/gentoolkit/pym/gentoolkit/equery/check.py
new file mode 100644 (file)
index 0000000..6ce288c
--- /dev/null
@@ -0,0 +1,291 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Checks timestamps and MD5 sums for files owned by a given installed package"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from functools import partial
+from getopt import gnu_getopt, GetoptError
+
+import portage.checksum as checksum
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "includeInstalled": True,
+       "includeOverlayTree": False,
+       "includePortTree": False,
+       "checkMD5sum": True,
+       "checkTimestamp" : True,
+       "isRegex": False,
+       "onlyFailures": False,
+       "printMatchInfo": False,
+       "showSummary" : True,
+       "showPassedFiles" : False,
+       "showFailedFiles" : True
+}
+
+# =======
+# Classes
+# =======
+
+class VerifyContents(object):
+       """Verify installed packages' CONTENTS files.
+
+       The CONTENTS file contains timestamps and MD5 sums for each file owned
+       by a package.
+       """
+       def __init__(self, printer_fn=None):
+               """Create a VerifyObjects instance.
+
+               @type printer_fn: callable
+               @param printer_fn: if defined, will be applied to each result as found
+               """
+               self.check_sums = True
+               self.check_timestamps = True
+               self.printer_fn = printer_fn
+
+               self.is_regex = False
+
+       def __call__(
+               self,
+               pkgs,
+               is_regex=False,
+               check_sums=True,
+               check_timestamps=True
+       ):
+               self.is_regex = is_regex
+               self.check_sums = check_sums
+               self.check_timestamps = check_timestamps
+
+               result = {}
+               for pkg in pkgs:
+                       # _run_checks returns tuple(n_passed, n_checked, err)
+                       check_results = self._run_checks(pkg.parsed_contents())
+                       result[pkg.cpv] = check_results
+                       if self.printer_fn is not None:
+                               self.printer_fn(pkg.cpv, check_results)
+
+               return result
+
+       def _run_checks(self, files):
+               """Run some basic sanity checks on a package's contents.
+
+               If the file type (ftype) is not a directory or symlink, optionally
+               verify MD5 sums or mtimes via L{self._verify_obj}.
+
+               @see: gentoolkit.packages.get_contents()
+               @type files: dict
+               @param files: in form {'PATH': ['TYPE', 'TIMESTAMP', 'MD5SUM']}
+               @rtype: tuple
+               @return:
+                       n_passed (int): number of files that passed all checks
+                       n_checked (int): number of files checked
+                       errs (list): check errors' descriptions
+               """
+               n_checked = 0
+               n_passed = 0
+               errs = []
+               for cfile in files:
+                       n_checked += 1
+                       ftype = files[cfile][0]
+                       if not os.path.exists(cfile):
+                               errs.append("%s does not exist" % cfile)
+                               continue
+                       elif ftype == "dir":
+                               if not os.path.isdir(cfile):
+                                       err = "%(cfile)s exists, but is not a directory"
+                                       errs.append(err % locals())
+                                       continue
+                       elif ftype == "obj":
+                               obj_errs = self._verify_obj(files, cfile, errs)
+                               if len(obj_errs) > len(errs):
+                                       errs = obj_errs[:]
+                                       continue
+                       elif ftype == "sym":
+                               target = files[cfile][2].strip()
+                               if not os.path.islink(cfile):
+                                       err = "%(cfile)s exists, but is not a symlink"
+                                       errs.append(err % locals())
+                                       continue
+                               tgt = os.readlink(cfile)
+                               if tgt != target:
+                                       err = "%(cfile)s does not point to %(target)s"
+                                       errs.append(err % locals())
+                                       continue
+                       else:
+                               err = "%(cfile)s has unknown type %(ftype)s"
+                               errs.append(err % locals())
+                               continue
+                       n_passed += 1
+
+               return n_passed, n_checked, errs
+
+       def _verify_obj(self, files, cfile, errs):
+               """Verify the MD5 sum and/or mtime and return any errors."""
+
+               obj_errs = errs[:]
+               if self.check_sums:
+                       md5sum = files[cfile][2]
+                       try:
+                               cur_checksum = checksum.perform_md5(cfile, calc_prelink=1)
+                       except IOError:
+                               err = "Insufficient permissions to read %(cfile)s"
+                               obj_errs.append(err % locals())
+                               return obj_errs
+                       if cur_checksum != md5sum:
+                               err = "%(cfile)s has incorrect MD5sum"
+                               obj_errs.append(err % locals())
+                               return obj_errs
+               if self.check_timestamps:
+                       mtime = int(files[cfile][1])
+                       st_mtime = int(os.lstat(cfile).st_mtime)
+                       if st_mtime != mtime:
+                               err = (
+                                       "%(cfile)s has wrong mtime (is %(st_mtime)d, should be "
+                                       "%(mtime)d)"
+                               )
+                               obj_errs.append(err % locals())
+                               return obj_errs
+
+               return obj_errs
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+
+       # Deprecation warning added by djanderson, 12/2008
+       depwarning = (
+               "Default action for this module has changed in Gentoolkit 0.3.",
+               "Use globbing to simulate the old behavior (see man equery).",
+               "Use '*' to check all installed packages.",
+               "Use 'foo-bar/*' to filter by category."
+       )
+       for line in depwarning:
+               sys.stderr.write(pp.warn(line))
+       print
+
+       print mod_usage(mod_name="check")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -f, --full-regex", "query is a regular expression"),
+               (" -o, --only-failures", "only display packages that do not pass"),
+       ))
+
+
+def checks_printer(cpv, data, verbose=True, only_failures=False):
+       """Output formatted results of pkg file(s) checks"""
+       seen = []
+
+       n_passed, n_checked, errs = data
+       n_failed = n_checked - n_passed
+       if only_failures and not n_failed:
+               return
+       else:
+               if verbose:
+                       if not cpv in seen:
+                               print "* Checking %s ..." % (pp.emph(str(cpv)))
+                               seen.append(cpv)
+               else:
+                       print "%s:" % cpv,
+
+       if verbose:
+               for err in errs:
+                       sys.stderr.write(pp.error(err))
+
+       if verbose:
+               n_passed = pp.number(str(n_passed))
+               n_checked = pp.number(str(n_checked))
+               info = "   %(n_passed)s out of %(n_checked)s files passed"
+               print info % locals()
+       else:
+               print "failed(%s)" % n_failed
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       for opt in opts:
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-f', '--full-regex'):
+                       QUERY_OPTS['isRegex'] = True
+               elif opt in ('-o', '--only-failures'):
+                       QUERY_OPTS['onlyFailures'] = True
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "hof"
+       long_opts = ('help', 'only-failures', 'full-regex')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       first_run = True
+       for query in queries:
+               if not first_run:
+                       print
+
+               matches = do_lookup(query, QUERY_OPTS)
+
+               if not matches:
+                       raise errors.GentoolkitNoMatches(query, in_installed=True)
+
+               matches.sort()
+
+               printer = partial(
+                       checks_printer,
+                       verbose=CONFIG['verbose'],
+                       only_failures=QUERY_OPTS['onlyFailures']
+               )
+               check = VerifyContents(printer_fn=printer)
+               check(matches)
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/depends.py b/gentoolkit/pym/gentoolkit/equery/depends.py
new file mode 100644 (file)
index 0000000..759ecba
--- /dev/null
@@ -0,0 +1,193 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""List all packages that depend on a atom given query"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit.dependencies import Dependencies
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import (get_cpvs, get_installed_cpvs,
+       compare_package_strings)
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "includeMasked": False,
+       "onlyDirect": True,
+       "maxDepth": -1,
+}
+
+# =======
+# Classes
+# =======
+
+class DependPrinter(object):
+       """Output L{gentoolkit.dependencies.Dependencies} objects."""
+       def __init__(self, verbose=True):
+               if verbose:
+                       self.print_fn = self.print_verbose
+               else:
+                       self.print_fn = self.print_quiet
+
+       def __call__(self, dep, dep_is_displayed=False):
+               self.format_depend(dep, dep_is_displayed)
+
+       @staticmethod
+       def print_verbose(indent, cpv, use_conditional, depatom):
+               """Verbosely prints a set of dep strings."""
+
+               sep = ' ? ' if (depatom and use_conditional) else ''
+               print indent + pp.cpv(cpv), "(" + use_conditional + sep + depatom + ")"
+
+       # W0613: *Unused argument %r*
+       # pylint: disable-msg=W0613
+       @staticmethod
+       def print_quiet(indent, cpv, use_conditional, depatom):
+               """Quietly prints a subset set of dep strings."""
+
+               print indent + pp.cpv(cpv)
+
+       def format_depend(self, dep, dep_is_displayed):
+               """Format a dependency for printing.
+
+               @type dep: L{gentoolkit.dependencies.Dependencies}
+               @param dep: the dependency to display
+               """
+
+               depth = getattr(dep, 'depth', 0)
+               indent = " " * depth
+               mdep = dep.matching_dep
+               use_conditional = ""
+               if mdep.use_conditional:
+                       use_conditional = " & ".join(
+                               pp.useflag(u) for u in mdep.use_conditional.split()
+                       )
+               if mdep.operator == '=*':
+                       formatted_dep = '=%s*' % str(mdep.cpv)
+               else:
+                       formatted_dep = mdep.operator + str(mdep.cpv)
+               if mdep.slot:
+                       formatted_dep += pp.emph(':') + pp.slot(mdep.slot)
+               if mdep.use:
+                       useflags = pp.useflag(','.join(mdep.use.tokens))
+                       formatted_dep += (pp.emph('[') + useflags + pp.emph(']'))
+
+               if dep_is_displayed:
+                       indent = indent + " " * len(str(dep.cpv))
+                       self.print_fn(indent, '', use_conditional, formatted_dep)
+               else:
+                       self.print_fn(indent, str(dep.cpv), use_conditional, formatted_dep)
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       print mod_usage(mod_name="depends")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -a, --all-packages",
+                       "include dependencies that are not installed (slow)"),
+               (" -D, --indirect",
+                       "search both direct and indirect dependencies"),
+               ("     --depth=N", "limit indirect dependency tree to specified depth")
+       ))
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       posargs = (x[1] for x in module_opts)
+       for opt, posarg in zip(opts, posargs):
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-a', '--all-packages'):
+                       QUERY_OPTS['includeMasked'] = True
+               elif opt in ('-D', '--indirect'):
+                       QUERY_OPTS['onlyDirect'] = False
+               elif opt in ('--depth'):
+                       if posarg.isdigit():
+                               depth = int(posarg)
+                       else:
+                               err = "Module option --depth requires integer (got '%s')"
+                               sys.stdout.write(pp.error(err % posarg))
+                               print
+                               print_help(with_description=False)
+                               sys.exit(2)
+                       QUERY_OPTS["maxDepth"] = depth
+
+
+def main(input_args):
+       """Parse input and run the program"""
+       short_opts = "hadD" # -d, --direct was old option for default action
+       long_opts = ('help', 'all-packages', 'direct', 'indirect', 'depth=')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       #
+       # Output
+       #
+
+       dep_print = DependPrinter(verbose=CONFIG['verbose'])
+       first_run = True
+       for query in queries:
+               if not first_run:
+                       print
+
+               pkg = Dependencies(query)
+               if QUERY_OPTS['includeMasked']:
+                       pkggetter = get_cpvs
+               else:
+                       pkggetter = get_installed_cpvs
+
+               if CONFIG['verbose']:
+                       print " * These packages depend on %s:" % pp.emph(str(pkg.cpv))
+               pkg.graph_reverse_depends(
+                       pkgset=sorted(pkggetter(), cmp=compare_package_strings),
+                       max_depth=QUERY_OPTS["maxDepth"],
+                       only_direct=QUERY_OPTS["onlyDirect"],
+                       printer_fn=dep_print
+               )
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/depgraph.py b/gentoolkit/pym/gentoolkit/equery/depgraph.py
new file mode 100644 (file)
index 0000000..18d19ba
--- /dev/null
@@ -0,0 +1,223 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Display a direct dependency graph for a given package"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from functools import partial
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "depth": 1,
+       "noAtom": False,
+       "noIndent": False,
+       "noUseflags": False,
+       "includeInstalled": True,
+       "includePortTree": True,
+       "includeOverlayTree": True,
+       "includeMasked": True,
+       "isRegex": False,
+       "matchExact": True,
+       "printMatchInfo": (not CONFIG['quiet'])
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       print "Default depth is set to 1 (direct only). Use --depth=0 for no max."
+       print
+       print mod_usage(mod_name="depgraph")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -A, --no-atom", "do not show dependency atom"),
+               (" -U, --no-useflags", "do not show USE flags"),
+               (" -l, --linear", "do not format the graph by indenting dependencies"),
+               ("     --depth=N", "limit dependency graph to specified depth")
+       ))
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       posargs = (x[1] for x in module_opts)
+       for opt, posarg in zip(opts, posargs):
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               if opt in ('-A', '--no-atom'):
+                       QUERY_OPTS["noAtom"] = True
+               if opt in ('-U', '--no-useflags'):
+                       QUERY_OPTS["noUseflags"] = True
+               if opt in ('-l', '--linear'):
+                       QUERY_OPTS["noIndent"] = True
+               if opt in ('--depth'):
+                       if posarg.isdigit():
+                               depth = int(posarg)
+                       else:
+                               err = "Module option --depth requires integer (got '%s')"
+                               sys.stderr.write(pp.error(err % posarg))
+                               print
+                               print_help(with_description=False)
+                               sys.exit(2)
+                       QUERY_OPTS["depth"] = depth
+
+
+def depgraph_printer(
+       depth,
+       pkg,
+       dep,
+       no_use=False,
+       no_atom=False,
+       no_indent=False,
+       initial_pkg=False
+):
+       """Display L{gentoolkit.dependencies.Dependencies.graph_depends} results.
+
+       @type depth: int
+       @param depth: depth of indirection, used to calculate indent
+       @type pkg: L{gentoolkit.package.Package}
+       @param pkg: "best match" package matched by B{dep}
+       @type dep: L{gentoolkit.atom.Atom}
+       @param dep: dependency that matched B{pkg}
+       @type no_use: bool
+       @param no_use: don't output USE flags
+       @type no_atom: bool
+       @param no_atom: don't output dep atom
+       @type no_indent: bool
+       @param no_indent: don't output indent based on B{depth}
+       @type initial_pkg: bool
+       @param initial_pkg: somewhat of a hack used to print the root package of
+               the graph with absolutely no indent
+       """
+       indent = '' if no_indent or initial_pkg else ' ' + (' ' * depth)
+       decorator = '[%3d] ' % depth if no_indent else '`-- '
+       use = ''
+       try:
+               atom = '' if no_atom else ' (%s)' % dep.atom
+               if not no_use and dep is not None and dep.use:
+                       use = ' [%s]' % ' '.join(
+                               pp.useflag(x, enabled=True) for x in dep.use.tokens
+                       )
+       except AttributeError:
+               # 'NoneType' object has no attribute 'atom'
+               atom = ''
+       try:
+               print ''.join((indent, decorator, pp.cpv(str(pkg.cpv)), atom, use))
+       except AttributeError:
+               # 'NoneType' object has no attribute 'cpv'
+               print ''.join((indent, decorator, "(no match for %r)" % dep.atom))
+
+
+def make_depgraph(pkg, printer_fn):
+       """Create and display depgraph for each package."""
+
+       if CONFIG['verbose']:
+               print " * direct dependency graph for %s:" % pp.cpv(str(pkg.cpv))
+       else:
+               print "%s:" % str(pkg.cpv)
+
+       # Print out the first package
+       printer_fn(0, pkg, None, initial_pkg=True)
+
+       deps = pkg.deps.graph_depends(
+               max_depth=QUERY_OPTS['depth'],
+               printer_fn=printer_fn,
+               # Use this to set this pkg as the graph's root; better way?
+               result=[(0, pkg)]
+       )
+
+       if CONFIG['verbose']:
+               pkgname = pp.cpv(str(pkg.cpv))
+               n_packages = pp.number(str(len(deps)))
+               max_seen = pp.number(str(max(x[0] for x in deps)))
+               info = "[ %s stats: packages (%s), max depth (%s) ]"
+               print info % (pkgname, n_packages, max_seen)
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "hAUl"
+       long_opts = ('help', 'no-atom', 'no-useflags', 'depth=')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       #
+       # Output
+       #
+
+       first_run = True
+       for query in queries:
+               if not first_run:
+                       print
+
+               matches = do_lookup(query, QUERY_OPTS)
+
+               if not matches:
+                       raise errors.GentoolkitNoMatches(query)
+
+               if CONFIG['verbose']:
+                       printer = partial(
+                               depgraph_printer,
+                               no_atom=QUERY_OPTS['noAtom'],
+                               no_indent=QUERY_OPTS['noIndent'],
+                               no_use=QUERY_OPTS['noUseflags']
+                       )
+               else:
+                       printer = partial(
+                               depgraph_printer,
+                               no_atom=True,
+                               no_indent=True,
+                               no_use=True
+                       )
+
+               for pkg in matches:
+                       make_depgraph(pkg, printer)
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/files.py b/gentoolkit/pym/gentoolkit/equery/files.py
new file mode 100644 (file)
index 0000000..400e4b1
--- /dev/null
@@ -0,0 +1,320 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""List files owned by a given package"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import portage
+
+import gentoolkit.pprinter as pp
+from gentoolkit.equery import (format_filetype, format_options, mod_usage,
+       CONFIG)
+from gentoolkit.helpers import do_lookup
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "categoryFilter": None,
+       "includeInstalled": True,
+       "includePortTree": False,
+       "includeOverlayTree": False,
+       "includeMasked": True,
+       "isRegex": False,
+       "matchExact": True,
+       "outputTree": False,
+       "printMatchInfo": (not CONFIG['quiet']),
+       "showType": False,
+       "showTimestamp": False,
+       "showMD5": False,
+       "typeFilter": None
+}
+
+FILTER_RULES = (
+       'dir', 'obj', 'sym', 'dev', 'path', 'conf', 'cmd', 'doc', 'man', 'info'
+)
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       print mod_usage(mod_name="files")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -m, --md5sum", "include MD5 sum in output"),
+               (" -s, --timestamp", "include timestamp in output"),
+               (" -t, --type", "include file type in output"),
+               ("     --tree", "display results in a tree (turns off other options)"),
+               (" -f, --filter=RULES", "filter output by file type"),
+               ("              RULES",
+                       "a comma-separated list (no spaces); choose from:")
+       ))
+       print " " * 24, ', '.join(pp.emph(x) for x in FILTER_RULES)
+
+
+# R0912: *Too many branches (%s/%s)*
+# pylint: disable-msg=R0912
+def display_files(contents):
+       """Display the content of an installed package.
+
+       @see: gentoolkit.package.Package.parsed_contents
+       @type contents: dict
+       @param contents: {'path': ['filetype', ...], ...}
+       """
+
+       filenames = contents.keys()
+       filenames.sort()
+       last = []
+
+       for name in filenames:
+               if QUERY_OPTS["outputTree"]:
+                       dirdepth = name.count('/')
+                       indent = " "
+                       if dirdepth == 2:
+                               indent = "   "
+                       elif dirdepth > 2:
+                               indent = "   " * (dirdepth - 1)
+
+                       basename = name.rsplit("/", dirdepth - 1)
+                       if contents[name][0] == "dir":
+                               if len(last) == 0:
+                                       last = basename
+                                       print pp.path(indent + basename[0])
+                                       continue
+                               for i, directory in enumerate(basename):
+                                       try:
+                                               if directory in last[i]:
+                                                       continue
+                                       except IndexError:
+                                               pass
+                                       last = basename
+                                       if len(last) == 1:
+                                               print pp.path(indent + last[0])
+                                               continue
+                                       print pp.path(indent + "> /" + last[-1])
+                       elif contents[name][0] == "sym":
+                               print pp.path(indent + "+"),
+                               print pp.path_symlink(basename[-1] + " -> " + contents[name][2])
+                       else:
+                               print pp.path(indent + "+ ") + basename[-1]
+               else:
+                       print format_filetype(
+                               name,
+                               contents[name],
+                               show_type=QUERY_OPTS["showType"],
+                               show_md5=QUERY_OPTS["showMD5"],
+                               show_timestamp=QUERY_OPTS["showTimestamp"]
+                       )
+
+
+def filter_by_doc(contents, content_filter):
+       """Return a copy of content filtered by documentation."""
+
+       filtered_content = {}
+       for doctype in ('doc' ,'man' ,'info'):
+               # List only files from /usr/share/{doc,man,info}
+               if doctype in content_filter:
+                       docpath = os.path.join(os.sep, 'usr', 'share', doctype)
+                       for path in contents:
+                               if contents[path][0] == 'obj' and path.startswith(docpath):
+                                       filtered_content[path] = contents[path]
+
+       return filtered_content
+
+
+def filter_by_command(contents):
+       """Return a copy of content filtered by executable commands."""
+
+       filtered_content = {}
+       userpath = os.environ["PATH"].split(os.pathsep)
+       userpath = [os.path.normpath(x) for x in userpath]
+       for path in contents:
+               if (contents[path][0] in ['obj', 'sym'] and
+                       os.path.dirname(path) in userpath):
+                       filtered_content[path] = contents[path]
+
+       return filtered_content
+
+
+def filter_by_path(contents):
+       """Return a copy of content filtered by file paths."""
+
+       filtered_content = {}
+       paths = list(reversed(sorted(contents.keys())))
+       while paths:
+               basepath = paths.pop()
+               if contents[basepath][0] == 'dir':
+                       check_subdirs = False
+                       for path in paths:
+                               if (contents[path][0] != "dir" and
+                                       os.path.dirname(path) == basepath):
+                                       filtered_content[basepath] = contents[basepath]
+                                       check_subdirs = True
+                                       break
+                       if check_subdirs:
+                               while (paths and paths[-1].startswith(basepath)):
+                                       paths.pop()
+
+       return filtered_content
+
+
+def filter_by_conf(contents):
+       """Return a copy of content filtered by configuration files."""
+
+       filtered_content = {}
+       conf_path = portage.settings["CONFIG_PROTECT"].split()
+       conf_path = tuple(os.path.normpath(x) for x in conf_path)
+       conf_mask_path = portage.settings["CONFIG_PROTECT_MASK"].split()
+       conf_mask_path = tuple(os.path.normpath(x) for x in conf_mask_path)
+       for path in contents:
+               if contents[path][0] == 'obj' and path.startswith(conf_path):
+                       if not path.startswith(conf_mask_path):
+                               filtered_content[path] = contents[path]
+
+       return filtered_content
+
+
+def filter_contents(contents):
+       """Filter files by type if specified by the user.
+
+       @see: gentoolkit.package.Package.parsed_contents
+       @type contents: dict
+       @param contents: {'path': ['filetype', ...], ...}
+       @rtype: dict
+       @return: contents with unrequested filetypes stripped
+       """
+
+       if QUERY_OPTS['typeFilter']:
+               content_filter = QUERY_OPTS['typeFilter']
+       else:
+               return contents
+
+       filtered_content = {}
+       if frozenset(('dir', 'obj', 'sym', 'dev')).intersection(content_filter):
+               # Filter elements by type (as recorded in CONTENTS)
+               for path in contents:
+                       if contents[path][0] in content_filter:
+                               filtered_content[path] = contents[path]
+       if "cmd" in content_filter:
+               filtered_content.update(filter_by_command(contents))
+       if "path" in content_filter:
+               filtered_content.update(filter_by_path(contents))
+       if "conf" in content_filter:
+               filtered_content.update(filter_by_conf(contents))
+       if frozenset(('doc' ,'man' ,'info')).intersection(content_filter):
+               filtered_content.update(filter_by_doc(contents, content_filter))
+
+       return filtered_content
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       content_filter = []
+       opts = (x[0] for x in module_opts)
+       posargs = (x[1] for x in module_opts)
+       for opt, posarg in zip(opts, posargs):
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-e', '--exact-name'):
+                       QUERY_OPTS["matchExact"] = True
+               elif opt in ('-m', '--md5sum'):
+                       QUERY_OPTS["showMD5"] = True
+               elif opt in ('-s', '--timestamp'):
+                       QUERY_OPTS["showTimestamp"] = True
+               elif opt in ('-t', '--type'):
+                       QUERY_OPTS["showType"] = True
+               elif opt in ('--tree'):
+                       QUERY_OPTS["outputTree"] = True
+               elif opt in ('-f', '--filter'):
+                       f_split = posarg.split(',')
+                       content_filter.extend(x.lstrip('=') for x in f_split)
+                       for rule in content_filter:
+                               if not rule in FILTER_RULES:
+                                       sys.stderr.write(
+                                               pp.error("Invalid filter rule '%s'" % rule)
+                                       )
+                                       print
+                                       print_help(with_description=False)
+                                       sys.exit(2)
+                       QUERY_OPTS["typeFilter"] = content_filter
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       # -e, --exact-name is legacy option. djanderson '09
+       short_opts = "hemstf:"
+       long_opts = ('help', 'exact-name', 'md5sum', 'timestamp', 'type', 'tree',
+               'filter=')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       # Turn off filtering for tree output
+       if QUERY_OPTS["outputTree"]:
+               QUERY_OPTS["typeFilter"] = None
+
+       #
+       # Output files
+       #
+
+       first_run = True
+       for query in queries:
+               if not first_run:
+                       print
+
+               matches = do_lookup(query, QUERY_OPTS)
+
+               if not matches:
+                       sys.stderr.write(
+                               pp.error("No matching packages found for %s" % query)
+                       )
+
+               for pkg in matches:
+                       if CONFIG['verbose']:
+                               print " * Contents of %s:" % pp.cpv(str(pkg.cpv))
+
+                       contents = pkg.parsed_contents()
+                       display_files(filter_contents(contents))
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/hasuse.py b/gentoolkit/pym/gentoolkit/equery/hasuse.py
new file mode 100644 (file)
index 0000000..bc4bb63
--- /dev/null
@@ -0,0 +1,156 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""List all installed packages that have a given USE flag"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup
+from gentoolkit.package import PackageFormatter
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "categoryFilter": None,
+       "includeInstalled": True,
+       "includePortTree": False,
+       "includeOverlayTree": False,
+       "includeMasked": True,
+       "isRegex": False,             # Necessary for do_lookup, don't change
+       "printMatchInfo": False
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       print mod_usage(mod_name="hasuse", arg="USE-flag")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -I, --exclude-installed",
+                       "exclude installed packages from search path"),
+               (" -o, --overlay-tree", "include overlays in search path"),
+               (" -p, --portage-tree", "include entire portage tree in search path")
+       ))
+
+
+def display_useflags(query, pkg):
+       """Display USE flag information for a given package."""
+
+       try:
+               useflags = [x.lstrip("+-") for x in pkg.environment("IUSE").split()]
+       except errors.GentoolkitFatalError:
+               # aux_get KeyError or other unexpected result
+               return
+
+       if query not in useflags:
+               return
+
+       if CONFIG['verbose']:
+               fmt_pkg = PackageFormatter(pkg, do_format=True)
+       else:
+               fmt_pkg = PackageFormatter(pkg, do_format=False)
+
+       if (QUERY_OPTS["includeInstalled"] and
+               not QUERY_OPTS["includePortTree"] and
+               not QUERY_OPTS["includeOverlayTree"]):
+               if not 'I' in fmt_pkg.location:
+                       return
+       if (QUERY_OPTS["includePortTree"] and
+               not QUERY_OPTS["includeOverlayTree"]):
+               if not 'P' in fmt_pkg.location:
+                       return
+       if (QUERY_OPTS["includeOverlayTree"] and
+               not QUERY_OPTS["includePortTree"]):
+               if not 'O' in fmt_pkg.location:
+                       return
+       print fmt_pkg
+
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       # Parse module options
+       opts = (x[0] for x in module_opts)
+       for opt in opts:
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-I', '--exclue-installed'):
+                       QUERY_OPTS['includeInstalled'] = False
+               elif opt in ('-p', '--portage-tree'):
+                       QUERY_OPTS['includePortTree'] = True
+               elif opt in ('-o', '--overlay-tree'):
+                       QUERY_OPTS['includeOverlayTree'] = True
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "hiIpo" # -i was option for default action
+       # --installed is no longer needed, kept for compatibility (djanderson '09)
+       long_opts = ('help', 'installed', 'exclude-installed', 'portage-tree',
+               'overlay-tree')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       matches = do_lookup("*", QUERY_OPTS)
+       matches.sort()
+
+       #
+       # Output
+       #
+
+       first_run = True
+       for query in queries:
+               if not first_run:
+                       print
+
+               if CONFIG['verbose']:
+                       print " * Searching for USE flag %s ... " % pp.emph(query)
+
+               for pkg in matches:
+                       display_useflags(query, pkg)
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/list_.py b/gentoolkit/pym/gentoolkit/equery/list_.py
new file mode 100644 (file)
index 0000000..c727590
--- /dev/null
@@ -0,0 +1,224 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""List installed packages matching the query pattern"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit
+import gentoolkit.pprinter as pp
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup, get_installed_cpvs
+from gentoolkit.package import Package, PackageFormatter
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "duplicates": False,
+       "includeInstalled": True,
+       "includePortTree": False,
+       "includeOverlayTree": False,
+       "includeMasked": True,
+       "includeMaskReason": False,
+       "isRegex": False,
+       "printMatchInfo": (not CONFIG['quiet'])
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+
+       # Deprecation warning added by djanderson, 12/2008
+       depwarning = (
+               "Default action for this module has changed in Gentoolkit 0.3.",
+               "Use globbing to simulate the old behavior (see man equery).",
+               "Use '*' to check all installed packages.",
+               "Use 'foo-bar/*' to filter by category."
+       )
+       for line in depwarning:
+               sys.stderr.write(pp.warn(line))
+       print
+
+       print mod_usage(mod_name="list")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -d, --duplicates", "list only installed duplicate packages"),
+               (" -f, --full-regex", "query is a regular expression"),
+               (" -m, --mask-reason", "include reason for package mask"),
+               (" -I, --exclude-installed",
+                       "exclude installed packages from output"),
+               (" -o, --overlay-tree", "list packages in overlays"),
+               (" -p, --portage-tree", "list packages in the main portage tree")
+       ))
+
+
+def get_duplicates(matches):
+       """Return only packages that have more than one version installed."""
+
+       dups = {}
+       result = []
+       for pkg in matches:
+               if pkg.cpv.cp in dups:
+                       dups[pkg.cpv.cp].append(pkg)
+               else:
+                       dups[pkg.cpv.cp] = [pkg]
+
+       for cpv in dups.values():
+               if len(cpv) > 1:
+                       result.extend(cpv)
+
+       return result
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       posargs = (x[1] for x in module_opts)
+       for opt, posarg in zip(opts, posargs):
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-I', '--exclude-installed'):
+                       QUERY_OPTS['includeInstalled'] = False
+               elif opt in ('-p', '--portage-tree'):
+                       QUERY_OPTS['includePortTree'] = True
+               elif opt in ('-o', '--overlay-tree'):
+                       QUERY_OPTS['includeOverlayTree'] = True
+               elif opt in ('-f', '--full-regex'):
+                       QUERY_OPTS['isRegex'] = True
+               elif opt in ('-m', '--mask-reason'):
+                       QUERY_OPTS['includeMaskReason'] = True
+               elif opt in ('-e', '--exact-name'):
+                       sys.stderr.write(pp.warn("-e, --exact-name is now default."))
+                       sys.stderr.write(
+                               pp.warn("Use globbing to simulate the old behavior.")
+                       )
+                       print
+               elif opt in ('-d', '--duplicates'):
+                       QUERY_OPTS['duplicates'] = True
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "hdefiImop" # -i, -e were options for default actions
+
+       # 04/09: djanderson
+       # --all is no longer needed. Kept for compatibility.
+       # --installed is no longer needed. Kept for compatibility.
+       # --exact-name is no longer needed. Kept for compatibility.
+       long_opts = ('help', 'all', 'installed', 'exclude-installed',
+       'mask-reason', 'portage-tree', 'overlay-tree', 'full-regex', 'exact-name',
+       'duplicates')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       # Only search installed packages when listing duplicate packages
+       if QUERY_OPTS["duplicates"]:
+               QUERY_OPTS["includeInstalled"] = True
+               QUERY_OPTS["includePortTree"] = False
+               QUERY_OPTS["includeOverlayTree"] = False
+               QUERY_OPTS["includeMaskReason"] = False
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       first_run = True
+       for query in queries:
+               if not first_run:
+                       print
+
+               matches = do_lookup(query, QUERY_OPTS)
+
+               # Find duplicate packages
+               if QUERY_OPTS["duplicates"]:
+                       matches = get_duplicates(matches)
+
+               matches.sort()
+
+               #
+               # Output
+               #
+
+               for pkg in matches:
+                       if CONFIG['verbose']:
+                               pkgstr = PackageFormatter(pkg, do_format=True)
+                       else:
+                               pkgstr = PackageFormatter(pkg, do_format=False)
+
+                       if (QUERY_OPTS["includeInstalled"] and
+                               not QUERY_OPTS["includePortTree"] and
+                               not QUERY_OPTS["includeOverlayTree"]):
+                               if not 'I' in pkgstr.location:
+                                       continue
+                       if (QUERY_OPTS["includePortTree"] and
+                               not QUERY_OPTS["includeOverlayTree"]):
+                               if not 'P' in pkgstr.location:
+                                       continue
+                       if (QUERY_OPTS["includeOverlayTree"] and
+                               not QUERY_OPTS["includePortTree"]):
+                               if not 'O' in pkgstr.location:
+                                       continue
+                       print pkgstr
+
+                       if QUERY_OPTS["includeMaskReason"]:
+                               ms_int, ms_orig = pkgstr.format_mask_status()
+                               if not ms_int > 2:
+                                       # ms_int is a number representation of mask level.
+                                       # Only 2 and above are "hard masked" and have reasons.
+                                       continue
+                               mask_reason = pkg.mask_reason()
+                               if not mask_reason:
+                                       # Package not on system or not masked
+                                       continue
+                               elif not any(mask_reason):
+                                       print " * No mask reason given"
+                               else:
+                                       status = ', '.join(ms_orig)
+                                       explanation = mask_reason[0]
+                                       mask_location = mask_reason[1]
+                                       print " * Masked by %r" % status
+                                       print " * %s:" % mask_location
+                                       print '\n'.join(
+                                               [' * %s' % line.lstrip(' #')
+                                                       for line in explanation.splitlines()]
+                                               )
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/meta.py b/gentoolkit/pym/gentoolkit/equery/meta.py
new file mode 100644 (file)
index 0000000..1fc8964
--- /dev/null
@@ -0,0 +1,492 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header: $
+
+"""Display metadata about a given package"""
+
+# Move to Imports section after Python-2.6 is stable
+from __future__ import with_statement
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import re
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import find_packages, print_sequence, print_file
+from gentoolkit.textwrap_ import TextWrapper
+
+# =======
+# Globals
+# =======
+
+# E1101: Module 'portage.output' has no $color member
+# portage.output creates color functions dynamically
+# pylint: disable-msg=E1101
+
+QUERY_OPTS = {
+       'current': False,
+       'description': False,
+       'herd': False,
+       'keywords': False,
+       'maintainer': False,
+       'useflags': False,
+       'upstream': False,
+       'xml': False
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True, with_usage=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       if with_usage:
+               print mod_usage(mod_name="meta")
+               print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -d, --description", "show an extended package description"),
+               (" -H, --herd", "show the herd(s) for the package"),
+               (" -k, --keywords", "show keywords for all matching package versions"),
+               (" -m, --maintainer", "show the maintainer(s) for the package"),
+               (" -u, --useflags", "show per-package USE flag descriptions"),
+               (" -U, --upstream", "show package's upstream information"),
+               (" -x, --xml", "show the plain metadata.xml file")
+       ))
+
+
+def filter_keywords(matches):
+       """Filters non-unique keywords per slot.
+
+       Does not filter arch mask keywords (-). Besides simple non-unique keywords,
+       also remove unstable keywords (~) if a higher version in the same slot is
+       stable. This view makes version bumps easier for package maintainers.
+
+       @type matches: array
+       @param matches: set of L{gentoolkit.package.Package} instances whose
+               'key' are all the same.
+       @rtype: dict
+       @return: a dict with L{gentoolkit.package.Package} instance keys and
+               'array of keywords not found in a higher version of pkg within the
+               same slot' values.
+       """
+       def del_archmask(keywords):
+               """Don't add arch_masked to filter set."""
+               return [x for x in keywords if not x.startswith('-')]
+
+       def add_unstable(keywords):
+               """Add unstable keyword for all stable keywords to filter set."""
+               result = list(keywords)
+               result.extend(
+                       ['~%s' % x for x in keywords if not x.startswith(('-', '~'))]
+               )
+               return result
+
+       result = {}
+       slot_map = {}
+       # Start from the newest
+       rev_matches = reversed(matches)
+       for pkg in rev_matches:
+               keywords_str, slot = pkg.environment(('KEYWORDS', 'SLOT'),
+                       prefer_vdb=False)
+               keywords = keywords_str.split()
+               result[pkg] = [x for x in keywords if x not in slot_map.get(slot, [])]
+               try:
+                       slot_map[slot].update(del_archmask(add_unstable(keywords)))
+               except KeyError:
+                       slot_map[slot] = set(del_archmask(add_unstable(keywords)))
+
+       return result
+
+
+def format_herds(herds):
+       """Format herd information for display."""
+
+       result = []
+       for herd in herds:
+               herdstr = ''
+               email = "(%s)" % herd[1] if herd[1] else ''
+               herdstr = herd[0]
+               if CONFIG['verbose']:
+                       herdstr += " %s" % (email,)
+               result.append(herdstr)
+
+       return result
+
+
+def format_maintainers(maints):
+       """Format maintainer information for display."""
+
+       result = []
+       for maint in maints:
+               maintstr = ''
+               maintstr = maint.email
+               if CONFIG['verbose']:
+                       maintstr += " (%s)" % (maint.name,) if maint.name else ''
+                       maintstr += "\n%s" % (maint.description,) \
+                               if maint.description else ''
+               result.append(maintstr)
+
+       return result
+
+
+def format_upstream(upstream):
+       """Format upstream information for display."""
+
+       def _format_upstream_docs(docs):
+               result = []
+               for doc in docs:
+                       doc_location = doc[0]
+                       doc_lang = doc[1]
+                       docstr = doc_location
+                       if doc_lang is not None:
+                               docstr += " (%s)" % (doc_lang,)
+                       result.append(docstr)
+               return result
+
+       def _format_upstream_ids(ids):
+               result = []
+               for id_ in ids:
+                       site = id_[0]
+                       proj_id = id_[1]
+                       idstr = "%s ID: %s" % (site, proj_id)
+                       result.append(idstr)
+               return result
+
+       result = []
+       for up in upstream:
+               upmaints = format_maintainers(up.maintainers)
+               for upmaint in upmaints:
+                       result.append(format_line(upmaint, "Maintainer:  ", " " * 13))
+
+               for upchange in up.changelogs:
+                       result.append(format_line(upchange, "ChangeLog:   ", " " * 13))
+
+               updocs = _format_upstream_docs(up.docs)
+               for updoc in updocs:
+                       result.append(format_line(updoc, "Docs:       ", " " * 13))
+
+               for upbug in up.bugtrackers:
+                       result.append(format_line(upbug, "Bugs-to:     ", " " * 13))
+
+               upids = _format_upstream_ids(up.remoteids)
+               for upid in upids:
+                       result.append(format_line(upid, "Remote-ID:   ", " " * 13))
+
+       return result
+
+
+def format_useflags(useflags):
+       """Format USE flag information for display."""
+
+       result = []
+       for flag in useflags:
+               result.append(pp.useflag(flag.name))
+               result.append(flag.description)
+               result.append("")
+
+       return result
+
+
+def format_keywords(keywords):
+       """Sort and colorize keywords for display."""
+
+       result = []
+
+       for kw in sorted(keywords):
+               if kw.startswith('-'):
+                       # arch masked
+                       kw = pp.keyword(kw, stable=False, hard_masked=True)
+               elif kw.startswith('~'):
+                       # keyword masked
+                       kw = pp.keyword(kw, stable=False, hard_masked=False)
+               else:
+                       # stable
+                       kw = pp.keyword(kw, stable=True, hard_masked=False)
+               result.append(kw)
+
+       return ' '.join(result)
+
+
+def format_keywords_line(pkg, fmtd_keywords, slot, verstr_len):
+       """Format the entire keywords line for display."""
+
+       ver = pkg.cpv.fullversion
+       result = "%s:%s: %s" % (ver, pp.slot(slot), fmtd_keywords)
+       if CONFIG['verbose'] and fmtd_keywords:
+               result = format_line(fmtd_keywords, "%s:%s: " % (ver, pp.slot(slot)),
+                       " " * (verstr_len + 2))
+
+       return result
+
+
+# R0912: *Too many branches (%s/%s)*
+# pylint: disable-msg=R0912
+def call_format_functions(matches):
+       """Call information gathering functions and display the results."""
+
+       # Choose a good package to reference metadata from
+       ref_pkg = get_reference_pkg(matches)
+
+       if CONFIG['verbose']:
+               repo = ref_pkg.repo_id()
+               print " * %s [%s]" % (pp.cpv(ref_pkg.cpv.cp), pp.section(repo))
+
+       got_opts = False
+       if any(QUERY_OPTS.values()):
+               # Specific information requested, less formatting
+               got_opts = True
+
+       if QUERY_OPTS["herd"] or not got_opts:
+               herds = format_herds(ref_pkg.metadata.herds(include_email=True))
+               if QUERY_OPTS["herd"]:
+                       print_sequence(format_list(herds))
+               else:
+                       for herd in herds:
+                               print format_line(herd, "Herd:        ", " " * 13)
+
+       if QUERY_OPTS["maintainer"] or not got_opts:
+               maints = format_maintainers(ref_pkg.metadata.maintainers())
+               if QUERY_OPTS["maintainer"]:
+                       print_sequence(format_list(maints))
+               else:
+                       if not maints:
+                               print format_line([], "Maintainer:  ", " " * 13)
+                       else:
+                               for maint in maints:
+                                       print format_line(maint, "Maintainer:  ", " " * 13)
+
+       if QUERY_OPTS["upstream"] or not got_opts:
+               upstream = format_upstream(ref_pkg.metadata.upstream())
+               if QUERY_OPTS["upstream"]:
+                       upstream = format_list(upstream)
+               else:
+                       upstream = format_list(upstream, "Upstream:    ", " " * 13)
+               print_sequence(upstream)
+
+       if not got_opts:
+               pkg_loc = ref_pkg.package_path()
+               print format_line(pkg_loc, "Location:    ", " " * 13)
+
+       if QUERY_OPTS["keywords"] or not got_opts:
+               # Get {<Package 'dev-libs/glib-2.20.5'>: [u'ia64', u'm68k', ...], ...}
+               keyword_map = filter_keywords(matches)
+
+               for match in matches:
+                       slot = match.environment('SLOT')
+                       verstr_len = len(match.cpv.fullversion) + len(slot)
+                       fmtd_keywords = format_keywords(keyword_map[match])
+                       keywords_line = format_keywords_line(
+                               match, fmtd_keywords, slot, verstr_len
+                       )
+                       if QUERY_OPTS["keywords"]:
+                               print keywords_line
+                       else:
+                               indent = " " * (16 + verstr_len)
+                               print format_line(keywords_line, "Keywords:    ", indent)
+
+       if QUERY_OPTS["description"]:
+               desc = ref_pkg.metadata.descriptions()
+               print_sequence(format_list(desc))
+
+       if QUERY_OPTS["useflags"]:
+               useflags = format_useflags(ref_pkg.metadata.use())
+               print_sequence(format_list(useflags))
+
+       if QUERY_OPTS["xml"]:
+               print_file(os.path.join(ref_pkg.package_path(), 'metadata.xml'))
+
+
+def format_line(line, first="", subsequent="", force_quiet=False):
+       """Wrap a string at word boundaries and optionally indent the first line
+       and/or subsequent lines with custom strings.
+
+       Preserve newlines if the longest line is not longer than
+       CONFIG['termWidth']. To force the preservation of newlines and indents,
+       split the string into a list and feed it to format_line via format_list.
+
+       @see: format_list()
+       @type line: string
+       @param line: text to format
+       @type first: string
+       @param first: text to prepend to the first line
+       @type subsequent: string
+       @param subsequent: text to prepend to subsequent lines
+       @type force_quiet: boolean
+       @rtype: string
+       @return: A wrapped line
+       """
+
+       if line:
+               line = line.expandtabs().strip("\n").splitlines()
+       else:
+               if force_quiet:
+                       return
+               else:
+                       return first + "None specified"
+
+       if len(first) > len(subsequent):
+               wider_indent = first
+       else:
+               wider_indent = subsequent
+
+       widest_line_len = len(max(line, key=len)) + len(wider_indent)
+
+       if widest_line_len > CONFIG['termWidth']:
+               twrap = TextWrapper(width=CONFIG['termWidth'], expand_tabs=False,
+                       initial_indent=first, subsequent_indent=subsequent)
+               line = " ".join(line)
+               line = re.sub("\s+", " ", line)
+               line = line.lstrip()
+               result = twrap.fill(line)
+       else:
+               # line will fit inside CONFIG['termWidth'], so preserve whitespace and
+               # newlines
+               line[0] = first + line[0]          # Avoid two newlines if len == 1
+
+               if len(line) > 1:
+                       line[0] = line[0] + "\n"
+                       for i in range(1, (len(line[1:-1]) + 1)):
+                               line[i] = subsequent + line[i] + "\n"
+                       line[-1] = subsequent + line[-1]  # Avoid two newlines on last line
+
+               if line[-1].isspace():
+                       del line[-1]                # Avoid trailing blank lines
+
+               result = "".join(line)
+
+       return result.encode("utf-8")
+
+
+def format_list(lst, first="", subsequent="", force_quiet=False):
+       """Feed elements of a list to format_line().
+
+       @see: format_line()
+       @type lst: list
+       @param lst: list to format
+       @type first: string
+       @param first: text to prepend to the first line
+       @type subsequent: string
+       @param subsequent: text to prepend to subsequent lines
+       @rtype: list
+       @return: list with element text wrapped at CONFIG['termWidth']
+       """
+
+       result = []
+       if lst:
+               # Format the first line
+               line = format_line(lst[0], first, subsequent, force_quiet)
+               result.append(line)
+               # Format subsequent lines
+               for elem in lst[1:]:
+                       if elem:
+                               result.append(format_line(elem, subsequent, subsequent,
+                                       force_quiet))
+                       else:
+                               # We don't want to send a blank line to format_line()
+                               result.append("")
+       else:
+               if CONFIG['verbose']:
+                       if force_quiet:
+                               result = None
+                       else:
+                               # Send empty list, we'll get back first + `None specified'
+                               result.append(format_line(lst, first, subsequent))
+
+       return result
+
+
+def get_reference_pkg(matches):
+       """Find a package in the Portage tree to reference."""
+
+       pkg = None
+       rev_matches = list(reversed(matches))
+       while rev_matches:
+               pkg = rev_matches.pop()
+               if not pkg.is_overlay():
+                       break
+
+       return pkg
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       for opt in opts:
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-d', '--description'):
+                       QUERY_OPTS["description"] = True
+               elif opt in ('-H', '--herd'):
+                       QUERY_OPTS["herd"] = True
+               elif opt in ('-m', '--maintainer'):
+                       QUERY_OPTS["maintainer"] = True
+               elif opt in ('-k', '--keywords'):
+                       QUERY_OPTS["keywords"] = True
+               elif opt in ('-u', '--useflags'):
+                       QUERY_OPTS["useflags"] = True
+               elif opt in ('-U', '--upstream'):
+                       QUERY_OPTS["upstream"] = True
+               elif opt in ('-x', '--xml'):
+                       QUERY_OPTS["xml"] = True
+
+
+def main(input_args):
+       """Parse input and run the program."""
+
+       short_opts = "hdHkmuUx"
+       long_opts = ('help', 'description', 'herd', 'keywords', 'maintainer',
+               'useflags', 'upstream', 'xml')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       # Find queries' Portage directory and throw error if invalid
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       first_run = True
+       for query in queries:
+               matches = find_packages(query, include_masked=True)
+               if not matches:
+                       raise errors.GentoolkitNoMatches(query)
+
+               if not first_run:
+                       print
+
+               matches.sort()
+               call_format_functions(matches)
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/size.py b/gentoolkit/pym/gentoolkit/equery/size.py
new file mode 100644 (file)
index 0000000..f7b8888
--- /dev/null
@@ -0,0 +1,193 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Print total size of files contained in a given package"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import do_lookup
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {
+       "includeInstalled": True,
+       "includePortTree": False,
+       "includeOverlayTree": False,
+       "includeMasked": True,
+       "isRegex": False,
+       "matchExact": False,
+       "printMatchInfo": False,
+       "sizeInBytes": False
+}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+
+       # Deprecation warning added by djanderson, 12/2008
+       depwarning = (
+               "Default action for this module has changed in Gentoolkit 0.3.",
+               "Use globbing to simulate the old behavior (see man equery).",
+               "Use '*' to check all installed packages.",
+               "Use 'foo-bar/*' to filter by category."
+       )
+       for line in depwarning:
+               sys.stderr.write(pp.warn(line))
+       print
+
+       print mod_usage(mod_name="size")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -b, --bytes", "report size in bytes"),
+               (" -f, --full-regex", "query is a regular expression")
+       ))
+
+
+def display_size(match_set):
+       """Display the total size of all accessible files owned by packages.
+
+       @type match_set: list
+       @param match_set: package cat/pkg-ver strings
+       """
+
+       for pkg in match_set:
+               size, files, uncounted = pkg.size()
+
+               if CONFIG['verbose']:
+                       print " * %s" % pp.cpv(str(pkg.cpv))
+                       print "Total files : %s".rjust(25) % pp.number(str(files))
+
+                       if uncounted:
+                               print ("Inaccessible files : %s".rjust(25) %
+                                       pp.number(str(uncounted)))
+
+                       if QUERY_OPTS["sizeInBytes"]:
+                               size_str = pp.number(str(size))
+                       else:
+                               size_str = "%s %s" % format_bytes(size)
+
+                       print "Total size  : %s".rjust(25) % size_str
+               else:
+                       info = "%s: total(%d), inaccessible(%d), size(%s)"
+                       print info % (str(pkg.cpv), files, uncounted, size)
+
+
+def format_bytes(bytes_, precision=2):
+       """Format bytes into human-readable format (IEC naming standard).
+
+       @see: http://mail.python.org/pipermail/python-list/2008-August/503423.html
+       @rtype: tuple
+       @return: (str(num), str(label))
+       """
+
+       labels = (
+               (1<<40L, 'TiB'),
+               (1<<30L, 'GiB'),
+               (1<<20L, 'MiB'),
+               (1<<10L, 'KiB'),
+               (1, 'bytes')
+       )
+
+       if bytes_ == 0:
+               return (pp.number('0'), 'bytes')
+       elif bytes_ == 1:
+               return (pp.number('1'), 'byte')
+
+       for factor, label in labels:
+               if not bytes_ >= factor:
+                       continue
+
+               float_split = str(bytes_/float(factor)).split('.')
+               integer = float_split[0]
+               decimal = float_split[1]
+               if int(decimal[0:precision]):
+                       float_string = '.'.join([integer, decimal[0:precision]])
+               else:
+                       float_string = integer
+
+               return (pp.number(float_string), label)
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       for opt in opts:
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-b', '--bytes'):
+                       QUERY_OPTS["sizeInBytes"] = True
+               elif opt in ('-e', '--exact-name'):
+                       sys.stderr.write(pp.warn("-e, --exact-name is now default."))
+                       warning = pp.warn("Use globbing to simulate the old behavior.")
+                       sys.stderr.write(warning)
+                       print
+               elif opt in ('-f', '--full-regex'):
+                       QUERY_OPTS['isRegex'] = True
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       # -e, --exact-name is no longer needed. Kept for compatibility.
+       # 04/09 djanderson
+       short_opts = "hbfe"
+       long_opts = ('help', 'bytes', 'full-regex', 'exact-name')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       first_run = True
+       for query in queries:
+               if not first_run:
+                       print
+
+               matches = do_lookup(query, QUERY_OPTS)
+
+               if not matches:
+                       sys.stderr.write(pp.error("No package found matching %s" % query))
+
+               display_size(matches)
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/uses.py b/gentoolkit/pym/gentoolkit/equery/uses.py
new file mode 100644 (file)
index 0000000..d38dfe5
--- /dev/null
@@ -0,0 +1,317 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Display USE flags for a given package"""
+
+# Move to imports section when Python 2.6 is stable
+from __future__ import with_statement
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from functools import partial
+from getopt import gnu_getopt, GetoptError
+from glob import glob
+
+from portage import settings
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage, CONFIG
+from gentoolkit.helpers import find_best_match, find_packages, uniqify
+from gentoolkit.textwrap_ import TextWrapper
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {"allVersions" : False}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       print mod_usage(mod_name=__name__.split('.')[-1])
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -a, --all", "include all package versions")
+       ))
+
+
+def display_useflags(output):
+       """Print USE flag descriptions and statuses.
+
+       @type output: list
+       @param output: [(inuse, inused, flag, desc, restrict), ...]
+               inuse (int) = 0 or 1; if 1, flag is set in make.conf
+               inused (int) = 0 or 1; if 1, package is installed with flag enabled
+               flag (str) = the name of the USE flag
+               desc (str) = the flag's description
+               restrict (str) = corresponds to the text of restrict in metadata
+       """
+
+       maxflag_len = len(max([t[2] for t in output], key=len))
+
+       twrap = TextWrapper()
+       twrap.width = CONFIG['termWidth']
+       twrap.subsequent_indent = " " * (maxflag_len + 8)
+
+       markers = ("-", "+")
+       color = (
+               partial(pp.useflag, enabled=False), partial(pp.useflag, enabled=True)
+       )
+       for in_makeconf, in_installed, flag, desc, restrict in output:
+               if CONFIG['verbose']:
+                       flag_name = ""
+                       if in_makeconf != in_installed:
+                               flag_name += pp.emph(" %s %s" %
+                                       (markers[in_makeconf], markers[in_installed]))
+                       else:
+                               flag_name += (" %s %s" %
+                                       (markers[in_makeconf], markers[in_installed]))
+
+                       flag_name += " " + color[in_makeconf](flag.ljust(maxflag_len))
+                       flag_name += " : "
+
+                       # print description
+                       if restrict:
+                               restrict = "(%s %s)" % (pp.emph("Restricted to"),
+                                       pp.cpv(restrict))
+                               twrap.initial_indent = flag_name
+                               print twrap.fill(restrict)
+                               if desc:
+                                       twrap.initial_indent = twrap.subsequent_indent
+                                       print twrap.fill(desc)
+                               else:
+                                       print " : <unknown>"
+                       else:
+                               if desc:
+                                       twrap.initial_indent = flag_name
+                                       desc = twrap.fill(desc)
+                                       print desc
+                               else:
+                                       twrap.initial_indent = flag_name
+                                       print twrap.fill("<unknown>")
+               else:
+                       print markers[in_makeconf] + flag
+
+
+def get_global_useflags():
+       """Get global and expanded USE flag variables from
+       PORTDIR/profiles/use.desc and PORTDIR/profiles/desc/*.desc respectively.
+
+       @rtype: dict
+       @return: {'flag_name': 'flag description', ...}
+       """
+
+       global_usedesc = {}
+       # Get global USE flag descriptions
+       try:
+               path = os.path.join(settings["PORTDIR"], 'profiles', 'use.desc')
+               with open(path) as open_file:
+                       for line in open_file:
+                               if line.startswith('#'):
+                                       continue
+                               # Ex. of fields: ['syslog', 'Enables support for syslog\n']
+                               fields = line.split(" - ", 1)
+                               if len(fields) == 2:
+                                       global_usedesc[fields[0]] = fields[1].rstrip()
+       except IOError:
+               sys.stderr.write(
+                       pp.warn(
+                               "Could not load USE flag descriptions from %s" % pp.path(path)
+                       )
+               )
+
+       del path, open_file
+       # Add USE_EXPANDED variables to usedesc hash -- Bug #238005
+       for path in glob(os.path.join(settings["PORTDIR"],
+               'profiles', 'desc', '*.desc')):
+               try:
+                       with open(path) as open_file:
+                               for line in open_file:
+                                       if line.startswith('#'):
+                                               continue
+                                       fields = [field.strip() for field in line.split(" - ", 1)]
+                                       if len(fields) == 2:
+                                               expanded_useflag = "%s_%s" % \
+                                                       (path.split("/")[-1][0:-5], fields[0])
+                                               global_usedesc[expanded_useflag] = fields[1]
+               except IOError:
+                       sys.stderr.write(
+                               pp.warn("Could not load USE flag descriptions from %s" % path)
+                       )
+
+       return global_usedesc
+
+
+def get_matches(query):
+       """Get packages matching query."""
+
+       if not QUERY_OPTS["allVersions"]:
+               matches = [find_best_match(query)]
+               if None in matches:
+                       matches = find_packages(query, include_masked=False)
+                       if matches:
+                               matches.sort()
+       else:
+               matches = find_packages(query, include_masked=True)
+
+       if not matches:
+               raise errors.GentoolkitNoMatches(query)
+
+       return matches
+
+
+def get_output_descriptions(pkg, global_usedesc):
+       """Prepare descriptions and usage information for each USE flag."""
+
+       local_usedesc = pkg.metadata.use()
+       iuse = pkg.environment("IUSE")
+
+       if iuse:
+               usevar = uniqify([x.lstrip('+-') for x in iuse.split()])
+               usevar.sort()
+       else:
+               usevar = []
+
+       if pkg.is_installed():
+               used_flags = pkg.use().split()
+       else:
+               used_flags = settings["USE"].split()
+
+       # store (inuse, inused, flag, desc, restrict)
+       output = []
+       for flag in usevar:
+               inuse = False
+               inused = False
+
+               local_use = None
+               for use in local_usedesc:
+                       if use.name == flag:
+                               local_use = use
+                               break
+
+               try:
+                       desc = local_use.description
+               except AttributeError:
+                       try:
+                               desc = global_usedesc[flag]
+                       except KeyError:
+                               desc = ""
+
+               try:
+                       restrict = local_use.restrict
+                       restrict = restrict if restrict is not None else ""
+               except AttributeError:
+                       restrict = ""
+
+               if flag in pkg.settings("USE").split():
+                       inuse = True
+               if flag in used_flags:
+                       inused = True
+
+               output.append((inuse, inused, flag, desc, restrict))
+
+       return output
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       for opt in opts:
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-a', '--all'):
+                       QUERY_OPTS['allVersions'] = True
+
+
+def print_legend():
+       """Print a legend to explain the output format."""
+
+       print "[ Legend : %s - flag is set in make.conf       ]" % pp.emph("U")
+       print "[        : %s - package is installed with flag ]" % pp.emph("I")
+       print "[ Colors : %s, %s                         ]" % (
+               pp.useflag("set", enabled=True), pp.useflag("unset", enabled=False))
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "ha"
+       long_opts = ('help', 'all')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       #
+       # Output
+       #
+
+       first_run = True
+       legend_printed = False
+       for query in queries:
+               if not first_run:
+                       print
+
+               if CONFIG['verbose']:
+                       print " * Searching for %s ..." % pp.pkgquery(query)
+
+               matches = get_matches(query)
+               matches.sort()
+
+               global_usedesc = get_global_useflags()
+               for pkg in matches:
+
+                       output = get_output_descriptions(pkg, global_usedesc)
+                       if output:
+                               if CONFIG['verbose']:
+                                       if not legend_printed:
+                                               print_legend()
+                                               legend_printed = True
+                                       print (" * Found these USE flags for %s:" %
+                                               pp.cpv(str(pkg.cpv)))
+                                       print pp.emph(" U I")
+                               display_useflags(output)
+                       else:
+                               if CONFIG['verbose']:
+                                       sys.stderr.write(
+                                               pp.warn("No USE flags found for %s" % pp.cpv(pkg.cpv))
+                                       )
+
+               first_run = False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/equery/which.py b/gentoolkit/pym/gentoolkit/equery/which.py
new file mode 100644 (file)
index 0000000..38a8c6b
--- /dev/null
@@ -0,0 +1,102 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header: $
+
+"""Display the path to the ebuild that would be used by Portage with the current
+configuration
+"""
+
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import os
+import sys
+from getopt import gnu_getopt, GetoptError
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.equery import format_options, mod_usage
+from gentoolkit.helpers import find_packages
+
+# =======
+# Globals
+# =======
+
+QUERY_OPTS = {"includeMasked": False}
+
+# =========
+# Functions
+# =========
+
+def print_help(with_description=True):
+       """Print description, usage and a detailed help message.
+
+       @type with_description: bool
+       @param with_description: if true, print module's __doc__ string
+       """
+
+       if with_description:
+               print __doc__.strip()
+               print
+       print mod_usage(mod_name="which")
+       print
+       print pp.command("options")
+       print format_options((
+               (" -h, --help", "display this help message"),
+               (" -m, --include-masked", "return highest version ebuild available")
+       ))
+
+
+def parse_module_options(module_opts):
+       """Parse module options and update QUERY_OPTS"""
+
+       opts = (x[0] for x in module_opts)
+       for opt in opts:
+               if opt in ('-h', '--help'):
+                       print_help()
+                       sys.exit(0)
+               elif opt in ('-m', '--include-masked'):
+                       QUERY_OPTS['includeMasked'] = True
+
+
+def main(input_args):
+       """Parse input and run the program"""
+
+       short_opts = "hm"
+       long_opts = ('help', 'include-masked')
+
+       try:
+               module_opts, queries = gnu_getopt(input_args, short_opts, long_opts)
+       except GetoptError, err:
+               sys.stderr.write(pp.error("Module %s" % err))
+               print
+               print_help(with_description=False)
+               sys.exit(2)
+
+       parse_module_options(module_opts)
+
+       if not queries:
+               print_help()
+               sys.exit(2)
+
+       for query in queries:
+
+               matches = find_packages(query, QUERY_OPTS['includeMasked'])
+               if matches:
+                       pkg = sorted(matches).pop()
+                       ebuild_path = pkg.ebuild_path()
+                       if ebuild_path:
+                               print os.path.normpath(ebuild_path)
+                       else:
+                               sys.stderr.write(
+                                       pp.warn("No ebuilds to satisfy %s" % pkg.cpv.name)
+                               )
+               else:
+                       raise errors.GentoolkitNoMatches(query)
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/errors.py b/gentoolkit/pym/gentoolkit/errors.py
new file mode 100644 (file)
index 0000000..988d91c
--- /dev/null
@@ -0,0 +1,114 @@
+# Copyright(c) 2004-2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or later
+
+"""Exception classes for gentoolkit"""
+
+__all__ = (
+       'GentoolkitException',
+       'GentoolkitFatalError',
+       'GentoolkitAmbiguousPackage',
+       'GentoolkitInvalidAtom',
+       'GentoolkitInvalidCategory',
+       'GentoolkitInvalidPackage',
+       'GentoolkitInvalidCPV',
+       'GentoolkitInvalidRegex',
+       'GentoolkitInvalidVersion',
+       'GentoolkitNoMatches'
+)
+
+# ==========
+# Exceptions
+# ==========
+
+class GentoolkitException(Exception):
+       """Base class for gentoolkit exceptions."""
+       def __init__(self):
+               pass
+
+
+class GentoolkitFatalError(GentoolkitException):
+       """A fatal error occurred. Usually used to catch Portage exceptions."""
+       def __init__(self, err):
+               self.err = err
+
+       def __str__(self):
+               return "Fatal error: %s" % self.err
+
+
+class GentoolkitAmbiguousPackage(GentoolkitException):
+       """Got an ambiguous package name."""
+       def __init__(self, choices):
+               self.choices = choices
+
+       def __str__(self):
+               choices = '\n'.join("  %s" % x for x in self.choices)
+               return '\n'.join(("Ambiguous package name. Choose from:", choices))
+
+
+class GentoolkitInvalidAtom(GentoolkitException):
+       """Got a malformed package atom."""
+       def __init__(self, atom):
+               self.atom = atom
+
+       def __str__(self):
+               return "Invalid atom: '%s'" % self.atom
+
+
+class GentoolkitInvalidCategory(GentoolkitException):
+       """The category was not listed in portage.settings.categories."""
+       def __init__(self, category):
+               self.category = category
+
+       def __str__(self):
+               return "Invalid category: '%s'" % self.category
+
+
+class GentoolkitInvalidPackage(GentoolkitException):
+       """Got an unknown or invalid package."""
+       def __init__(self, package):
+               self.package = package
+
+       def __str__(self):
+               return "Invalid package: '%s'" % self.package
+
+
+class GentoolkitInvalidCPV(GentoolkitException):
+       """Got an invalid category/package-ver string."""
+       def __init__(self, cpv):
+               self.cpv = cpv
+
+       def __str__(self):
+               return "Invalid CPV: '%s'" % self.cpv
+
+
+class GentoolkitInvalidRegex(GentoolkitException):
+       """The regex could not be compiled."""
+       def __init__(self, regex):
+               self.regex = regex
+
+       def __str__(self):
+               return "Invalid regex: '%s'" % self.regex
+
+
+class GentoolkitInvalidVersion(GentoolkitException):
+       """Got a malformed version."""
+       def __init__(self, version):
+               self.version = version
+
+       def __str__(self):
+               return "Malformed version: '%s'" % self.version
+
+
+class GentoolkitNoMatches(GentoolkitException):
+       """No packages were found matching the search query."""
+       def __init__(self, query, in_installed=False):
+               self.query = query
+               self.in_installed = in_installed
+
+       def __str__(self):
+               inst = 'installed ' if self.in_installed else ''
+               return "No %spackages matching '%s'" % (inst, self.query)
+
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/glsa/__init__.py b/gentoolkit/pym/gentoolkit/glsa/__init__.py
new file mode 100644 (file)
index 0000000..11b7dbe
--- /dev/null
@@ -0,0 +1,726 @@
+# $Header$
+
+# This program is licensed under the GPL, version 2
+
+# WARNING: this code is only tested by a few people and should NOT be used
+# on production systems at this stage. There are possible security holes and probably
+# bugs in this code. If you test it please report ANY success or failure to
+# me (genone@gentoo.org).
+
+# The following planned features are currently on hold:
+# - getting GLSAs from http/ftp servers (not really useful without the fixed ebuilds)
+# - GPG signing/verification (until key policy is clear)
+
+__author__ = "Marius Mauch <genone@gentoo.org>"
+
+import os
+import sys
+import urllib
+import codecs
+import re
+import operator
+import xml.dom.minidom
+from StringIO import StringIO
+
+if sys.version_info[0:2] < (2,3):
+       raise NotImplementedError("Python versions below 2.3 have broken XML code " \
+                                                               +"and are not supported")
+
+try:
+       import portage
+except ImportError:
+       sys.path.insert(0, "/usr/lib/portage/pym")
+       import portage
+
+# Note: the space for rgt and rlt is important !!
+opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=",
+                        "rge": ">=~", "rle": "<=~", "rgt": " >~", "rlt": " <~"}
+NEWLINE_ESCAPE = "!;\\n"       # some random string to mark newlines that should be preserved
+SPACE_ESCAPE = "!;_"           # some random string to mark spaces that should be preserved
+
+def center(text, width):
+       """
+       Returns a string containing I{text} that is padded with spaces on both
+       sides. If C{len(text) >= width} I{text} is returned unchanged.
+
+       @type   text: String
+       @param  text: the text to be embedded
+       @type   width: Integer
+       @param  width: the minimum length of the returned string
+       @rtype:         String
+       @return:        the expanded string or I{text}
+       """
+       if len(text) >= width:
+               return text
+       margin = (width-len(text))/2
+       rValue = " "*margin
+       rValue += text
+       if 2*margin + len(text) == width:
+               rValue += " "*margin
+       elif 2*margin + len(text) + 1 == width:
+               rValue += " "*(margin+1)
+       return rValue
+
+
+def wrap(text, width, caption=""):
+       """
+       Wraps the given text at column I{width}, optionally indenting
+       it so that no text is under I{caption}. It's possible to encode
+       hard linebreaks in I{text} with L{NEWLINE_ESCAPE}.
+
+       @type   text: String
+       @param  text: the text to be wrapped
+       @type   width: Integer
+       @param  width: the column at which the text should be wrapped
+       @type   caption: String
+       @param  caption: this string is inserted at the beginning of the
+                                        return value and the paragraph is indented up to
+                                        C{len(caption)}.
+       @rtype:         String
+       @return:        the wrapped and indented paragraph
+       """
+       rValue = ""
+       line = caption
+       text = text.replace(2*NEWLINE_ESCAPE, NEWLINE_ESCAPE+" "+NEWLINE_ESCAPE)
+       words = text.split()
+       indentLevel = len(caption)+1
+
+       for w in words:
+               if line[-1] == "\n":
+                       rValue += line
+                       line = " "*indentLevel
+               if len(line)+len(w.replace(NEWLINE_ESCAPE, ""))+1 > width:
+                       rValue += line+"\n"
+                       line = " "*indentLevel+w.replace(NEWLINE_ESCAPE, "\n")
+               elif w.find(NEWLINE_ESCAPE) >= 0:
+                       if len(line.strip()) > 0:
+                               rValue += line+" "+w.replace(NEWLINE_ESCAPE, "\n")
+                       else:
+                               rValue += line+w.replace(NEWLINE_ESCAPE, "\n")
+                       line = " "*indentLevel
+               else:
+                       if len(line.strip()) > 0:
+                               line += " "+w
+                       else:
+                               line += w
+       if len(line) > 0:
+               rValue += line.replace(NEWLINE_ESCAPE, "\n")
+       rValue = rValue.replace(SPACE_ESCAPE, " ")
+       return rValue
+
+def checkconfig(myconfig):
+       """
+       takes a portage.config instance and adds GLSA specific keys if
+       they are not present. TO-BE-REMOVED (should end up in make.*)
+       """
+       mysettings = {
+               "GLSA_DIR": portage.settings["PORTDIR"]+"/metadata/glsa/",
+               "GLSA_PREFIX": "glsa-",
+               "GLSA_SUFFIX": ".xml",
+               "CHECKFILE": "/var/lib/portage/glsa_injected",
+               "GLSA_SERVER": "www.gentoo.org/security/en/glsa/",      # not completely implemented yet
+               "CHECKMODE": "local",                                                           # not completely implemented yet
+               "PRINTWIDTH": "76"
+       }
+       for k in mysettings.keys():
+               if k not in myconfig:
+                       myconfig[k] = mysettings[k]
+       return myconfig
+
+def get_glsa_list(repository, myconfig):
+       """
+       Returns a list of all available GLSAs in the given repository
+       by comparing the filelist there with the pattern described in
+       the config.
+
+       @type   repository: String
+       @param  repository: The directory or an URL that contains GLSA files
+                                               (Note: not implemented yet)
+       @type   myconfig: portage.config
+       @param  myconfig: a GLSA aware config instance (see L{checkconfig})
+
+       @rtype:         List of Strings
+       @return:        a list of GLSA IDs in this repository
+       """
+       # TODO: remote fetch code for listing
+
+       rValue = []
+
+       if not os.access(repository, os.R_OK):
+               return []
+       dirlist = os.listdir(repository)
+       prefix = myconfig["GLSA_PREFIX"]
+       suffix = myconfig["GLSA_SUFFIX"]
+
+       for f in dirlist:
+               try:
+                       if f[:len(prefix)] == prefix and f[-1*len(suffix):] == suffix:
+                               rValue.append(f[len(prefix):-1*len(suffix)])
+               except IndexError:
+                       pass
+       return rValue
+
+def getListElements(listnode):
+       """
+       Get all <li> elements for a given <ol> or <ul> node.
+
+       @type   listnode: xml.dom.Node
+       @param  listnode: <ul> or <ol> list to get the elements for
+       @rtype:         List of Strings
+       @return:        a list that contains the value of the <li> elements
+       """
+       if not listnode.nodeName in ["ul", "ol"]:
+               raise GlsaFormatException("Invalid function call: listnode is not <ul> or <ol>")
+       rValue = [getText(li, format="strip") \
+               for li in listnode.childNodes \
+               if li.nodeType == xml.dom.Node.ELEMENT_NODE]
+       return rValue
+
+def getText(node, format, textfd = None):
+       """
+       This is the main parser function. It takes a node and traverses
+       recursive over the subnodes, getting the text of each (and the
+       I{link} attribute for <uri> and <mail>). Depending on the I{format}
+       parameter the text might be formatted by adding/removing newlines,
+       tabs and spaces. This function is only useful for the GLSA DTD,
+       it's not applicable for other DTDs.
+
+       @type   node: xml.dom.Node
+       @param  node: the root node to start with the parsing
+       @type   format: String
+       @param  format: this should be either I{strip}, I{keep} or I{xml}
+                                       I{keep} just gets the text and does no formatting.
+                                       I{strip} replaces newlines and tabs with spaces and
+                                       replaces multiple spaces with one space.
+                                       I{xml} does some more formatting, depending on the
+                                       type of the encountered nodes.
+       @type   textfd: writable file-like object
+       @param  textfd: the file-like object to write the output to
+       @rtype:         String
+       @return:        the (formatted) content of the node and its subnodes
+                       except if textfd was not none
+       """
+       if not textfd:
+               textfd = StringIO()
+               returnNone = False
+       else:
+               returnNone = True
+       if format in ["strip", "keep"]:
+               if node.nodeName in ["uri", "mail"]:
+                       textfd.write(node.childNodes[0].data+": "+node.getAttribute("link"))
+               else:
+                       for subnode in node.childNodes:
+                               if subnode.nodeName == "#text":
+                                       textfd.write(subnode.data)
+                               else:
+                                       getText(subnode, format, textfd)
+       else: # format = "xml"
+               for subnode in node.childNodes:
+                       if subnode.nodeName == "p":
+                               for p_subnode in subnode.childNodes:
+                                       if p_subnode.nodeName == "#text":
+                                               textfd.write(p_subnode.data.strip())
+                                       elif p_subnode.nodeName in ["uri", "mail"]:
+                                               textfd.write(p_subnode.childNodes[0].data)
+                                               textfd.write(" ( "+p_subnode.getAttribute("link")+" )")
+                               textfd.write(NEWLINE_ESCAPE)
+                       elif subnode.nodeName == "ul":
+                               for li in getListElements(subnode):
+                                       textfd.write("-"+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" ")
+                       elif subnode.nodeName == "ol":
+                               i = 0
+                               for li in getListElements(subnode):
+                                       i = i+1
+                                       textfd.write(str(i)+"."+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" ")
+                       elif subnode.nodeName == "code":
+                               textfd.write(getText(subnode, format="keep").lstrip().replace("\n", NEWLINE_ESCAPE))
+                               textfd.write(NEWLINE_ESCAPE)
+                       elif subnode.nodeName == "#text":
+                               textfd.write(subnode.data)
+                       else:
+                               raise GlsaFormatException("Invalid Tag found: ", subnode.nodeName)
+       if returnNone:
+               return None
+       rValue = textfd.getvalue()
+       if format == "strip":
+               rValue = rValue.strip(" \n\t")
+               rValue = re.sub("[\s]{2,}", " ", rValue)
+       return rValue
+
+def getMultiTagsText(rootnode, tagname, format):
+       """
+       Returns a list with the text of all subnodes of type I{tagname}
+       under I{rootnode} (which itself is not parsed) using the given I{format}.
+
+       @type   rootnode: xml.dom.Node
+       @param  rootnode: the node to search for I{tagname}
+       @type   tagname: String
+       @param  tagname: the name of the tags to search for
+       @type   format: String
+       @param  format: see L{getText}
+       @rtype:         List of Strings
+       @return:        a list containing the text of all I{tagname} childnodes
+       """
+       rValue = [getText(e, format) \
+               for e in rootnode.getElementsByTagName(tagname)]
+       return rValue
+
+def makeAtom(pkgname, versionNode):
+       """
+       creates from the given package name and information in the
+       I{versionNode} a (syntactical) valid portage atom.
+
+       @type   pkgname: String
+       @param  pkgname: the name of the package for this atom
+       @type   versionNode: xml.dom.Node
+       @param  versionNode: a <vulnerable> or <unaffected> Node that
+                                                contains the version information for this atom
+       @rtype:         String
+       @return:        the portage atom
+       """
+       rValue = opMapping[versionNode.getAttribute("range")] \
+                               + pkgname \
+                               + "-" + getText(versionNode, format="strip")
+       try:
+               slot = versionNode.getAttribute("slot").strip()
+       except KeyError:
+               pass
+       else:
+               if slot and slot != "*":
+                       rValue += ":" + slot
+       return str(rValue)
+
+def makeVersion(versionNode):
+       """
+       creates from the information in the I{versionNode} a
+       version string (format <op><version>).
+
+       @type   versionNode: xml.dom.Node
+       @param  versionNode: a <vulnerable> or <unaffected> Node that
+                                                contains the version information for this atom
+       @rtype:         String
+       @return:        the version string
+       """
+       rValue = opMapping[versionNode.getAttribute("range")] \
+                       +getText(versionNode, format="strip")
+       try:
+               slot = versionNode.getAttribute("slot").strip()
+       except KeyError:
+               pass
+       else:
+               if slot and slot != "*":
+                       rValue += ":" + slot
+       return rValue
+
+def match(atom, portdbname, match_type="default"):
+       """
+       wrapper that calls revisionMatch() or portage.dbapi.match() depending on
+       the given atom.
+
+       @type   atom: string
+       @param  atom: a <~ or >~ atom or a normal portage atom that contains the atom to match against
+       @type   portdb: portage.dbapi
+       @param  portdb: one of the portage databases to use as information source
+       @type   match_type: string
+       @param  match_type: if != "default" passed as first argument to dbapi.xmatch
+                               to apply the wanted visibility filters
+
+       @rtype:         list of strings
+       @return:        a list with the matching versions
+       """
+       db = portage.db["/"][portdbname].dbapi
+       if atom[2] == "~":
+               return revisionMatch(atom, db, match_type=match_type)
+       elif match_type == "default" or not hasattr(db, "xmatch"):
+               return db.match(atom)
+       else:
+               return db.xmatch(match_type, atom)
+
+def revisionMatch(revisionAtom, portdb, match_type="default"):
+       """
+       handler for the special >~, >=~, <=~ and <~ atoms that are supposed to behave
+       as > and < except that they are limited to the same version, the range only
+       applies to the revision part.
+
+       @type   revisionAtom: string
+       @param  revisionAtom: a <~ or >~ atom that contains the atom to match against
+       @type   portdb: portage.dbapi
+       @param  portdb: one of the portage databases to use as information source
+       @type   match_type: string
+       @param  match_type: if != "default" passed as first argument to portdb.xmatch
+                               to apply the wanted visibility filters
+
+       @rtype:         list of strings
+       @return:        a list with the matching versions
+       """
+       if match_type == "default" or not hasattr(portdb, "xmatch"):
+               if ":" in revisionAtom:
+                       mylist = portdb.match(re.sub(r'-r[0-9]+(:[^ ]+)?$', r'\1', revisionAtom[2:]))
+               else:
+                       mylist = portdb.match(re.sub("-r[0-9]+$", "", revisionAtom[2:]))
+       else:
+               if ":" in revisionAtom:
+                       mylist = portdb.xmatch(match_type, re.sub(r'-r[0-9]+(:[^ ]+)?$', r'\1', revisionAtom[2:]))
+               else:
+                       mylist = portdb.xmatch(match_type, re.sub("-r[0-9]+$", "", revisionAtom[2:]))
+       rValue = []
+       for v in mylist:
+               r1 = portage.pkgsplit(v)[-1][1:]
+               r2 = portage.pkgsplit(revisionAtom[3:])[-1][1:]
+               if eval(r1+" "+revisionAtom[0:2]+" "+r2):
+                       rValue.append(v)
+       return rValue
+
+
+def getMinUpgrade(vulnerableList, unaffectedList, minimize=True):
+       """
+       Checks if the systemstate is matching an atom in
+       I{vulnerableList} and returns string describing
+       the lowest version for the package that matches an atom in
+       I{unaffectedList} and is greater than the currently installed
+       version. It will return an empty list if the system is affected,
+       and no upgrade is possible or None if the system is not affected.
+       Both I{vulnerableList} and I{unaffectedList} should have the
+       same base package.
+
+       @type   vulnerableList: List of Strings
+       @param  vulnerableList: atoms matching vulnerable package versions
+       @type   unaffectedList: List of Strings
+       @param  unaffectedList: atoms matching unaffected package versions
+       @type   minimize:       Boolean
+       @param  minimize:       True for a least-change upgrade, False for emerge-like algorithm
+
+       @rtype:         String | None
+       @return:        the lowest unaffected version that is greater than
+                               the installed version.
+       """
+       rValue = ""
+       v_installed = reduce(operator.add, [match(v, "vartree") for v in vulnerableList], [])
+       u_installed = reduce(operator.add, [match(u, "vartree") for u in unaffectedList], [])
+
+       # remove all unaffected atoms from vulnerable list
+       v_installed = list(set(v_installed).difference(set(u_installed)))
+
+       if not v_installed:
+               return None
+
+       # this tuple holds all vulnerable atoms, and the related upgrade atom
+       vuln_update = []
+       avail_updates = set()
+       for u in unaffectedList:
+               # TODO: This had match_type="match-all" before. I don't think it should
+               # since we disregarded masked items later anyway (match(=rValue, "porttree"))
+               avail_updates.update(match(u, "porttree"))
+       # if an atom is already installed, we should not consider it for upgrades
+       avail_updates.difference_update(u_installed)
+
+       for vuln in v_installed:
+               update = ""
+               for c in avail_updates:
+                       c_pv = portage.catpkgsplit(c)
+                       i_pv = portage.catpkgsplit(vuln)
+                       if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 \
+                                       and (update == "" \
+                                               or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(update)[1:]) > 0))) \
+                                       and portage.db["/"]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db["/"]["vartree"].dbapi.aux_get(vuln, ["SLOT"]):
+                               update = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
+                               if c_pv[3] != "r0":             # we don't like -r0 for display
+                                       update += "-"+c_pv[3]
+               vuln_update.append([vuln, update])
+
+       return vuln_update
+
+def format_date(datestr):
+       """
+       Takes a date (announced, revised) date from a GLSA and formats
+       it as readable text (i.e. "January 1, 2008").
+
+       @type   date: String
+       @param  date: the date string to reformat
+       @rtype:         String
+       @return:        a reformatted string, or the original string
+                               if it cannot be reformatted.
+       """
+       splitdate = datestr.split("-", 2)
+       if len(splitdate) != 3:
+               return datestr
+
+       # This cannot raise an error as we use () instead of []
+       splitdate = (int(x) for x in splitdate)
+
+       from datetime import date
+       try:
+               d = date(*splitdate)
+       except ValueError:
+               return datestr
+
+       # TODO We could format to local date format '%x' here?
+       return d.strftime("%B %d, %Y")
+
+# simple Exception classes to catch specific errors
+class GlsaTypeException(Exception):
+       def __init__(self, doctype):
+               Exception.__init__(self, "wrong DOCTYPE: %s" % doctype)
+
+class GlsaFormatException(Exception):
+       pass
+
+class GlsaArgumentException(Exception):
+       pass
+
+# GLSA xml data wrapper class
+class Glsa:
+       """
+       This class is a wrapper for the XML data and provides methods to access
+       and display the contained data.
+       """
+       def __init__(self, myid, myconfig):
+               """
+               Simple constructor to set the ID, store the config and gets the
+               XML data by calling C{self.read()}.
+
+               @type   myid: String
+               @param  myid: String describing the id for the GLSA object (standard
+                                         GLSAs have an ID of the form YYYYMM-nn) or an existing
+                                         filename containing a GLSA.
+               @type   myconfig: portage.config
+               @param  myconfig: the config that should be used for this object.
+               """
+               if re.match(r'\d{6}-\d{2}', myid):
+                       self.type = "id"
+               elif os.path.exists(myid):
+                       self.type = "file"
+               else:
+                       raise GlsaArgumentException("Given ID "+myid+" isn't a valid GLSA ID or filename.")
+               self.nr = myid
+               self.config = myconfig
+               self.read()
+
+       def read(self):
+               """
+               Here we build the filename from the config and the ID and pass
+               it to urllib to fetch it from the filesystem or a remote server.
+
+               @rtype:         None
+               @return:        None
+               """
+               if self.config["CHECKMODE"] == "local":
+                       repository = "file://" + self.config["GLSA_DIR"]
+               else:
+                       repository = self.config["GLSA_SERVER"]
+               if self.type == "file":
+                       myurl = "file://"+self.nr
+               else:
+                       myurl = repository + self.config["GLSA_PREFIX"] + str(self.nr) + self.config["GLSA_SUFFIX"]
+               self.parse(urllib.urlopen(myurl))
+               return None
+
+       def parse(self, myfile):
+               """
+               This method parses the XML file and sets up the internal data
+               structures by calling the different helper functions in this
+               module.
+
+               @type   myfile: String
+               @param  myfile: Filename to grab the XML data from
+               @rtype:         None
+               @returns:       None
+               """
+               self.DOM = xml.dom.minidom.parse(myfile)
+               if not self.DOM.doctype:
+                       raise GlsaTypeException(None)
+               elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa.dtd":
+                       self.dtdversion = 0
+               elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa-2.dtd":
+                       self.dtdversion = 2
+               else:
+                       raise GlsaTypeException(self.DOM.doctype.systemId)
+               myroot = self.DOM.getElementsByTagName("glsa")[0]
+               if self.type == "id" and myroot.getAttribute("id") != self.nr:
+                       raise GlsaFormatException("filename and internal id don't match:" + myroot.getAttribute("id") + " != " + self.nr)
+
+               # the simple (single, required, top-level, #PCDATA) tags first
+               self.title = getText(myroot.getElementsByTagName("title")[0], format="strip")
+               self.synopsis = getText(myroot.getElementsByTagName("synopsis")[0], format="strip")
+               self.announced = format_date(getText(myroot.getElementsByTagName("announced")[0], format="strip"))
+
+               count = 1
+               # Support both formats of revised:
+               # <revised>December 30, 2007: 02</revised>
+               # <revised count="2">2007-12-30</revised>
+               revisedEl = myroot.getElementsByTagName("revised")[0]
+               self.revised = getText(revisedEl, format="strip")
+               if (revisedEl.attributes.has_key("count")):
+                       count = revisedEl.getAttribute("count")
+               elif (self.revised.find(":") >= 0):
+                       (self.revised, count) = self.revised.split(":")
+
+               self.revised = format_date(self.revised)
+
+               try:
+                       self.count = int(count)
+               except ValueError:
+                       # TODO should this rais a GlsaFormatException?
+                       self.count = 1
+
+               # now the optional and 0-n toplevel, #PCDATA tags and references
+               try:
+                       self.access = getText(myroot.getElementsByTagName("access")[0], format="strip")
+               except IndexError:
+                       self.access = ""
+               self.bugs = getMultiTagsText(myroot, "bug", format="strip")
+               self.references = getMultiTagsText(myroot.getElementsByTagName("references")[0], "uri", format="keep")
+
+               # and now the formatted text elements
+               self.description = getText(myroot.getElementsByTagName("description")[0], format="xml")
+               self.workaround = getText(myroot.getElementsByTagName("workaround")[0], format="xml")
+               self.resolution = getText(myroot.getElementsByTagName("resolution")[0], format="xml")
+               self.impact_text = getText(myroot.getElementsByTagName("impact")[0], format="xml")
+               self.impact_type = myroot.getElementsByTagName("impact")[0].getAttribute("type")
+               try:
+                       self.background = getText(myroot.getElementsByTagName("background")[0], format="xml")
+               except IndexError:
+                       self.background = ""
+
+               # finally the interesting tags (product, affected, package)
+               self.glsatype = myroot.getElementsByTagName("product")[0].getAttribute("type")
+               self.product = getText(myroot.getElementsByTagName("product")[0], format="strip")
+               self.affected = myroot.getElementsByTagName("affected")[0]
+               self.packages = {}
+               for p in self.affected.getElementsByTagName("package"):
+                       name = p.getAttribute("name")
+                       if not self.packages.has_key(name):
+                               self.packages[name] = []
+                       tmp = {}
+                       tmp["arch"] = p.getAttribute("arch")
+                       tmp["auto"] = (p.getAttribute("auto") == "yes")
+                       tmp["vul_vers"] = [makeVersion(v) for v in p.getElementsByTagName("vulnerable")]
+                       tmp["unaff_vers"] = [makeVersion(v) for v in p.getElementsByTagName("unaffected")]
+                       tmp["vul_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("vulnerable")]
+                       tmp["unaff_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("unaffected")]
+                       self.packages[name].append(tmp)
+               # TODO: services aren't really used yet
+               self.services = self.affected.getElementsByTagName("service")
+               return None
+
+       def dump(self, outstream=sys.stdout, encoding="utf-8"):
+               """
+               Dumps a plaintext representation of this GLSA to I{outfile} or
+               B{stdout} if it is ommitted. You can specify an alternate
+               I{encoding} if needed (default is utf-8).
+
+               @type   outstream: File
+               @param  outfile: Stream that should be used for writing
+                                                (defaults to sys.stdout)
+               """
+               outstream = codecs.getwriter(encoding)(outstream)
+               width = int(self.config["PRINTWIDTH"])
+               outstream.write(center("GLSA %s: \n%s" % (self.nr, self.title), width)+"\n")
+               outstream.write((width*"=")+"\n")
+               outstream.write(wrap(self.synopsis, width, caption="Synopsis:         ")+"\n")
+               outstream.write("Announced on:      %s\n" % self.announced)
+               outstream.write("Last revised on:   %s : %02d\n\n" % (self.revised, self.count))
+               if self.glsatype == "ebuild":
+                       for k in self.packages.keys():
+                               pkg = self.packages[k]
+                               for path in pkg:
+                                       vul_vers = "".join(path["vul_vers"])
+                                       unaff_vers = "".join(path["unaff_vers"])
+                                       outstream.write("Affected package:  %s\n" % k)
+                                       outstream.write("Affected archs:    ")
+                                       if path["arch"] == "*":
+                                               outstream.write("All\n")
+                                       else:
+                                               outstream.write("%s\n" % path["arch"])
+                                       outstream.write("Vulnerable:        %s\n" % vul_vers)
+                                       outstream.write("Unaffected:        %s\n\n" % unaff_vers)
+               elif self.glsatype == "infrastructure":
+                       pass
+               if len(self.bugs) > 0:
+                       outstream.write("\nRelated bugs:      ")
+                       outstream.write(", ".join(self.bugs))
+                       outstream.write("\n")
+               if self.background:
+                       outstream.write("\n"+wrap(self.background, width, caption="Background:       "))
+               outstream.write("\n"+wrap(self.description, width, caption="Description:      "))
+               outstream.write("\n"+wrap(self.impact_text, width, caption="Impact:           "))
+               outstream.write("\n"+wrap(self.workaround, width, caption="Workaround:       "))
+               outstream.write("\n"+wrap(self.resolution, width, caption="Resolution:       "))
+               myreferences = " ".join(r.replace(" ", SPACE_ESCAPE)+NEWLINE_ESCAPE for r in self.references)
+               outstream.write("\n"+wrap(myreferences, width, caption="References:       "))
+               outstream.write("\n")
+
+       def isVulnerable(self):
+               """
+               Tests if the system is affected by this GLSA by checking if any
+               vulnerable package versions are installed. Also checks for affected
+               architectures.
+
+               @rtype:         Boolean
+               @returns:       True if the system is affected, False if not
+               """
+               rValue = False
+               for k in self.packages.keys():
+                       pkg = self.packages[k]
+                       for path in pkg:
+                               if path["arch"] == "*" or self.config["ARCH"] in path["arch"].split():
+                                       for v in path["vul_atoms"]:
+                                               rValue = rValue \
+                                                       or (None != getMinUpgrade([v,], path["unaff_atoms"]))
+               return rValue
+
+       def isInjected(self):
+               """
+               Looks if the GLSA ID is in the GLSA checkfile to check if this
+               GLSA should be marked as applied.
+
+               @rtype:         Boolean
+               @returns:       True if the GLSA is in the inject file, False if not
+               """
+               if not os.access(self.config["CHECKFILE"], os.R_OK):
+                       return False
+               aList = portage.grabfile(self.config["CHECKFILE"])
+               return (self.nr in aList)
+
+       def inject(self):
+               """
+               Puts the ID of this GLSA into the GLSA checkfile, so it won't
+               show up on future checks. Should be called after a GLSA is
+               applied or on explicit user request.
+
+               @rtype:         None
+               @returns:       None
+               """
+               if not self.isInjected():
+                       checkfile = open(self.config["CHECKFILE"], "a+")
+                       checkfile.write(self.nr+"\n")
+                       checkfile.close()
+               return None
+
+       def getMergeList(self, least_change=True):
+               """
+               Returns the list of package-versions that have to be merged to
+               apply this GLSA properly. The versions are as low as possible
+               while avoiding downgrades (see L{getMinUpgrade}).
+
+               @type   least_change: Boolean
+               @param  least_change: True if the smallest possible upgrade should be selected,
+                                       False for an emerge-like algorithm
+               @rtype:         List of Strings
+               @return:        list of package-versions that have to be merged
+               """
+               return list(set(update for (vuln, update) in self.getAffectionTable(least_change) if update))
+
+       def getAffectionTable(self, least_change=True):
+               """
+               Will initialize the self.systemAffection list of
+               atoms installed on the system that are affected
+               by this GLSA, and the atoms that are minimal upgrades.
+               """
+               systemAffection = []
+               for pkg in self.packages.keys():
+                       for path in self.packages[pkg]:
+                               update = getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], minimize=least_change)
+                               if update:
+                                       systemAffection.extend(update)
+               return systemAffection
diff --git a/gentoolkit/pym/gentoolkit/helpers.py b/gentoolkit/pym/gentoolkit/helpers.py
new file mode 100644 (file)
index 0000000..6956f97
--- /dev/null
@@ -0,0 +1,709 @@
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2 or higher
+#
+# $Header$
+
+"""Improved versions of the original helpers functions.
+
+As a convention, functions ending in '_packages' or '_match{es}' return
+Package objects, while functions ending in 'cpvs' return a sequence of strings.
+Functions starting with 'get_' return a set of packages by default and can be
+filtered, while functions starting with 'find_' return nothing unless the
+query matches one or more packages.
+"""
+
+# Move to Imports section after Python 2.6 is stable
+from __future__ import with_statement
+
+__all__ = (
+       'ChangeLog',
+       'FileOwner',
+       'compare_package_strings',
+       'do_lookup',
+       'find_best_match',
+       'find_installed_packages',
+       'find_packages',
+       'get_cpvs',
+       'get_installed_cpvs',
+       'get_uninstalled_cpvs',
+       'uniqify',
+       'uses_globbing',
+       'split_cpv'
+)
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import fnmatch
+import os
+import re
+from functools import partial
+from itertools import chain
+
+import portage
+from portage.versions import catpkgsplit, pkgcmp
+
+from gentoolkit import pprinter as pp
+from gentoolkit import CONFIG
+from gentoolkit import errors
+from gentoolkit.atom import Atom
+from gentoolkit.cpv import CPV
+from gentoolkit.dbapi import PORTDB, VARDB
+from gentoolkit.versionmatch import VersionMatch
+# This has to be imported below to stop circular import.
+#from gentoolkit.package import Package
+
+# =======
+# Classes
+# =======
+
+class ChangeLog(object):
+       """Provides methods for working with a Gentoo ChangeLog file.
+
+       Example usage:
+               >>> from gentoolkit.helpers import ChangeLog
+               >>> portage = ChangeLog('/usr/portage/sys-apps/portage/ChangeLog')
+               >>> print portage.latest.strip()
+               *portage-2.2_rc50 (15 Nov 2009)
+
+                 15 Nov 2009; Zac Medico <zmedico@gentoo.org> +portage-2.2_rc50.ebuild:
+                 2.2_rc50 bump. This includes all fixes in 2.1.7.5.
+               >>> len(portage.full)
+               75
+               >>> len(portage.entries_matching_range(
+               ...     from_ver='2.2_rc40',
+               ...     to_ver='2.2_rc50'))
+               11
+
+       """
+       def __init__(self, changelog_path, invalid_entry_is_fatal=False):
+               if not (os.path.isfile(changelog_path) and
+                       os.access(changelog_path, os.R_OK)):
+                       raise errors.GentoolkitFatalError(
+                               "%s does not exist or is unreadable" % pp.path(changelog_path)
+                       )
+               self.changelog_path = changelog_path
+               self.invalid_entry_is_fatal = invalid_entry_is_fatal
+
+               # Process the ChangeLog:
+               self.entries = self._split_changelog()
+               self.indexed_entries = self._index_changelog()
+               self.full = self.entries
+               self.latest = self.entries[0]
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, self.changelog_path)
+
+       def entries_matching_atom(self, atom):
+               """Return entries whose header versions match atom's version.
+
+               @type atom: L{gentoolkit.atom.Atom} or str
+               @param atom: a atom to find matching entries against
+               @rtype: list
+               @return: entries matching atom
+               @raise errors.GentoolkitInvalidAtom: if atom is a string and malformed
+               """
+               result = []
+
+               if not isinstance(atom, Atom):
+                       atom = Atom(atom)
+
+               for entry_set in self.indexed_entries:
+                       i, entry = entry_set
+                       # VersionMatch doesn't store .cp, so we'll force it to match here:
+                       i.cpv.cp = atom.cpv.cp
+                       if atom.intersects(i):
+                               result.append(entry)
+
+               return result
+
+       def entries_matching_range(self, from_ver=None, to_ver=None):
+               """Return entries whose header versions are within a range of versions.
+
+               @type from_ver: str
+               @param from_ver: valid Gentoo version
+               @type to_ver: str
+               @param to_ver: valid Gentoo version
+               @rtype: list
+               @return: entries between from_ver and to_ver
+               @raise errors.GentoolkitFatalError: if neither vers are set
+               @raise errors.GentoolkitInvalidVersion: if either ver is invalid
+               """
+               result = []
+
+               # Make sure we have at least one version set
+               if not (from_ver or to_ver):
+                       raise errors.GentoolkitFatalError(
+                               "Need to specifiy 'from_ver' or 'to_ver'"
+                       )
+
+               # Create a VersionMatch instance out of from_ver
+               from_restriction = None
+               if from_ver:
+                       try:
+                               from_ver_rev = CPV("null-%s" % from_ver)
+                       except errors.GentoolkitInvalidCPV:
+                               raise errors.GentoolkitInvalidVersion(from_ver)
+                       from_restriction = VersionMatch(from_ver_rev, op='>=')
+
+               # Create a VersionMatch instance out of to_ver
+               to_restriction = None
+               if to_ver:
+                       try:
+                               to_ver_rev = CPV("null-%s" % to_ver)
+                       except errors.GentoolkitInvalidCPV:
+                               raise errors.GentoolkitInvalidVersion(to_ver)
+                       to_restriction = VersionMatch(to_ver_rev, op='<=')
+
+               # Add entry to result if version ranges intersect it
+               for entry_set in self.indexed_entries:
+                       i, entry = entry_set
+                       if from_restriction and not from_restriction.match(i):
+                               continue
+                       if to_restriction and not to_restriction.match(i):
+                               continue
+                       result.append(entry)
+
+               return result
+
+       def _index_changelog(self):
+               """Use the output of L{self._split_changelog} to create an index list
+               of L{gentoolkit.versionmatch.VersionMatch} objects.
+
+               @rtype: list
+               @return: tuples containing a VersionMatch instance for the release
+                       version of each entry header as the first item and the entire entry
+                       as the second item
+               @raise ValueError: if self.invalid_entry_is_fatal is True and we hit an
+                       invalid entry
+               """
+
+               result = []
+               for entry in self.entries:
+                       # Extract the package name from the entry header, ex:
+                       # *xterm-242 (07 Mar 2009) => xterm-242
+                       pkg_name = entry.split(' ', 1)[0].lstrip('*')
+                       if not pkg_name.strip():
+                               continue
+                       try:
+                               entry_ver = CPV(pkg_name)
+                       except errors.GentoolkitInvalidCPV:
+                               if self.invalid_entry_is_fatal:
+                                       raise ValueError(entry_ver)
+                               continue
+
+                       result.append((VersionMatch(entry_ver, op='='), entry))
+
+               return result
+
+       def _split_changelog(self):
+               """Split the ChangeLog into individual entries.
+
+               @rtype: list
+               @return: individual ChangeLog entries
+               """
+
+               result = []
+               partial_entries = []
+               with open(self.changelog_path) as log:
+                       for line in log:
+                               if line.startswith('#'):
+                                       continue
+                               elif line.startswith('*'):
+                                       # Append last entry to result...
+                                       entry = ''.join(partial_entries)
+                                       if entry and not entry.isspace():
+                                               result.append(entry)
+                                       # ... and start a new entry
+                                       partial_entries = [line]
+                               else:
+                                       partial_entries.append(line)
+                       else:
+                               # Append the final entry
+                               entry = ''.join(partial_entries)
+                               result.append(entry)
+
+               return result
+
+
+class FileOwner(object):
+       """Creates a function for locating the owner of filename queries.
+
+       Example usage:
+               >>> from gentoolkit.helpers import FileOwner
+               >>> findowner = FileOwner()
+               >>> findowner(('/usr/bin/vim',))
+               [(<Package app-editors/vim-7.2.182>, '/usr/bin/vim')]
+       """
+       def __init__(self, is_regex=False, early_out=False, printer_fn=None):
+               """Instantiate function.
+
+               @type is_regex: bool
+               @param is_regex: funtion args are regular expressions
+               @type early_out: bool
+               @param early_out: return when first result is found (safe)
+               @type printer_fn: callable
+               @param printer_fn: If defined, will be passed useful information for
+                       printing each result as it is found.
+               """
+               self.is_regex = is_regex
+               self.early_out = early_out
+               self.printer_fn = printer_fn
+
+       def __call__(self, queries):
+               """Run the function.
+
+               @type queries: iterable
+               @param queries: filepaths or filepath regexes
+               """
+               query_re_string = self._prepare_search_regex(queries)
+               try:
+                       query_re = re.compile(query_re_string)
+               except (TypeError, re.error), err:
+                       raise errors.GentoolkitInvalidRegex(err)
+
+               use_match = False
+               if ((self.is_regex or query_re_string.startswith('^\/'))
+                       and '|' not in query_re_string ):
+                       # If we were passed a regex or a single path starting with root,
+                       # we can use re.match, else use re.search.
+                       use_match = True
+
+               pkgset = get_installed_cpvs()
+
+               return self.find_owners(query_re, use_match=use_match, pkgset=pkgset)
+
+       def find_owners(self, query_re, use_match=False, pkgset=None):
+               """Find owners and feed data to supplied output function.
+
+               @type query_re: _sre.SRE_Pattern
+               @param query_re: file regex
+               @type use_match: bool
+               @param use_match: use re.match or re.search
+               @type pkgset: iterable or None
+               @param pkgset: list of packages to look through
+               """
+               # FIXME: Remove when lazyimport supports objects:
+               from gentoolkit.package import Package
+
+               if use_match:
+                       query_fn = query_re.match
+               else:
+                       query_fn = query_re.search
+
+               results = []
+               found_match = False
+               for pkg in sorted([Package(x) for x in pkgset]):
+                       files = pkg.parsed_contents()
+                       for cfile in files:
+                               match = query_fn(cfile)
+                               if match:
+                                       results.append((pkg, cfile))
+                                       if self.printer_fn is not None:
+                                               self.printer_fn(pkg, cfile)
+                                       if self.early_out:
+                                               found_match = True
+                                               break
+                       if found_match:
+                               break
+               return results
+
+       @staticmethod
+       def extend_realpaths(paths):
+               """Extend a list of paths with the realpaths for any symlinks.
+
+               @type paths: list
+               @param paths: file path strs
+               @rtype: list
+               @return: the original list plus the realpaths for any symlinks
+                       so long as the realpath doesn't already exist in the list
+               @raise AttributeError: if paths does not have attribute 'extend'
+               """
+
+               osp = os.path
+               paths.extend([osp.realpath(x) for x in paths
+                       if osp.islink(x) and osp.realpath(x) not in paths])
+
+               return paths
+
+       def _prepare_search_regex(self, queries):
+               """Create a regex out of the queries"""
+
+               queries = list(queries)
+               if self.is_regex:
+                       return '|'.join(queries)
+               else:
+                       result = []
+                       # Trim trailing and multiple slashes from queries
+                       slashes = re.compile('/+')
+                       queries = self.extend_realpaths(queries)
+                       for query in queries:
+                               query = slashes.sub('/', query).rstrip('/')
+                               if query.startswith('/'):
+                                       query = "^%s$" % re.escape(query)
+                               else:
+                                       query = "/%s$" % re.escape(query)
+                               result.append(query)
+               result = "|".join(result)
+               return result
+
+# =========
+# Functions
+# =========
+
+def compare_package_strings(pkg1, pkg2):
+       """Similar to the builtin cmp, but for package strings. Usually called
+       as: package_list.sort(compare_package_strings)
+
+       An alternative is to use the CPV descriptor from gentoolkit.cpv:
+       >>> cpvs = sorted(CPV(x) for x in package_list)
+
+       @see: >>> help(cmp)
+       """
+
+       pkg1 = catpkgsplit(pkg1)
+       pkg2 = catpkgsplit(pkg2)
+       if pkg1[0] != pkg2[0]:
+               return cmp(pkg1[0], pkg2[0])
+       elif pkg1[1] != pkg2[1]:
+               return cmp(pkg1[1], pkg2[1])
+       else:
+               return pkgcmp(pkg1[1:], pkg2[1:])
+
+
+def do_lookup(query, query_opts):
+       """A high-level wrapper around gentoolkit package-finder functions.
+
+       @type query: str
+       @param query: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom, glob or regex
+       @type query_opts: dict
+       @param query_opts: user-configurable options from the calling module
+               Currently supported options are:
+
+               includeInstalled   = bool
+               includePortTree    = bool
+               includeOverlayTree = bool
+               isRegex            = bool
+               printMatchInfo     = bool           # Print info about the search
+
+       @rtype: list
+       @return: Package objects matching query
+       """
+
+       if query_opts["includeInstalled"]:
+               if query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
+                       simple_package_finder = partial(find_packages, include_masked=True)
+                       complex_package_finder = get_cpvs
+               else:
+                       simple_package_finder = find_installed_packages
+                       complex_package_finder = get_installed_cpvs
+       elif query_opts["includePortTree"] or query_opts["includeOverlayTree"]:
+               simple_package_finder = partial(find_packages, include_masked=True)
+               complex_package_finder = get_uninstalled_cpvs
+       else:
+               raise errors.GentoolkitFatalError(
+                       "Not searching in installed, Portage tree, or overlay. "
+                       "Nothing to do."
+               )
+
+       is_simple_query = True
+       if query_opts["isRegex"] or uses_globbing(query):
+               is_simple_query = False
+
+       if is_simple_query:
+               matches = _do_simple_lookup(query, simple_package_finder, query_opts)
+       else:
+               matches = _do_complex_lookup(query, complex_package_finder, query_opts)
+
+       return matches
+
+
+def _do_complex_lookup(query, package_finder, query_opts):
+       """Find matches for a query which is a regex or includes globbing."""
+
+       # FIXME: Remove when lazyimport supports objects:
+       from gentoolkit.package import Package
+
+       result = []
+
+       if query_opts["printMatchInfo"] and not CONFIG["piping"]:
+               print_query_info(query, query_opts)
+
+       cat = split_cpv(query)[0]
+
+       pre_filter = []
+       # The "get_" functions can pre-filter against the whole package key,
+       # but since we allow globbing now, we run into issues like:
+       # >>> portage.dep.dep_getkey("sys-apps/portage-*")
+       # 'sys-apps/portage-'
+       # So the only way to guarantee we don't overrun the key is to
+       # prefilter by cat only.
+       if cat:
+               if query_opts["isRegex"]:
+                       cat_re = cat
+               else:
+                       cat_re = fnmatch.translate(cat)
+                       # [::-1] reverses a sequence, so we're emulating an ".rreplace()"
+                       # except we have to put our "new" string on backwards
+                       cat_re = cat_re[::-1].replace('$', '*./', 1)[::-1]
+               predicate = lambda x: re.match(cat_re, x)
+               pre_filter = package_finder(predicate=predicate)
+
+       # Post-filter
+       if query_opts["isRegex"]:
+               predicate = lambda x: re.search(query, x)
+       else:
+               if cat:
+                       query_re = fnmatch.translate(query)
+               else:
+                       query_re = fnmatch.translate("*/%s" % query)
+               predicate = lambda x: re.search(query_re, x)
+       if pre_filter:
+               result = [x for x in pre_filter if predicate(x)]
+       else:
+               result = package_finder(predicate=predicate)
+
+       return [Package(x) for x in result]
+
+
+def _do_simple_lookup(query, package_finder, query_opts):
+       """Find matches for a query which is an atom or string."""
+
+       result = []
+
+       if query_opts["printMatchInfo"] and CONFIG['verbose']:
+               print_query_info(query, query_opts)
+
+       result = package_finder(query)
+       if not query_opts["includeInstalled"]:
+               result = [x for x in result if not x.is_installed()]
+
+       return result
+
+
+def find_best_match(query):
+       """Return the highest unmasked version of a package matching query.
+
+       @type query: str
+       @param query: can be of the form: pkg, pkg-ver, cat/pkg, cat/pkg-ver, atom
+       @rtype: str or None
+       @raise portage.exception.InvalidAtom: if query is not valid input
+       """
+       # FIXME: Remove when lazyimport supports objects:
+       from gentoolkit.package import Package
+
+       try:
+               match = PORTDB.xmatch("bestmatch-visible", query)
+       except portage.exception.InvalidAtom, err:
+               raise errors.GentoolkitInvalidAtom(err)
+
+       return Package(match) if match else None
+
+
+def find_installed_packages(query):
+       """Return a list of Package objects that matched the search key."""
+       # FIXME: Remove when lazyimport supports objects:
+       from gentoolkit.package import Package
+
+       try:
+               matches = VARDB.match(query)
+       # catch the ambiguous package Exception
+       except portage.exception.AmbiguousPackageName, err:
+               matches = []
+               for pkgkey in err[0]:
+                       matches.extend(VARDB.match(pkgkey))
+       except portage.exception.InvalidAtom, err:
+               raise errors.GentoolkitInvalidAtom(err)
+
+       return [Package(x) for x in matches]
+
+
+def find_packages(query, include_masked=False):
+       """Returns a list of Package objects that matched the query.
+
+       @type query: str
+       @param query: can be of the form: pkg, pkg-ver, cat/pkg, cat/pkg-ver, atom
+       @type include_masked: bool
+       @param include_masked: include masked packages
+       @rtype: list
+       @return: matching Package objects
+       """
+       # FIXME: Remove when lazyimport supports objects:
+       from gentoolkit.package import Package
+
+       if not query:
+               return []
+
+       try:
+               if include_masked:
+                       matches = PORTDB.xmatch("match-all", query)
+               else:
+                       matches = PORTDB.match(query)
+               matches.extend(VARDB.match(query))
+       except portage.exception.InvalidAtom, err:
+               raise errors.GentoolkitInvalidAtom(str(err))
+
+       return [Package(x) for x in set(matches)]
+
+
+def get_cpvs(predicate=None, include_installed=True):
+       """Get all packages in the Portage tree and overlays. Optionally apply a
+       predicate.
+
+       Example usage:
+               >>> from gentoolkit.helpers import get_cpvs
+               >>> len(set(get_cpvs()))
+               26065
+               >>> fn = lambda x: x.startswith('app-portage')
+               >>> len(get_cpvs(fn, include_installed=False))
+               112
+
+       @type predicate: function
+       @param predicate: a function to filter the package list with
+       @type include_installed: bool
+       @param include_installed:
+               If True: Return the union of all_cpvs and all_installed_cpvs
+               If False: Return the difference of all_cpvs and all_installed_cpvs
+       @rtype: generator
+       @return: a generator that yields unsorted cat/pkg-ver strings from the
+               Portage tree
+       """
+
+       if predicate:
+               all_cps = iter(x for x in PORTDB.cp_all() if predicate(x))
+       else:
+               all_cps = PORTDB.cp_all()
+
+       all_cpvs = chain.from_iterable(PORTDB.cp_list(x) for x in all_cps)
+       all_installed_cpvs = get_installed_cpvs(predicate)
+
+       if include_installed:
+               for cpv in chain(all_cpvs, all_installed_cpvs):
+                       yield cpv
+       else:
+               # Consume the smaller pkg set:
+               installed_cpvs = set(all_installed_cpvs)
+               for cpv in all_cpvs:
+                       if cpv not in installed_cpvs:
+                               yield cpv
+
+
+# pylint thinks this is a global variable
+# pylint: disable-msg=C0103
+get_uninstalled_cpvs = partial(get_cpvs, include_installed=False)
+
+
+def get_installed_cpvs(predicate=None):
+       """Get all installed packages. Optionally apply a predicate.
+
+       @type predicate: function
+       @param predicate: a function to filter the package list with
+       @rtype: generator
+       @return: a generator that yields unsorted installed cat/pkg-ver strings
+               from VARDB
+       """
+
+       if predicate:
+               installed_cps = iter(x for x in VARDB.cp_all() if predicate(x))
+       else:
+               installed_cps = VARDB.cp_all()
+
+       for cpv in chain.from_iterable(VARDB.cp_list(x) for x in installed_cps):
+               yield cpv
+
+
+def print_query_info(query, query_opts):
+       """Print info about the query to the screen."""
+
+       cat, pkg = split_cpv(query)[:2]
+       if cat and not query_opts["isRegex"]:
+               cat_str = "in %s " % pp.emph(cat.lstrip('><=~!'))
+       else:
+               cat_str = ""
+
+       if query_opts["isRegex"]:
+               pkg_str = query
+       else:
+               pkg_str = pkg
+
+       print " * Searching for %s %s..." % (pp.emph(pkg_str), cat_str)
+
+
+def print_file(path):
+       """Display the contents of a file."""
+
+       with open(path) as open_file:
+               lines = open_file.read()
+               print lines.strip()
+
+
+def print_sequence(seq):
+       """Print every item of a sequence."""
+
+       for item in seq:
+               print item
+
+
+def split_cpv(query):
+       """Split a cpv into category, name, version and revision.
+
+       @type query: str
+       @param query: pkg, cat/pkg, pkg-ver, cat/pkg-ver, atom or regex
+       @rtype: tuple
+       @return: (category, pkg_name, version, revision)
+               Each tuple element is a string or empty string ("").
+       """
+
+       result = catpkgsplit(query)
+
+       if result:
+               result = list(result)
+               if result[0] == 'null':
+                       result[0] = ''
+               if result[3] == 'r0':
+                       result[3] = ''
+       else:
+               result = query.split("/")
+               if len(result) == 1:
+                       result = ['', query, '', '']
+               else:
+                       result = result + ['', '']
+
+       if len(result) != 4:
+               raise errors.GentoolkitInvalidPackageName(query)
+
+       return tuple(result)
+
+
+def uniqify(seq, preserve_order=True):
+       """Return a uniqified list. Optionally preserve order."""
+
+       if preserve_order:
+               seen = set()
+               result = [x for x in seq if x not in seen and not seen.add(x)]
+       else:
+               result = list(set(seq))
+
+       return result
+
+
+def uses_globbing(query):
+       """Check the query to see if it is using globbing.
+
+       @type query: str
+       @param query: user input package query
+       @rtype: bool
+       @return: True if query uses globbing, else False
+       """
+
+       if set('!*?[]').intersection(query):
+               # Is query an atom such as '=sys-apps/portage-2.2*'?
+               if query[0] != '=':
+                       return True
+
+       return False
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/metadata.py b/gentoolkit/pym/gentoolkit/metadata.py
new file mode 100644 (file)
index 0000000..792e65f
--- /dev/null
@@ -0,0 +1,302 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Provides an easy-to-use python interface to Gentoo's metadata.xml file.
+
+       Example usage:
+               >>> from gentoolkit.metadata import MetaData
+               >>> pkg_md = MetaData('/usr/portage/app-misc/gourmet/metadata.xml')
+               >>> pkg_md
+               <MetaData '/usr/portage/app-misc/gourmet/metadata.xml'>
+               >>> pkg_md.herds()
+               ['no-herd']
+               >>> for maint in pkg_md.maintainers():
+               ...     print "{0} ({1})".format(maint.email, maint.name)
+               ...
+               nixphoeni@gentoo.org (Joe Sapp)
+               >>> for flag in pkg_md.use():
+               ...     print flag.name, "->", flag.description
+               ...
+               rtf -> Enable export to RTF
+               gnome-print -> Enable printing support using gnome-print
+               >>> upstream = pkg_md.upstream()
+               >>> upstream
+               [<_Upstream {'docs': [], 'remoteid': [], 'maintainer':
+                [<_Maintainer 'Thomas_Hinkle@alumni.brown.edu'>], 'bugtracker': [],
+                'changelog': []}>]
+               >>> upstream[0].maintainer[0].name
+               'Thomas Mills Hinkle'
+"""
+
+# Move to Imports section after Python-2.6 is stable
+from __future__ import with_statement
+
+__all__ = ('MetaData',)
+__docformat__ = 'epytext'
+
+# =======
+# Imports
+# =======
+
+import re
+import os
+import xml.etree.cElementTree as etree
+
+from portage import settings
+
+# =======
+# Classes
+# =======
+
+class _Maintainer(object):
+       """An object for representing one maintainer.
+
+       @type email: str or None
+       @ivar email: Maintainer's email address. Used for both Gentoo and upstream.
+       @type name: str or None
+       @ivar name: Maintainer's name. Used for both Gentoo and upstream.
+       @type description: str or None
+       @ivar description: Description of what a maintainer does. Gentoo only.
+       @type restrict: str or None
+       @ivar restrict: e.g. &gt;=portage-2.2 means only maintains versions
+               of Portage greater than 2.2.
+       @type status: str or None
+       @ivar status: If set, either 'active' or 'inactive'. Upstream only.
+       """
+
+       def __init__(self, node):
+               self.email = None
+               self.name = None
+               self.description = None
+               self.restrict = node.get('restrict')
+               self.status = node.get('status')
+               maint_attrs = node.getchildren()
+               for attr in maint_attrs:
+                       setattr(self, attr.tag, attr.text)
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, self.email)
+
+
+class _Useflag(object):
+       """An object for representing one USE flag.
+
+       @todo: Is there any way to have a keyword option to leave in
+               <pkg> and <cat> for later processing?
+       @type name: str or None
+       @ivar name: USE flag
+       @type restrict: str or None
+       @ivar restrict: e.g. &gt;=portage-2.2 means flag is only avaiable in
+               versions greater than 2.2
+       @type description: str
+       @ivar description: description of the USE flag
+       """
+
+       def __init__(self, node):
+               self.name = node.get('name')
+               self.restrict = node.get('restrict')
+               _desc = ''
+               if node.text:
+                       _desc = node.text
+               for child in node.getchildren():
+                       _desc += child.text if child.text else ''
+                       _desc += child.tail if child.tail else ''
+               # This takes care of tabs and newlines left from the file
+               self.description = re.sub('\s+', ' ', _desc)
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, self.name)
+
+
+class _Upstream(object):
+       """An object for representing one package's upstream.
+
+       @type maintainers: list
+       @ivar maintainers: L{_Maintainer} objects for each upstream maintainer
+       @type changelogs: list
+       @ivar changelogs: URLs to upstream's ChangeLog file in str format
+       @type docs: list
+       @ivar docs: Sequence of tuples containing URLs to upstream documentation
+               in the first slot and 'lang' attribute in the second, e.g.,
+               [('http.../docs/en/tut.html', None), ('http.../doc/fr/tut.html', 'fr')]
+       @type bugtrackers: list
+       @ivar bugtrackers: URLs to upstream's bugtracker. May also contain an email
+               address if prepended with 'mailto:'
+       @type remoteids: list
+       @ivar remoteids: Sequence of tuples containing the project's hosting site
+               name in the first slot and the project's ID name or number for that
+               site in the second, e.g., [('sourceforge', 'systemrescuecd')]
+       """
+
+       def __init__(self, node):
+               self.node = node
+               self.maintainers = self.upstream_maintainers()
+               self.changelogs = self.upstream_changelogs()
+               self.docs = self.upstream_documentation()
+               self.bugtrackers = self.upstream_bugtrackers()
+               self.remoteids = self.upstream_remoteids()
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, self.__dict__)
+
+       def upstream_bugtrackers(self):
+               """Retrieve upstream bugtracker location from xml node."""
+               return [e.text for e in self.node.findall('bugs-to')]
+
+       def upstream_changelogs(self):
+               """Retrieve upstream changelog location from xml node."""
+               return [e.text for e in self.node.findall('changelog')]
+
+       def upstream_documentation(self):
+               """Retrieve upstream documentation location from xml node."""
+               result = []
+               for elem in self.node.findall('doc'):
+                       lang = elem.get('lang')
+                       result.append((elem.text, lang))
+               return result
+
+       def upstream_maintainers(self):
+               """Retrieve upstream maintainer information from xml node."""
+               return [_Maintainer(m) for m in self.node.findall('maintainer')]
+
+       def upstream_remoteids(self):
+               """Retrieve upstream remote ID from xml node."""
+               return [(e.text, e.get('type')) for e in self.node.findall('remote-id')]
+
+
+class MetaData(object):
+       """Access metadata.xml"""
+
+       def __init__(self, metadata_path):
+               """Parse a valid metadata.xml file.
+
+               @type metadata_path: str
+               @param metadata_path: path to a valid metadata.xml file
+               @raise IOError: if C{metadata_path} can not be read
+               """
+
+               self.metadata_path = metadata_path
+               self._xml_tree = etree.parse(metadata_path)
+
+               # Used for caching
+               self._herdstree = None
+               self._descriptions = None
+               self._maintainers = None
+               self._useflags = None
+               self._upstream = None
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, self.metadata_path)
+
+       def _get_herd_email(self, herd):
+               """Get a herd's email address.
+
+               @type herd: str
+               @param herd: herd whose email you want
+               @rtype: str or None
+               @return: email address or None if herd is not in herds.xml
+               @raise IOError: if $PORTDIR/metadata/herds.xml can not be read
+               """
+
+               if self._herdstree is None:
+                       herds_path = os.path.join(settings['PORTDIR'], 'metadata/herds.xml')
+                       self._herdstree = etree.parse(herds_path)
+
+               # Some special herds are not listed in herds.xml
+               if herd in ('no-herd', 'maintainer-wanted', 'maintainer-needed'):
+                       return None
+
+               for node in self._herdstree.getiterator('herd'):
+                       if node.findtext('name') == herd:
+                               return node.findtext('email')
+
+       def herds(self, include_email=False):
+               """Return a list of text nodes for <herd>.
+
+               @type include_email: bool
+               @keyword include_email: if True, also look up the herd's email
+               @rtype: list
+               @return: if include_email is False, return a list of string;
+                        if include_email is True, return a list of tuples containing:
+                                        [('herd1', 'herd1@gentoo.org'), ('no-herd', None);
+               """
+
+               result = []
+               for elem in self._xml_tree.findall('herd'):
+                       if include_email:
+                               herd_mail = self._get_herd_email(elem.text)
+                               result.append((elem.text, herd_mail))
+                       else:
+                               result.append(elem.text)
+
+               return result
+
+       def descriptions(self):
+               """Return a list of text nodes for <longdescription>.
+
+               @rtype: list
+               @return: package description in string format
+               @todo: Support the C{lang} attribute
+               """
+
+               if self._descriptions is not None:
+                       return self._descriptions
+
+               long_descriptions = self._xml_tree.findall("longdescription")
+               self._descriptions = [e.text for e in long_descriptions]
+               return self._descriptions
+
+       def maintainers(self):
+               """Get maintainers' name, email and description.
+
+               @rtype: list
+               @return: a sequence of L{_Maintainer} objects in document order.
+               """
+
+               if self._maintainers is not None:
+                       return self._maintainers
+
+               self._maintainers = []
+               for node in self._xml_tree.findall('maintainer'):
+                       self._maintainers.append(_Maintainer(node))
+
+               return self._maintainers
+
+       def use(self):
+               """Get names and descriptions for USE flags defined in metadata.
+
+               @rtype: list
+               @return: a sequence of L{_Useflag} objects in document order.
+               """
+
+               if self._useflags is not None:
+                       return self._useflags
+
+               self._useflags = []
+               for node in self._xml_tree.getiterator('flag'):
+                       self._useflags.append(_Useflag(node))
+
+               return self._useflags
+
+       def upstream(self):
+               """Get upstream contact information.
+
+               @rtype: list
+               @return: a sequence of L{_Upstream} objects in document order.
+               """
+
+               if self._upstream is not None:
+                       return self._upstream
+
+               self._upstream = []
+               for node in self._xml_tree.findall('upstream'):
+                       self._upstream.append(_Upstream(node))
+
+               return self._upstream
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/package.py b/gentoolkit/pym/gentoolkit/package.py
new file mode 100644 (file)
index 0000000..c020f63
--- /dev/null
@@ -0,0 +1,459 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2004-2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Provides an interface to package information stored by package managers.
+
+The Package class is the heart of much of Gentoolkit. Given a CPV
+(category/package-version) string, it can reveal the package's status in the
+tree and VARDB (/var/db/), provide rich comparison and sorting, and expose
+important parts of Portage's back-end.
+
+Example usage:
+       >>> portage = Package('sys-apps/portage-2.1.6.13')
+       >>> portage.ebuild_path()
+       '/usr/portage/sys-apps/portage/portage-2.1.6.13.ebuild'
+       >>> portage.is_masked()
+       False
+       >>> portage.is_installed()
+       True
+"""
+
+__all__ = (
+       'Package',
+       'PackageFormatter'
+)
+
+# =======
+# Imports
+# =======
+
+import os
+
+import portage
+from portage import settings
+
+import gentoolkit.pprinter as pp
+from gentoolkit import errors
+from gentoolkit.cpv import CPV
+from gentoolkit.dbapi import PORTDB, VARDB
+from gentoolkit.dependencies import Dependencies
+from gentoolkit.metadata import MetaData
+
+# =======
+# Classes
+# =======
+
+class Package(CPV):
+       """Exposes the state of a given CPV."""
+
+       def __init__(self, cpv):
+               if isinstance(cpv, CPV):
+                       self.cpv = cpv
+               else:
+                       self.cpv = CPV(cpv)
+               del cpv
+
+               if not all(getattr(self.cpv, x) for x in ('category', 'version')):
+                       # CPV allows some things that Package must not
+                       raise errors.GentoolkitInvalidPackage(str(self.cpv))
+
+               # Set dynamically
+               self._package_path = None
+               self._dblink = None
+               self._metadata = None
+               self._deps = None
+               self._portdir_path = None
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, str(self.cpv))
+
+       def __eq__(self, other):
+               if not hasattr(other, 'cpv'):
+                       return False
+               return self.cpv == other.cpv
+
+       def __ne__(self, other):
+               return not self == other
+
+       def __lt__(self, other):
+               return self.cpv < other.cpv
+
+       def __gt__(self, other):
+               return self.cpv > other.cpv
+
+       def __hash__(self):
+               return hash(str(self.cpv))
+
+       def __contains__(self, key):
+               return key in str(self.cpv)
+
+       def __str__(self):
+               return str(self.cpv)
+
+       @property
+       def metadata(self):
+               """Instantiate a L{gentoolkit.metadata.MetaData} object here."""
+
+               if self._metadata is None:
+                       metadata_path = os.path.join(
+                               self.package_path(), 'metadata.xml'
+                       )
+                       self._metadata = MetaData(metadata_path)
+
+               return self._metadata
+
+       @property
+       def dblink(self):
+               """Instantiate a L{portage.dbapi.vartree.dblink} object here."""
+
+               if self._dblink is None:
+                       self._dblink = portage.dblink(
+                               self.cpv.category,
+                               "%s-%s" % (self.cpv.name, self.cpv.fullversion),
+                               settings["ROOT"],
+                               settings
+                       )
+
+               return self._dblink
+
+       @property
+       def deps(self):
+               """Instantiate a L{gentoolkit.dependencies.Dependencies} object here."""
+
+               if self._deps is None:
+                       self._deps = Dependencies(self.cpv)
+
+               return self._deps
+
+       def environment(self, envvars, prefer_vdb=True, no_fallback=False):
+               """Returns one or more of the predefined environment variables.
+
+               Available envvars are:
+               ----------------------
+                       BINPKGMD5  COUNTER         FEATURES   LICENSE  SRC_URI
+                       CATEGORY   CXXFLAGS        HOMEPAGE   PDEPEND  USE
+                       CBUILD     DEFINED_PHASES  INHERITED  PF
+                       CFLAGS     DEPEND          IUSE       PROVIDE
+                       CHOST      DESCRIPTION     KEYWORDS   RDEPEND
+                       CONTENTS   EAPI            LDFLAGS    SLOT
+
+               Example usage:
+                       >>> pkg = Package('sys-apps/portage-2.1.6.13')
+                       >>> pkg.environment('USE')
+                       'elibc_glibc kernel_linux userland_GNU x86'
+                       >>> pkg.environment(('USE', 'IUSE'))
+                       ['elibc_glibc kernel_linux userland_GNU x86',
+                               'build doc epydoc selinux linguas_pl']
+
+               @type envvars: str or array
+               @param envvars: one or more of (DEPEND, SRC_URI, etc.)
+               @type prefer_vdb: bool
+               @keyword prefer_vdb: if True, look in the vardb before portdb, else
+                       reverse order. Specifically KEYWORDS will get more recent
+                       information by preferring portdb.
+               @type no_fallback: bool
+               @keyword no_fallback: query only the preferred db
+               @rtype: str or list
+               @return: str if envvars is str, list if envvars is array
+               @raise KeyError: if key is not found in requested db(s)
+               """
+
+               got_string = False
+               if isinstance(envvars, basestring):
+                       got_string = True
+                       envvars = (envvars,)
+               if prefer_vdb:
+                       try:
+                               result = VARDB.aux_get(str(self.cpv), envvars)
+                       except KeyError:
+                               try:
+                                       if no_fallback:
+                                               raise KeyError
+                                       result = PORTDB.aux_get(str(self.cpv), envvars)
+                               except KeyError:
+                                       err = "aux_get returned unexpected results"
+                                       raise errors.GentoolkitFatalError(err)
+               else:
+                       try:
+                               result = PORTDB.aux_get(str(self.cpv), envvars)
+                       except KeyError:
+                               try:
+                                       if no_fallback:
+                                               raise KeyError
+                                       result = VARDB.aux_get(str(self.cpv), envvars)
+                               except KeyError:
+                                       err = "aux_get returned unexpected results"
+                                       raise errors.GentoolkitFatalError(err)
+
+               if got_string:
+                       return result[0]
+               return result
+
+       def exists(self):
+               """Return True if package exists in the Portage tree, else False"""
+
+               return bool(PORTDB.cpv_exists(str(self.cpv)))
+
+       @staticmethod
+       def settings(key):
+               """Returns the value of the given key for this package (useful
+               for package.* files."""
+
+               if settings.locked:
+                       settings.unlock()
+               try:
+                       result = settings[key]
+               finally:
+                       settings.lock()
+               return result
+
+       def mask_status(self):
+               """Shortcut to L{portage.getmaskingstatus}.
+
+               @rtype: None or list
+               @return: a list containing none or some of:
+                       'profile'
+                       'package.mask'
+                       license(s)
+                       "kmask" keyword
+                       'missing keyword'
+               """
+
+               if settings.locked:
+                       settings.unlock()
+               try:
+                       result = portage.getmaskingstatus(str(self.cpv),
+                               settings=settings,
+                               portdb=PORTDB)
+               except KeyError:
+                       # getmaskingstatus doesn't support packages without ebuilds in the
+                       # Portage tree.
+                       result = None
+
+               return result
+
+       def mask_reason(self):
+               """Shortcut to L{portage.getmaskingreason}.
+
+               @rtype: None or tuple
+               @return: empty tuple if pkg not masked OR
+                       ('mask reason', 'mask location')
+               """
+
+               try:
+                       result = portage.getmaskingreason(str(self.cpv),
+                               settings=settings,
+                               portdb=PORTDB,
+                               return_location=True)
+                       if result is None:
+                               result = tuple()
+               except KeyError:
+                       # getmaskingstatus doesn't support packages without ebuilds in the
+                       # Portage tree.
+                       result = None
+
+               return result
+
+       def ebuild_path(self, in_vartree=False):
+               """Returns the complete path to the .ebuild file.
+
+               Example usage:
+                       >>> pkg.ebuild_path()
+                       '/usr/portage/sys-apps/portage/portage-2.1.6.13.ebuild'
+                       >>> pkg.ebuild_path(in_vartree=True)
+                       '/var/db/pkg/sys-apps/portage-2.1.6.13/portage-2.1.6.13.ebuild'
+               """
+
+               if in_vartree:
+                       return VARDB.findname(str(self.cpv))
+               return PORTDB.findname(str(self.cpv))
+
+       def package_path(self, in_vartree=False):
+               """Return the path to where the ebuilds and other files reside."""
+
+               if in_vartree:
+                       return self.dblink.getpath()
+               return os.sep.join(self.ebuild_path().split(os.sep)[:-1])
+
+       def repo_id(self):
+               """Using the package path, determine the repository id.
+
+               @rtype: str
+               @return: /usr/<THIS>portage</THIS>/category/name/
+               """
+
+               return self.package_path().split(os.sep)[-3]
+
+       def use(self):
+               """Returns the USE flags active at time of installation."""
+
+               return self.dblink.getstring("USE")
+
+       def parsed_contents(self):
+               """Returns the parsed CONTENTS file.
+
+               @rtype: dict
+               @return: {'/full/path/to/obj': ['type', 'timestamp', 'md5sum'], ...}
+               """
+
+               return self.dblink.getcontents()
+
+       def size(self):
+               """Estimates the installed size of the contents of this package.
+
+               @rtype: tuple
+               @return: (size, number of files in total, number of uncounted files)
+               """
+
+               contents = self.parsed_contents()
+               size = n_uncounted = n_files = 0
+               for cfile in contents:
+                       try:
+                               size += os.lstat(cfile).st_size
+                               n_files += 1
+                       except OSError:
+                               n_uncounted += 1
+               return (size, n_files, n_uncounted)
+
+       def is_installed(self):
+               """Returns True if this package is installed (merged)"""
+
+               return self.dblink.exists()
+
+       def is_overlay(self):
+               """Returns True if the package is in an overlay."""
+
+               ebuild, tree = PORTDB.findname2(str(self.cpv))
+               if not ebuild:
+                       return None
+               if self._portdir_path is None:
+                       self._portdir_path = os.path.realpath(settings["PORTDIR"])
+               return (tree and tree != self._portdir_path)
+
+       def is_masked(self):
+               """Returns true if this package is masked against installation.
+               Note: We blindly assume that the package actually exists on disk
+               somewhere."""
+
+               unmasked = PORTDB.xmatch("match-visible", str(self.cpv))
+               return str(self.cpv) not in unmasked
+
+
+class PackageFormatter(object):
+       """When applied to a L{gentoolkit.package.Package} object, determine the
+       location (Portage Tree vs. overlay), install status and masked status. That
+       information can then be easily formatted and displayed.
+
+       Example usage:
+               >>> from gentoolkit.helpers import find_packages
+               >>> from gentoolkit.package import PackageFormatter
+               >>> pkgs = [PackageFormatter(x) for x in find_packages('gcc')]
+               >>> for pkg in pkgs:
+               ...     # Only print packages that are installed and from the Portage
+               ...     # tree
+               ...     if set('IP').issubset(pkg.location):
+               ...             print pkg
+               ...
+               [IP-] [  ] sys-devel/gcc-4.3.2-r3 (4.3)
+
+       @type pkg: L{gentoolkit.package.Package}
+       @param pkg: package to format
+       @type format: L{bool}
+       @param format: Whether to format the package name or not.
+               Essentially C{format} should be set to False when piping or when
+               quiet output is desired. If C{do_format} is False, only the location
+               attribute will be created to save time.
+       """
+
+       def __init__(self, pkg, do_format=True):
+               self.pkg = pkg
+               self.do_format = do_format
+               self.location = self.format_package_location() or ''
+
+       def __repr__(self):
+               return "<%s %s @%#8x>" % (self.__class__.__name__, self.pkg, id(self))
+
+       def __str__(self):
+               if self.do_format:
+                       maskmodes = ['  ', ' ~', ' -', 'M ', 'M~', 'M-', 'XX']
+                       maskmode = maskmodes[self.format_mask_status()[0]]
+                       return "[%(location)s] [%(mask)s] %(package)s:%(slot)s" % {
+                               'location': self.location,
+                               'mask': pp.keyword(
+                                       maskmode,
+                                       stable=not maskmode.strip(),
+                                       hard_masked=set(('M', 'X', '-')).intersection(maskmode)
+                               ),
+                               'package': pp.cpv(str(self.pkg.cpv)),
+                               'slot': pp.slot(self.pkg.environment("SLOT"))
+                       }
+               else:
+                       return str(self.pkg.cpv)
+
+       def format_package_location(self):
+               """Get the install status (in /var/db/?) and origin (from and overlay
+               and the Portage tree?).
+
+               @rtype: str
+               @return: one of:
+                       'I--' : Installed but ebuild doesn't exist on system anymore
+                       '-P-' : Not installed and from the Portage tree
+                       '--O' : Not installed and from an overlay
+                       'IP-' : Installed and from the Portage tree
+                       'I-O' : Installed and from an overlay
+               """
+
+               result = ['-', '-', '-']
+
+               if self.pkg.is_installed():
+                       result[0] = 'I'
+
+               overlay = self.pkg.is_overlay()
+               if overlay is None:
+                       pass
+               elif overlay:
+                       result[2] = 'O'
+               else:
+                       result[1] = 'P'
+
+               return ''.join(result)
+
+       def format_mask_status(self):
+               """Get the mask status of a given package.
+
+               @rtype: tuple: (int, list)
+               @return: int = an index for this list:
+                       ["  ", " ~", " -", "M ", "M~", "M-", "XX"]
+                       0 = not masked
+                       1 = keyword masked
+                       2 = arch masked
+                       3 = hard masked
+                       4 = hard and keyword masked,
+                       5 = hard and arch masked
+                       6 = ebuild doesn't exist on system anymore
+
+                       list = original output of portage.getmaskingstatus
+               """
+
+               result = 0
+               masking_status = self.pkg.mask_status()
+               if masking_status is None:
+                       return (6, [])
+
+               if ("~%s keyword" % self.pkg.settings("ARCH")) in masking_status:
+                       result += 1
+               if "missing keyword" in masking_status:
+                       result += 2
+               if set(('profile', 'package.mask')).intersection(masking_status):
+                       result += 3
+
+               return (result, masking_status)
+
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/pprinter.py b/gentoolkit/pym/gentoolkit/pprinter.py
new file mode 100644 (file)
index 0000000..bd5e6cb
--- /dev/null
@@ -0,0 +1,131 @@
+#!/usr/bin/python
+#
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+"""Provides a consistent color scheme for Gentoolkit scripts."""
+
+__all__ = (
+       'command',
+       'cpv',
+       'die',
+       'emph',
+       'error',
+       'globaloption',
+       'installedflag',
+       'localoption',
+       'number',
+       'path',
+       'path_symlink',
+       'pkgquery',
+       'productname',
+       'regexpquery',
+       'section',
+       'slot',
+       'subsection',
+       'useflag',
+       'warn'
+)
+
+# =======
+# Imports
+# =======
+
+import sys
+
+import portage.output as output
+
+# =========
+# Functions
+# =========
+
+# output creates color functions on the fly, which confuses pylint.
+# E1101: *%s %r has no %r member*
+# pylint: disable-msg=E1101
+
+def command(string):
+       """Returns a program command string."""
+       return output.green(string)
+
+def cpv(string):
+       """Returns a category/package-<version> string."""
+       return output.green(string)
+
+def die(err, string):
+       """Returns an error string and die with an error code."""
+       sys.stderr.write(error(string))
+       sys.exit(err)
+
+def emph(string):
+       """Returns a string as emphasized."""
+       return output.bold(string)
+
+def error(string):
+       """Prints an error string."""
+       return output.red("!!! ") + string + "\n"
+
+def globaloption(string):
+       """Returns a global option string, i.e. the program global options."""
+       return output.yellow(string)
+
+def localoption(string):
+       """Returns a local option string, i.e. the program local options."""
+       return output.green(string)
+
+def number(string):
+       """Returns a number string."""
+       return output.turquoise(string)
+
+def path(string):
+       """Returns a file or directory path string."""
+       return output.bold(string)
+
+def path_symlink(string):
+       """Returns a symlink string."""
+       return output.turquoise(string)
+
+def pkgquery(string):
+       """Returns a package query string."""
+       return output.bold(string)
+
+def productname(string):
+       """Returns a product name string, i.e. the program name."""
+       return output.turquoise(string)
+
+def regexpquery(string):
+       """Returns a regular expression string."""
+       return output.bold(string)
+
+def section(string):
+       """Returns a string as a section header."""
+       return output.turquoise(string)
+
+def slot(string):
+       """Returns a slot string"""
+       return output.bold(string)
+
+def subsection(string):
+       """Returns a string as a subsection header."""
+       return output.turquoise(string)
+
+def useflag(string, enabled=True):
+       """Returns a USE flag string."""
+       return output.blue(string) if enabled else output.red(string)
+
+def keyword(string, stable=True, hard_masked=False):
+       """Returns a keyword string."""
+       if stable:
+               return output.green(string)
+       if hard_masked:
+               return output.red(string)
+       # keyword masked:
+       return output.blue(string)
+
+def warn(string):
+       """Returns a warning string."""
+       return "!!! " + string + "\n"
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/query.py b/gentoolkit/pym/gentoolkit/query.py
new file mode 100644 (file)
index 0000000..24f8c18
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2004-2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+"""Provides common methods on a package query."""
+
+__all__ = (
+       'Query',
+)
+
+# =======
+# Imports
+# =======
+
+from gentoolkit.cpv import CPV
+#from gentoolkit.helpers import *
+
+# =======
+# Classes
+# =======
+
+class Query(CPV):
+       """Provides common methods on a package query."""
+
+       def __init__(self, cpv):
+               if isinstance(cpv, CPV):
+                       self.cpv = cpv
+               else:
+                       self.cpv = CPV(cpv)
+               del cpv
diff --git a/gentoolkit/pym/gentoolkit/test/__init__.py b/gentoolkit/pym/gentoolkit/test/__init__.py
new file mode 100644 (file)
index 0000000..94423e9
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+# Copyright 2009 Gentoo Foundation
+#
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
diff --git a/gentoolkit/pym/gentoolkit/test/equery/__init__.py b/gentoolkit/pym/gentoolkit/test/equery/__init__.py
new file mode 100644 (file)
index 0000000..94423e9
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+# Copyright 2009 Gentoo Foundation
+#
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
diff --git a/gentoolkit/pym/gentoolkit/test/equery/test_init.py b/gentoolkit/pym/gentoolkit/test/equery/test_init.py
new file mode 100644 (file)
index 0000000..98e2648
--- /dev/null
@@ -0,0 +1,46 @@
+import unittest
+from test import test_support
+
+from gentoolkit import equery
+
+class TestEqueryInit(unittest.TestCase):
+
+       def setUp(self):
+               pass
+
+       def tearDown(self):
+               pass
+
+       def test_expand_module_name(self):
+               # Test that module names are properly expanded
+               name_map = {
+                       'b': 'belongs',
+                       'c': 'changes',
+                       'k': 'check',
+                       'd': 'depends',
+                       'g': 'depgraph',
+                       'f': 'files',
+                       'h': 'hasuse',
+                       'l': 'list_',
+                       'm': 'meta',
+                       's': 'size',
+                       'u': 'uses',
+                       'w': 'which'
+               }
+               self.failUnlessEqual(equery.NAME_MAP, name_map)
+               for short_name, long_name in zip(name_map, name_map.values()):
+                       self.failUnlessEqual(equery.expand_module_name(short_name),
+                               long_name)
+                       self.failUnlessEqual(equery.expand_module_name(long_name),
+                               long_name)
+               unused_keys = set(map(chr, range(0, 256))).difference(name_map.keys())
+               for key in unused_keys:
+                       self.failUnlessRaises(KeyError, equery.expand_module_name, key)
+
+
+def test_main():
+       test_support.run_unittest(TestEqueryInit)
+
+
+if __name__ == '__main__':
+       test_main()
diff --git a/gentoolkit/pym/gentoolkit/test/test_atom.py b/gentoolkit/pym/gentoolkit/test/test_atom.py
new file mode 100644 (file)
index 0000000..4a1568b
--- /dev/null
@@ -0,0 +1,149 @@
+# Copyright(c) 2009, Gentoo Foundation
+# Copyright: 2006-2008 Brian Harring <ferringb@gmail.com>
+#
+# License: GPL2/BSD
+
+# $Header$
+
+import unittest
+from test import test_support
+
+from gentoolkit.atom import *
+
+"""Atom test suite (verbatim) from pkgcore."""
+
+class TestGentoolkitAtom(unittest.TestCase):
+
+       def assertEqual2(self, o1, o2):
+               # logic bugs hidden behind short circuiting comparisons for metadata
+               # is why we test the comparison *both* ways.
+               self.assertEqual(o1, o2)
+               c = cmp(o1, o2)
+               self.assertEqual(c, 0,
+                       msg="checking cmp for %r, %r, aren't equal: got %i" % (o1, o2, c))
+               self.assertEqual(o2, o1)
+               c = cmp(o2, o1)
+               self.assertEqual(c, 0,
+                       msg="checking cmp for %r, %r,aren't equal: got %i" % (o2, o1, c))
+
+       def assertNotEqual2(self, o1, o2):
+               # is why we test the comparison *both* ways.
+               self.assertNotEqual(o1, o2)
+               c = cmp(o1, o2)
+               self.assertNotEqual(c, 0,
+                       msg="checking cmp for %r, %r, not supposed to be equal, got %i"
+                               % (o1, o2, c))
+               self.assertNotEqual(o2, o1)
+               c = cmp(o2, o1)
+               self.assertNotEqual(c, 0,
+                       msg="checking cmp for %r, %r, not supposed to be equal, got %i"
+                               % (o2, o1, c))
+
+       def test_comparison(self):
+               self.assertEqual2(Atom('cat/pkg'), Atom('cat/pkg'))
+               self.assertNotEqual2(Atom('cat/pkg'), Atom('cat/pkgb'))
+               self.assertNotEqual2(Atom('cata/pkg'), Atom('cat/pkg'))
+               self.assertNotEqual2(Atom('cat/pkg'), Atom('!cat/pkg'))
+               self.assertEqual2(Atom('!cat/pkg'), Atom('!cat/pkg'))
+               self.assertNotEqual2(Atom('=cat/pkg-0.1:0'),
+                       Atom('=cat/pkg-0.1'))
+               self.assertNotEqual2(Atom('=cat/pkg-1[foon]'),
+                       Atom('=cat/pkg-1'))
+               self.assertEqual2(Atom('=cat/pkg-0'), Atom('=cat/pkg-0'))
+               self.assertNotEqual2(Atom('<cat/pkg-2'), Atom('>cat/pkg-2'))
+               self.assertNotEqual2(Atom('=cat/pkg-2*'), Atom('=cat/pkg-2'))
+               # Portage Atom doesn't have 'negate_version' capability
+               #self.assertNotEqual2(Atom('=cat/pkg-2', True), Atom('=cat/pkg-2'))
+
+               # use...
+               self.assertNotEqual2(Atom('cat/pkg[foo]'), Atom('cat/pkg'))
+               self.assertNotEqual2(Atom('cat/pkg[foo]'),
+                                                        Atom('cat/pkg[-foo]'))
+               self.assertEqual2(Atom('cat/pkg[foo,-bar]'),
+                                                 Atom('cat/pkg[-bar,foo]'))
+
+               # repoid not supported by Portage Atom yet
+               ## repoid
+               #self.assertEqual2(Atom('cat/pkg::a'), Atom('cat/pkg::a'))
+               #self.assertNotEqual2(Atom('cat/pkg::a'), Atom('cat/pkg::b'))
+               #self.assertNotEqual2(Atom('cat/pkg::a'), Atom('cat/pkg'))
+
+               # slots.
+               self.assertNotEqual2(Atom('cat/pkg:1'), Atom('cat/pkg'))
+               self.assertEqual2(Atom('cat/pkg:2'), Atom('cat/pkg:2'))
+               # http://dev.gentoo.org/~tanderson/pms/eapi-2-approved/pms.html#x1-190002.1.2
+               self.assertEqual2(Atom('cat/pkg:AZaz09+_.-'), Atom('cat/pkg:AZaz09+_.-'))
+               for lesser, greater in (('0.1', '1'), ('1', '1-r1'), ('1.1', '1.2')):
+                       self.assertTrue(Atom('=d/b-%s' % lesser) <
+                               Atom('=d/b-%s' % greater),
+                               msg="d/b-%s < d/b-%s" % (lesser, greater))
+                       self.assertFalse(Atom('=d/b-%s' % lesser) >
+                               Atom('=d/b-%s' % greater),
+                               msg="!: d/b-%s < d/b-%s" % (lesser, greater))
+                       self.assertTrue(Atom('=d/b-%s' % greater) >
+                               Atom('=d/b-%s' % lesser),
+                               msg="d/b-%s > d/b-%s" % (greater, lesser))
+                       self.assertFalse(Atom('=d/b-%s' % greater) <
+                               Atom('=d/b-%s' % lesser),
+                               msg="!: d/b-%s > d/b-%s" % (greater, lesser))
+
+               #self.assertTrue(Atom("!!=d/b-1", eapi=2) > Atom("!=d/b-1"))
+               self.assertTrue(Atom("!=d/b-1") < Atom("!!=d/b-1"))
+               self.assertEqual(Atom("!=d/b-1"), Atom("!=d/b-1"))
+
+       def test_intersects(self):
+               for this, that, result in [
+                       ('cat/pkg', 'pkg/cat', False),
+                       ('cat/pkg', 'cat/pkg', True),
+                       ('cat/pkg:1', 'cat/pkg:1', True),
+                       ('cat/pkg:1', 'cat/pkg:2', False),
+                       ('cat/pkg:1', 'cat/pkg[foo]', True),
+                       ('cat/pkg[foo]', 'cat/pkg[-bar]', True),
+                       ('cat/pkg[foo]', 'cat/pkg[-foo]', False),
+                       ('>cat/pkg-3', '>cat/pkg-1', True),
+                       ('>cat/pkg-3', '<cat/pkg-3', False),
+                       ('>=cat/pkg-3', '<cat/pkg-3', False),
+                       ('>cat/pkg-2', '=cat/pkg-2*', True),
+                       # Portage vercmp disagrees with this one:
+                       #('<cat/pkg-2_alpha1', '=cat/pkg-2*', True),
+                       ('=cat/pkg-2', '=cat/pkg-2', True),
+                       ('=cat/pkg-3', '=cat/pkg-2', False),
+                       ('=cat/pkg-2', '>cat/pkg-2', False),
+                       ('=cat/pkg-2', '>=cat/pkg-2', True),
+                       ('~cat/pkg-2', '~cat/pkg-2', True),
+                       ('~cat/pkg-2', '~cat/pkg-2.1', False),
+                       ('=cat/pkg-2*', '=cat/pkg-2.3*', True),
+                       ('>cat/pkg-2.4', '=cat/pkg-2*', True),
+                       ('<cat/pkg-2.4', '=cat/pkg-2*', True),
+                       ('<cat/pkg-1', '=cat/pkg-2*', False),
+                       ('~cat/pkg-2', '>cat/pkg-2-r1', True),
+                       ('~cat/pkg-2', '<=cat/pkg-2', True),
+                       ('=cat/pkg-2-r2*', '<=cat/pkg-2-r20', True),
+                       ('=cat/pkg-2-r2*', '<cat/pkg-2-r20', True),
+                       ('=cat/pkg-2-r2*', '<=cat/pkg-2-r2', True),
+                       ('~cat/pkg-2', '<cat/pkg-2', False),
+                       ('=cat/pkg-1-r10*', '~cat/pkg-1', True),
+                       ('=cat/pkg-1-r1*', '<cat/pkg-1-r1', False),
+                       ('=cat/pkg-1*', '>cat/pkg-2', False),
+                       ('>=cat/pkg-8.4', '=cat/pkg-8.3.4*', False),
+                       # Repos not yet supported by Portage
+                       #('cat/pkg::gentoo', 'cat/pkg', True),
+                       #('cat/pkg::gentoo', 'cat/pkg::foo', False),
+                       ('=sys-devel/gcc-4.1.1-r3', '=sys-devel/gcc-3.3*', False),
+                       ('=sys-libs/db-4*', '~sys-libs/db-4.3.29', True),
+               ]:
+                       this_atom = Atom(this)
+                       that_atom = Atom(that)
+                       self.assertEqual(
+                               result, this_atom.intersects(that_atom),
+                               '%s intersecting %s should be %s' % (this, that, result))
+                       self.assertEqual(
+                               result, that_atom.intersects(this_atom),
+                               '%s intersecting %s should be %s' % (that, this, result))
+
+
+def test_main():
+       test_support.run_unittest(TestGentoolkitAtom)
+
+if __name__ == '__main__':
+       test_main()
diff --git a/gentoolkit/pym/gentoolkit/test/test_cpv.py b/gentoolkit/pym/gentoolkit/test/test_cpv.py
new file mode 100644 (file)
index 0000000..9b36cc3
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+#
+# Copyright(c) 2009, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+import unittest
+from test import test_support
+
+from gentoolkit.cpv import *
+
+class TestGentoolkitCPV(unittest.TestCase):
+
+       def assertEqual2(self, o1, o2):
+               # logic bugs hidden behind short circuiting comparisons for metadata
+               # is why we test the comparison *both* ways.
+               self.assertEqual(o1, o2)
+               c = cmp(o1, o2)
+               self.assertEqual(c, 0,
+                       msg="checking cmp for %r, %r, aren't equal: got %i" % (o1, o2, c))
+               self.assertEqual(o2, o1)
+               c = cmp(o2, o1)
+               self.assertEqual(c, 0,
+                       msg="checking cmp for %r, %r,aren't equal: got %i" % (o2, o1, c))
+
+       def assertNotEqual2(self, o1, o2):
+               # is why we test the comparison *both* ways.
+               self.assertNotEqual(o1, o2)
+               c = cmp(o1, o2)
+               self.assertNotEqual(c, 0,
+                       msg="checking cmp for %r, %r, not supposed to be equal, got %i"
+                               % (o1, o2, c))
+               self.assertNotEqual(o2, o1)
+               c = cmp(o2, o1)
+               self.assertNotEqual(c, 0,
+                       msg="checking cmp for %r, %r, not supposed to be equal, got %i"
+                               % (o2, o1, c))
+
+       def test_comparison(self):
+               self.assertEqual2(CPV('pkg'), CPV('pkg'))
+               self.assertNotEqual2(CPV('pkg'), CPV('pkg1'))
+               self.assertEqual2(CPV('cat/pkg'), CPV('cat/pkg'))
+               self.assertNotEqual2(CPV('cat/pkg'), CPV('cat/pkgb'))
+               self.assertNotEqual2(CPV('cata/pkg'), CPV('cat/pkg'))
+               self.assertEqual2(CPV('cat/pkg-0.1'), CPV('cat/pkg-0.1'))
+               self.assertNotEqual2(CPV('cat/pkg-1.0'), CPV('cat/pkg-1'))
+               self.assertEqual2(CPV('cat/pkg-0'), CPV('cat/pkg-0'))
+               self.assertEqual2(CPV('cat/pkg-1-r1'), CPV('cat/pkg-1-r1'))
+               self.assertNotEqual2(CPV('cat/pkg-2-r1'), CPV('cat/pkg-2-r10'))
+               self.assertEqual2(CPV('cat/pkg-1_rc2'), CPV('cat/pkg-1_rc2'))
+               self.assertNotEqual2(CPV('cat/pkg-2_rc2-r1'), CPV('cat/pkg-2_rc1-r1'))
+
+def test_main():
+       test_support.run_unittest(TestGentoolkitCPV)
+
+if __name__ == '__main__':
+       test_main()
diff --git a/gentoolkit/pym/gentoolkit/test/test_helpers.py b/gentoolkit/pym/gentoolkit/test/test_helpers.py
new file mode 100644 (file)
index 0000000..e46e03b
--- /dev/null
@@ -0,0 +1,107 @@
+import os
+import unittest
+import warnings
+from tempfile import NamedTemporaryFile
+from test import test_support
+
+from gentoolkit import helpers
+
+
+class TestFileOwner(unittest.TestCase):
+
+       def setUp(self):
+               pass
+
+       def tearDown(self):
+               pass
+
+       def test_extend_realpaths(self):
+               extend_realpaths = helpers.FileOwner.extend_realpaths
+
+               # Test that symlinks's realpaths are extended
+               f1 = NamedTemporaryFile(prefix='equeryunittest')
+               f2 = NamedTemporaryFile(prefix='equeryunittest')
+               f3 = NamedTemporaryFile(prefix='equeryunittest')
+               with warnings.catch_warnings():
+                       warnings.simplefilter("ignore")
+                       sym1 = os.tmpnam()
+                       os.symlink(f1.name, sym1)
+                       sym2 = os.tmpnam()
+                       os.symlink(f3.name, sym2)
+               # We've created 3 files and 2 symlinks for testing. We're going to pass
+               # in only the first two files and both symlinks. sym1 points to f1.
+               # Since f1 is already in the list, sym1's realpath should not be added.
+               # sym2 points to f3, but f3's not in our list, so sym2's realpath
+               # should be added to the list.
+               p = [f1.name, f2.name, sym1, sym2]
+               p_xr = extend_realpaths(p)
+
+               self.failUnlessEqual(p_xr[0], f1.name)
+               self.failUnlessEqual(p_xr[1], f2.name)
+               self.failUnlessEqual(p_xr[2], sym1)
+               self.failUnlessEqual(p_xr[3], sym2)
+               self.failUnlessEqual(p_xr[4], f3.name)
+
+               # Clean up
+               os.unlink(sym1)
+               os.unlink(sym2)
+
+               # Make sure we raise an exception if we don't get acceptable input
+               self.failUnlessRaises(AttributeError, extend_realpaths, 'str')
+               self.failUnlessRaises(AttributeError, extend_realpaths, set())
+
+
+class TestGentoolkitHelpers(unittest.TestCase):
+
+       def test_compare_package_strings(self):
+               # Test ordering of package strings, Portage has test for vercmp,
+               # so just do the rest
+               version_tests = [
+                       # different categories
+                       ('sys-apps/portage-2.1.6.8', 'sys-auth/pambase-20080318'),
+                       # different package names
+                       ('sys-apps/pkgcore-0.4.7.15-r1', 'sys-apps/portage-2.1.6.8'),
+                       # different package versions
+                       ('sys-apps/portage-2.1.6.8', 'sys-apps/portage-2.2_rc25')
+               ]
+               # Check less than
+               for vt in version_tests:
+                       self.failUnless(
+                               helpers.compare_package_strings(vt[0], vt[1]) == -1
+                       )
+               # Check greater than
+               for vt in version_tests:
+                       self.failUnless(
+                               helpers.compare_package_strings(vt[1], vt[0]) == 1
+                       )
+               # Check equal
+               vt = ('sys-auth/pambase-20080318', 'sys-auth/pambase-20080318')
+               self.failUnless(
+                       helpers.compare_package_strings(vt[0], vt[1]) == 0
+               )
+
+       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)
+
+
+if __name__ == '__main__':
+       test_main()
diff --git a/gentoolkit/pym/gentoolkit/test/test_syntax.py b/gentoolkit/pym/gentoolkit/test/test_syntax.py
new file mode 100644 (file)
index 0000000..bb7dcb4
--- /dev/null
@@ -0,0 +1,33 @@
+import os
+import os.path as osp
+import unittest
+import py_compile
+
+"""Does a basic syntax check by compiling all modules. From Portage."""
+
+pym_dirs = os.walk(osp.dirname(osp.dirname(osp.dirname(__file__))))
+blacklist_dirs = frozenset(('.svn', 'test'))
+
+class TestForSyntaxErrors(unittest.TestCase):
+
+       def test_compileability(self):
+               compileables = []
+               for thisdir, subdirs, files in pym_dirs:
+                       if os.path.basename(thisdir) in blacklist_dirs:
+                               continue
+                       compileables.extend([
+                               osp.join(thisdir, f)
+                               for f in files
+                               if osp.splitext(f)[1] == '.py'
+                       ])
+
+               for c in compileables:
+                       py_compile.compile(c, doraise=True)
+
+
+def test_main():
+       test_support.run_unittest(TestGentoolkitHelpers2)
+
+
+if __name__ == '__main__':
+       test_main()
diff --git a/gentoolkit/pym/gentoolkit/textwrap_.py b/gentoolkit/pym/gentoolkit/textwrap_.py
new file mode 100644 (file)
index 0000000..845ae9d
--- /dev/null
@@ -0,0 +1,99 @@
+"""This modification of textwrap allows it to wrap ANSI colorized text as if
+it weren't colorized. It also uses a much simpler word splitting regex to
+prevent the splitting of ANSI colors as well as package names and versions."""
+
+import re
+import textwrap
+
+class TextWrapper(textwrap.TextWrapper):
+       """Ignore ANSI escape codes while wrapping text"""
+
+       def _split(self, text):
+               """_split(text : string) -> [string]
+
+               Split the text to wrap into indivisible chunks.
+               """
+               # Only split on whitespace to avoid mangling ANSI escape codes or
+               # package names.
+               wordsep_re = re.compile(r'(\s+)')
+               chunks = wordsep_re.split(text)
+               chunks = [x for x in chunks if x is not None]
+               return chunks
+
+       def _wrap_chunks(self, chunks):
+               """_wrap_chunks(chunks : [string]) -> [string]
+
+               Wrap a sequence of text chunks and return a list of lines of
+               length 'self.width' or less.  (If 'break_long_words' is false,
+               some lines may be longer than this.)  Chunks correspond roughly
+               to words and the whitespace between them: each chunk is
+               indivisible (modulo 'break_long_words'), but a line break can
+               come between any two chunks.  Chunks should not have internal
+               whitespace; ie. a chunk is either all whitespace or a "word".
+               Whitespace chunks will be removed from the beginning and end of
+               lines, but apart from that whitespace is preserved.
+               """
+               lines = []
+               if self.width <= 0:
+                       raise ValueError("invalid width %r (must be > 0)" % self.width)
+
+               # Arrange in reverse order so items can be efficiently popped
+               # from a stack of chunks.
+               chunks.reverse()
+
+               # Regex to strip ANSI escape codes. It's only used for the
+               # length calculations of indent and each chuck.
+               ansi_re = re.compile('\x1b\[[0-9;]*m')
+
+               while chunks:
+
+                       # Start the list of chunks that will make up the current line.
+                       # cur_len is just the length of all the chunks in cur_line.
+                       cur_line = []
+                       cur_len = 0
+
+                       # Figure out which static string will prefix this line.
+                       if lines:
+                               indent = self.subsequent_indent
+                       else:
+                               indent = self.initial_indent
+
+                       # Maximum width for this line. Ingore ANSI escape codes.
+                       width = self.width - len(re.sub(ansi_re, '', indent))
+
+                       # First chunk on line is whitespace -- drop it, unless this
+                       # is the very beginning of the text (ie. no lines started yet).
+                       if chunks[-1].strip() == '' and lines:
+                               del chunks[-1]
+
+                       while chunks:
+                               # Ignore ANSI escape codes.
+                               chunk_len = len(re.sub(ansi_re, '', chunks[-1]))
+
+                               # Can at least squeeze this chunk onto the current line.
+                               if cur_len + chunk_len <= width:
+                                       cur_line.append(chunks.pop())
+                                       cur_len += chunk_len
+
+                               # Nope, this line is full.
+                               else:
+                                       break
+
+                       # The current line is full, and the next chunk is too big to
+                       # fit on *any* line (not just this one).
+                       # Ignore ANSI escape codes.
+                       if chunks and len(re.sub(ansi_re, '', chunks[-1])) > width:
+                               self._handle_long_word(chunks, cur_line, cur_len, width)
+
+                       # If the last chunk on this line is all whitespace, drop it.
+                       if cur_line and cur_line[-1].strip() == '':
+                               del cur_line[-1]
+
+                       # Convert current line back to a string and store it in list
+                       # of all lines (return value).
+                       if cur_line:
+                               lines.append(indent + ''.join(cur_line))
+
+               return lines
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/pym/gentoolkit/versionmatch.py b/gentoolkit/pym/gentoolkit/versionmatch.py
new file mode 100644 (file)
index 0000000..a11cc9a
--- /dev/null
@@ -0,0 +1,132 @@
+#! /usr/bin/python
+#
+# Copyright(c) 2009 Gentoo Foundation
+# Licensed under the GNU General Public License, v2
+#
+# Copyright: 2005-2007 Brian Harring <ferringb@gmail.com>
+# License: GPL2/BSD
+#
+# $Header$
+
+"""Gentoo version comparison object from pkgcore.ebuild.atom_restricts."""
+
+# =======
+# Imports
+# =======
+
+from portage.versions import vercmp
+
+from gentoolkit import errors
+from gentoolkit.cpv import CPV
+
+# =======
+# Classes
+# =======
+
+class VersionMatch(object):
+       """Gentoo version comparison object from pkgcore.ebuild.atom_restricts.
+
+       Any overriding of this class *must* maintain numerical order of
+       self.vals, see intersect for reason why. vals also must be a tuple.
+       """
+       _convert_op2int = {(-1,):"<", (-1, 0): "<=", (0,):"=",
+               (0, 1):">=", (1,):">"}
+
+       _convert_int2op = dict([(v, k) for k, v in _convert_op2int.iteritems()])
+
+       def __init__(self, cpv, op='='):
+               """Initialize a VersionMatch instance.
+
+               @type cpv: L{gentoolkit.cpv.CPV}
+               @param cpv: cpv object
+               @type op: str
+               @keyword op: operator
+               """
+
+               if not isinstance(cpv, CPV):
+                       raise ValueError("cpv must be a gentoolkit.cpv.CPV instance")
+               self.cpv = cpv
+               self.operator = op
+               self.version = cpv.version
+               self.revision = cpv.revision
+               self.fullversion = cpv.fullversion
+
+               if self.operator != "~" and self.operator not in self._convert_int2op:
+                       raise errors.GentoolkitInvalidVersion(
+                               "invalid operator '%s'" % self.operator)
+
+               if self.operator == "~":
+                       if not self.version:
+                               raise errors.GentoolkitInvalidVersion(
+                                       "for ~ op, ver must be specified")
+                       self.droprevision = True
+                       self.values = (0,)
+               else:
+                       self.droprevision = False
+                       self.values = self._convert_int2op[self.operator]
+
+       def match(self, other):
+               """See whether a passed in VersionMatch or CPV instance matches self.
+
+               Example usage:
+                       >>> from gentoolkit.versionmatch import VersionMatch
+                       >>> from gentoolkit.cpv import CPV
+                       >>> VersionMatch(CPV('foo/bar-1.5'), op='>').match(
+                       ... VersionMatch(CPV('foo/bar-2.0')))
+                       True
+
+               @type other: gentoolkit.versionmatch.VersionMatch OR
+                  gentoolkit.cpv.CPV
+               @param other: version to compare with self's version
+               @rtype: bool
+               """
+
+               if self.droprevision:
+                       ver1, ver2 = self.version, other.version
+               else:
+                       ver1, ver2 = self.fullversion, other.fullversion
+
+               return vercmp(ver2, ver1) in self.values
+
+       def __str__(self):
+               operator = self._convert_op2int[self.values]
+
+               if self.droprevision or not self.revision:
+                       return "ver %s %s" % (operator, self.version)
+               return "ver-rev %s %s-%s" % (
+                       operator, self.version, self.revision
+               )
+
+       def __repr__(self):
+               return "<%s %r>" % (self.__class__.__name__, str(self))
+
+       @staticmethod
+       def _convert_ops(inst):
+               if inst.droprevision:
+                       return inst.values
+               return tuple(sorted(set((-1, 0, 1)).difference(inst.values)))
+
+       def __eq__(self, other):
+               if self is other:
+                       return True
+               if isinstance(other, self.__class__):
+                       if (self.droprevision != other.droprevision or
+                               self.version != other.version or
+                               self.revision != other.revision):
+                               return False
+                       return self._convert_ops(self) == self._convert_ops(other)
+
+               return False
+
+       def __ne__(self, other):
+               return not self == other
+
+       def __hash__(self):
+               return hash((
+                       self.droprevision,
+                       self.version,
+                       self.revision,
+                       self.values
+               ))
+
+# vim: set ts=4 sw=4 tw=79:
diff --git a/gentoolkit/setup.py b/gentoolkit/setup.py
new file mode 100755 (executable)
index 0000000..ba06995
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+
+import os
+import re
+import sys
+import distutils
+from distutils import core, log
+from glob import glob
+
+__version__ = os.getenv('VERSION', default='9999')
+
+cwd = os.getcwd()
+
+# Bash files that need `VERSION=""` subbed, relative to this dir:
+bash_scripts = [os.path.join(cwd, path) for path in (
+       'bin/euse',
+       'bin/revdep-rebuild'
+)]
+
+# Python files that need `__version__ = ""` subbed, relative to this dir:
+python_scripts = [os.path.join(cwd, path) for path in (
+       'bin/eclean',
+       'bin/epkginfo',
+       'bin/glsa-check',
+       'pym/gentoolkit/equery/__init__.py'
+)]
+
+
+class set_version(core.Command):
+       """Set python __version__ and bash VERSION to our __version__."""
+       description = "hardcode scripts' version using VERSION from environment"
+       user_options = []  # [(long_name, short_name, desc),]
+
+       def initialize_options (self):
+               pass
+
+       def finalize_options (self):
+               pass
+
+       def run(self):
+               ver = 'svn' if __version__ == '9999' else __version__
+               def sub(files, pattern):
+                       for f in files:
+                               updated_file = []
+                               with open(f) as s:
+                                       for line in s:
+                                               newline = re.sub(pattern, '"%s"' % ver, line, 1)
+                                               if newline != line:
+                                                       log.info("%s: %s" % (f, newline))
+                                               updated_file.append(newline)
+                               with open(f, 'w') as s:
+                                       s.writelines(updated_file)
+               quote = r'[\'"]{1}'
+               bash_re = r'(?<=VERSION=)' + quote + '[^\'"]*' + quote
+               sub(bash_scripts, bash_re)
+               python_re = r'(?<=^__version__ = )' + quote + '[^\'"]*' + quote
+               sub(python_scripts, python_re)
+
+
+def    load_test():
+       """Only return the real test class if it's actually being run so that we
+       don't depend on snakeoil just to install."""
+
+       desc = "run the test suite"
+       if 'test' in sys.argv[1:]:
+               try:
+                       from snakeoil import distutils_extensions
+               except ImportError:
+                       sys.stderr.write("Error: We depend on dev-python/snakeoil ")
+                       sys.stderr.write("to run tests.\n")
+                       sys.exit(1)
+               class test(distutils_extensions.test):
+                       description = desc
+                       default_test_namespace = 'gentoolkit.test'
+       else:
+               class test(core.Command):
+                       description = desc
+
+       return test
+
+
+packages = [
+       '.'.join(root.split(os.sep)[1:])
+       for root, dirs, files in os.walk('pym/gentoolkit')
+       if '__init__.py' in files
+]
+
+core.setup(
+       name='gentoolkit',
+       version=__version__,
+       description='Set of tools that work with and enhance portage.',
+       author='',
+       author_email='',
+       maintainer='Gentoo Portage Tools Team',
+       maintainer_email='tools-portage@gentoo.org',
+       url='http://www.gentoo.org/proj/en/portage/tools/index.xml',
+       download_url='http://distfiles.gentoo.org/distfiles/gentoolkit-%s.tar.gz'\
+               % __version__,
+       package_dir={'': 'pym'},
+       packages=packages,
+       scripts=(glob('bin/*')),
+       data_files=(
+               ('/etc/env.d', ['data/99gentoolkit-env']),
+               ('/etc/revdep-rebuild', ['data/revdep-rebuild/99revdep-rebuild']),
+               ('/etc/eclean', glob('data/eclean/*')),
+               ('/usr/share/man/man1', glob('man/*'))
+       ),
+       cmdclass={
+               'test': load_test(),
+               'set_version': set_version,
+       },
+)
+
+# vim: set ts=4 sw=4 tw=79: