aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorfuzzyray <fuzzyray@gentoo.org>2009-04-30 21:52:45 +0000
committerfuzzyray <fuzzyray@gentoo.org>2009-04-30 21:52:45 +0000
commit86eaf5e03289e45a95514b4f6011157972016e9d (patch)
treec16903693f2030c7b01b346b29b265dc1a473888 /src
parentFix has_key() deprecation message. (Bug #232797) (diff)
downloadgentoolkit-86eaf5e03289e45a95514b4f6011157972016e9d.tar.gz
gentoolkit-86eaf5e03289e45a95514b4f6011157972016e9d.tar.bz2
gentoolkit-86eaf5e03289e45a95514b4f6011157972016e9d.zip
Tagging the gentoolkit-0.2.4 releasegentoolkit-0.2.4
svn path=/tags/gentoolkit-0.2.4/; revision=564
Diffstat (limited to 'src')
-rw-r--r--src/99gentoolkit-env1
-rw-r--r--src/change/AUTHORS5
-rw-r--r--src/change/ChangeLog7
-rw-r--r--src/change/README20
-rw-r--r--src/change/change343
-rw-r--r--src/change/change.10
-rw-r--r--src/dep-clean/AUTHORS9
-rw-r--r--src/dep-clean/ChangeLog13
-rw-r--r--src/dep-clean/README4
-rw-r--r--src/dep-clean/dep-clean164
-rw-r--r--src/dep-clean/dep-clean.1194
-rw-r--r--src/dev-scripts/README2
-rwxr-xr-xsrc/dev-scripts/included_headers.sh159
-rwxr-xr-xsrc/dev-scripts/linking_libs.sh204
-rw-r--r--src/distfiles-clean/AUTHORS6
-rw-r--r--src/distfiles-clean/ChangeLog2
-rw-r--r--src/distfiles-clean/TODO0
-rw-r--r--src/distfiles-clean/distfiles-clean78
-rw-r--r--src/ebump/AUTHORS5
-rw-r--r--src/ebump/ChangeLog8
-rw-r--r--src/ebump/Makefile20
-rw-r--r--src/ebump/README18
-rw-r--r--src/ebump/TODO0
-rwxr-xr-xsrc/ebump/ebump356
-rw-r--r--src/ebump/ebump.1110
-rw-r--r--src/echangelog/AUTHORS1
-rw-r--r--src/echangelog/ChangeLog84
-rw-r--r--src/echangelog/Makefile23
-rw-r--r--src/echangelog/README11
-rw-r--r--src/echangelog/TODO0
-rw-r--r--src/echangelog/echangelog493
-rw-r--r--src/echangelog/echangelog.1275
-rw-r--r--src/echangelog/echangelog.pod136
-rw-r--r--src/eclean/AUTHORS1
-rw-r--r--src/eclean/ChangeLog27
-rw-r--r--src/eclean/Makefile24
-rw-r--r--src/eclean/THANKS7
-rw-r--r--src/eclean/TODO16
-rw-r--r--src/eclean/distfiles.exclude5
-rw-r--r--src/eclean/eclean807
-rw-r--r--src/eclean/eclean.1176
-rw-r--r--src/eclean/packages.exclude4
-rw-r--r--src/ego/AUTHOR1
-rw-r--r--src/ego/AUTHORS1
-rw-r--r--src/ego/ChangeLog2
-rw-r--r--src/ego/Makefile18
-rw-r--r--src/ego/README2
-rw-r--r--src/ego/TODO0
-rw-r--r--src/ego/ego86
-rw-r--r--src/ekeyword/AUTHORS1
-rw-r--r--src/ekeyword/ChangeLog37
-rw-r--r--src/ekeyword/Makefile24
-rw-r--r--src/ekeyword/README20
-rw-r--r--src/ekeyword/TODO0
-rw-r--r--src/ekeyword/ekeyword114
-rw-r--r--src/ekeyword/ekeyword.pod74
-rw-r--r--src/epkginfo/Makefile17
-rwxr-xr-xsrc/epkginfo/epkginfo210
-rw-r--r--src/epkginfo/epkginfo.134
-rw-r--r--src/epkgmove/AUTHORS2
-rw-r--r--src/epkgmove/ChangeLog20
-rw-r--r--src/epkgmove/Makefile20
-rw-r--r--src/epkgmove/README16
-rw-r--r--src/epkgmove/TODO0
-rw-r--r--src/epkgmove/epkgmove895
-rw-r--r--src/equery/AUTHORS3
-rw-r--r--src/equery/Makefile20
-rw-r--r--src/equery/README0
-rw-r--r--src/equery/TODO63
-rwxr-xr-xsrc/equery/equery1846
-rw-r--r--src/equery/equery.1278
-rw-r--r--src/equery/tests/common-functions.sh52
-rwxr-xr-xsrc/equery/tests/run-all-tests.sh12
-rw-r--r--src/equery/tests/test-belongs-help.out11
-rwxr-xr-xsrc/equery/tests/test-belongs.sh24
-rw-r--r--src/equery/tests/test-changes-help.out0
-rw-r--r--src/equery/tests/test-check-help.out3
-rwxr-xr-xsrc/equery/tests/test-check.sh39
-rw-r--r--src/equery/tests/test-depends-help.out8
-rwxr-xr-xsrc/equery/tests/test-depends.sh27
-rw-r--r--src/equery/tests/test-depgraph-help.out7
-rwxr-xr-xsrc/equery/tests/test-depgraph.sh27
-rw-r--r--src/equery/tests/test-files-help.out11
-rwxr-xr-xsrc/equery/tests/test-files.sh61
-rw-r--r--src/equery/tests/test-glsa-help.out0
-rw-r--r--src/equery/tests/test-hasuses-help.out9
-rw-r--r--src/equery/tests/test-help.out21
-rwxr-xr-xsrc/equery/tests/test-help.sh105
-rw-r--r--src/equery/tests/test-list-help.out9
-rwxr-xr-xsrc/equery/tests/test-list.sh40
-rw-r--r--src/equery/tests/test-size-help.out6
-rwxr-xr-xsrc/equery/tests/test-size.sh27
-rw-r--r--src/equery/tests/test-stats-help.out0
-rw-r--r--src/equery/tests/test-uses-help.out7
-rwxr-xr-xsrc/equery/tests/test-uses.sh39
-rw-r--r--src/equery/tests/test-which-help.out3
-rwxr-xr-xsrc/equery/tests/test-which.sh22
-rw-r--r--src/eread/AUTHORS2
-rw-r--r--src/eread/Makefile20
-rwxr-xr-xsrc/eread/eread94
-rw-r--r--src/eread/eread.112
-rw-r--r--src/etc-update/AUTHORS0
-rw-r--r--src/etc-update/ChangeLog0
-rw-r--r--src/etc-update/Makefile20
-rw-r--r--src/etc-update/README0
-rwxr-xr-xsrc/etc-update/etc-update165
-rw-r--r--src/etc-update/etc-update.112
-rw-r--r--src/etcat/AUTHORS5
-rw-r--r--src/etcat/ChangeLog36
-rw-r--r--src/etcat/Makefile18
-rw-r--r--src/etcat/README2
-rw-r--r--src/etcat/TODO0
-rwxr-xr-xsrc/etcat/etcat688
-rw-r--r--src/etcat/etcat.179
-rw-r--r--src/euse/AUTHORS2
-rw-r--r--src/euse/ChangeLog9
-rw-r--r--src/euse/Makefile20
-rwxr-xr-xsrc/euse/euse544
-rw-r--r--src/euse/euse.1102
-rw-r--r--src/eviewcvs/AUTHORS1
-rw-r--r--src/eviewcvs/Makefile22
-rw-r--r--src/eviewcvs/README11
-rwxr-xr-xsrc/eviewcvs/eviewcvs95
-rw-r--r--src/eviewcvs/eviewcvs.pod48
-rw-r--r--src/genpkgindex/Makefile18
-rw-r--r--src/genpkgindex/genpkgindex336
-rw-r--r--src/genpkgindex/genpkgindex.159
-rw-r--r--src/gensync/AUTHORS5
-rw-r--r--src/gensync/ChangeLog12
-rw-r--r--src/gensync/Makefile24
-rw-r--r--src/gensync/README16
-rw-r--r--src/gensync/TODO4
-rw-r--r--src/gensync/bmg-gnome-current.syncsource18
-rw-r--r--src/gensync/bmg-main.syncsource18
-rwxr-xr-xsrc/gensync/gensync226
-rw-r--r--src/gensync/gensync.175
-rw-r--r--src/gensync/gensync.conf8
-rw-r--r--src/gentoolkit/AUTHORS2
-rw-r--r--src/gentoolkit/Makefile22
-rw-r--r--src/gentoolkit/README17
-rw-r--r--src/gentoolkit/TODO0
-rw-r--r--src/gentoolkit/__init__.py59
-rw-r--r--src/gentoolkit/errors.py14
-rw-r--r--src/gentoolkit/helpers.py162
-rw-r--r--src/gentoolkit/package.py241
-rw-r--r--src/gentoolkit/pprinter.py116
-rw-r--r--src/glsa-check/Makefile20
-rw-r--r--src/glsa-check/glsa-check364
-rw-r--r--src/glsa-check/glsa-check.157
-rw-r--r--src/glsa-check/glsa.py644
-rw-r--r--src/lintool/AUTHORS1
-rw-r--r--src/lintool/COPYING340
-rw-r--r--src/lintool/ChangeLog71
-rw-r--r--src/lintool/NEWS0
-rw-r--r--src/lintool/README23
-rw-r--r--src/lintool/lintool.141
-rwxr-xr-xsrc/lintool/lintool.py320
-rw-r--r--src/lintool/lintool/__init__.py3
-rw-r--r--src/lintool/lintool/changelog.py105
-rw-r--r--src/lintool/lintool/digest.py28
-rw-r--r--src/lintool/lintool/ebuild.py349
-rw-r--r--src/lintool/lintool/test.py30
-rw-r--r--src/moo/AUTHORS1
-rw-r--r--src/moo/README0
-rw-r--r--src/moo/TODO0
-rwxr-xr-xsrc/moo/moo244
-rw-r--r--src/moo/moo.112
-rw-r--r--src/old-scripts/Makefile32
-rw-r--r--src/old-scripts/dep-clean272
-rw-r--r--src/old-scripts/dep-clean.1190
-rwxr-xr-xsrc/old-scripts/ewhich44
-rw-r--r--src/old-scripts/ewhich.124
-rw-r--r--src/old-scripts/mkebuild216
-rw-r--r--src/old-scripts/mkebuild.120
-rw-r--r--src/old-scripts/pkg-clean107
-rw-r--r--src/old-scripts/pkg-clean.120
-rw-r--r--src/old-scripts/pkg-size63
-rw-r--r--src/old-scripts/pkg-size.111
-rw-r--r--src/pkg-clean/AUTHORS5
-rw-r--r--src/pkg-clean/ChangeLog0
-rw-r--r--src/pkg-clean/README0
-rw-r--r--src/pkg-clean/pkg-clean99
-rw-r--r--src/pkg-clean/pkg-clean.120
-rw-r--r--src/pkg-size/pkg-size66
-rw-r--r--src/qpkg/AUTHORS0
-rw-r--r--src/qpkg/ChangeLog5
-rw-r--r--src/qpkg/Makefile19
-rw-r--r--src/qpkg/README0
-rw-r--r--src/qpkg/TODO0
-rw-r--r--src/qpkg/qpkg581
-rw-r--r--src/qpkg/qpkg.1112
-rw-r--r--src/qpkg/qpkg.sh520
-rw-r--r--src/revdep-rebuild/99revdep-rebuild21
-rw-r--r--src/revdep-rebuild/AUTHORS2
-rw-r--r--src/revdep-rebuild/ChangeLog9
-rw-r--r--src/revdep-rebuild/Makefile23
-rw-r--r--src/revdep-rebuild/README4
-rw-r--r--src/revdep-rebuild/TODO7
-rwxr-xr-xsrc/revdep-rebuild/find_pkgs.py22
-rwxr-xr-xsrc/revdep-rebuild/revdep-rebuild1094
-rwxr-xr-xsrc/revdep-rebuild/revdep-rebuild-old720
-rwxr-xr-xsrc/revdep-rebuild/revdep-rebuild-sh332
-rw-r--r--src/revdep-rebuild/revdep-rebuild.1101
-rw-r--r--src/useflag/AUTHORS0
-rw-r--r--src/useflag/ChangeLog0
-rw-r--r--src/useflag/README0
-rw-r--r--src/useflag/useflag610
-rw-r--r--src/useflag/useflag.169
208 files changed, 19744 insertions, 0 deletions
diff --git a/src/99gentoolkit-env b/src/99gentoolkit-env
new file mode 100644
index 0000000..3933874
--- /dev/null
+++ b/src/99gentoolkit-env
@@ -0,0 +1 @@
+CONFIG_PROTECT_MASK="/etc/revdep-rebuild"
diff --git a/src/change/AUTHORS b/src/change/AUTHORS
new file mode 100644
index 0000000..4b3873a
--- /dev/null
+++ b/src/change/AUTHORS
@@ -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/src/change/ChangeLog b/src/change/ChangeLog
new file mode 100644
index 0000000..bd7d5dd
--- /dev/null
+++ b/src/change/ChangeLog
@@ -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/src/change/README b/src/change/README
new file mode 100644
index 0000000..bda1842
--- /dev/null
+++ b/src/change/README
@@ -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/src/change/change b/src/change/change
new file mode 100644
index 0000000..094573b
--- /dev/null
+++ b/src/change/change
@@ -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/src/change/change.1 b/src/change/change.1
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/change/change.1
diff --git a/src/dep-clean/AUTHORS b/src/dep-clean/AUTHORS
new file mode 100644
index 0000000..2fa030f
--- /dev/null
+++ b/src/dep-clean/AUTHORS
@@ -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/src/dep-clean/ChangeLog b/src/dep-clean/ChangeLog
new file mode 100644
index 0000000..dc6980e
--- /dev/null
+++ b/src/dep-clean/ChangeLog
@@ -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/src/dep-clean/README b/src/dep-clean/README
new file mode 100644
index 0000000..6521aef
--- /dev/null
+++ b/src/dep-clean/README
@@ -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/src/dep-clean/dep-clean b/src/dep-clean/dep-clean
new file mode 100644
index 0000000..2f2bde0
--- /dev/null
+++ b/src/dep-clean/dep-clean
@@ -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/src/dep-clean/dep-clean.1 b/src/dep-clean/dep-clean.1
new file mode 100644
index 0000000..9e42019
--- /dev/null
+++ b/src/dep-clean/dep-clean.1
@@ -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/src/dev-scripts/README b/src/dev-scripts/README
new file mode 100644
index 0000000..990a2ab
--- /dev/null
+++ b/src/dev-scripts/README
@@ -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/src/dev-scripts/included_headers.sh b/src/dev-scripts/included_headers.sh
new file mode 100755
index 0000000..915628b
--- /dev/null
+++ b/src/dev-scripts/included_headers.sh
@@ -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/src/dev-scripts/linking_libs.sh b/src/dev-scripts/linking_libs.sh
new file mode 100755
index 0000000..a249305
--- /dev/null
+++ b/src/dev-scripts/linking_libs.sh
@@ -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/src/distfiles-clean/AUTHORS b/src/distfiles-clean/AUTHORS
new file mode 100644
index 0000000..d913891
--- /dev/null
+++ b/src/distfiles-clean/AUTHORS
@@ -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/src/distfiles-clean/ChangeLog b/src/distfiles-clean/ChangeLog
new file mode 100644
index 0000000..dfe6aa8
--- /dev/null
+++ b/src/distfiles-clean/ChangeLog
@@ -0,0 +1,2 @@
+2002-15-11: Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Imported newest contributions from #10647.
diff --git a/src/distfiles-clean/TODO b/src/distfiles-clean/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/distfiles-clean/TODO
diff --git a/src/distfiles-clean/distfiles-clean b/src/distfiles-clean/distfiles-clean
new file mode 100644
index 0000000..23af32b
--- /dev/null
+++ b/src/distfiles-clean/distfiles-clean
@@ -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/src/ebump/AUTHORS b/src/ebump/AUTHORS
new file mode 100644
index 0000000..2432e06
--- /dev/null
+++ b/src/ebump/AUTHORS
@@ -0,0 +1,5 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Original author:
+Karl Trygve Kalleberg <karltk@gentoo.org>
diff --git a/src/ebump/ChangeLog b/src/ebump/ChangeLog
new file mode 100644
index 0000000..4434b94
--- /dev/null
+++ b/src/ebump/ChangeLog
@@ -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/src/ebump/Makefile b/src/ebump/Makefile
new file mode 100644
index 0000000..aa1d347
--- /dev/null
+++ b/src/ebump/Makefile
@@ -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/src/ebump/README b/src/ebump/README
new file mode 100644
index 0000000..c81835c
--- /dev/null
+++ b/src/ebump/README
@@ -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/src/ebump/TODO b/src/ebump/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/ebump/TODO
diff --git a/src/ebump/ebump b/src/ebump/ebump
new file mode 100755
index 0000000..2623a28
--- /dev/null
+++ b/src/ebump/ebump
@@ -0,0 +1,356 @@
+#! /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 2 ] ; 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-cvs do not add to CVS"
+ echo " -m|--message append message to ChangeLog"
+ echo " -d|--delete-old delete previous revision from CVS (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
+}
+
+#
+# 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() {
+ ebuild_arg=${1}
+
+ # Files to add to CVS
+ addfiles=""
+ # Files to remove from CVS
+ 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"
+
+ cp ${PF}.ebuild ${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}
+ 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 ChangeLog entry
+ #
+
+ if [ "${opt_add_changelog}" == "y" ] &&
+ [ -f ChangeLog ] ; then
+
+ # Add ChangeLog entry
+
+ curdate=$(LC_TIME="us" date +"%02d %b %Y")
+ cp ChangeLog ChangeLog.old
+ (
+ # Use header (four first lines) of the old ChangeLog
+ head -n 4 ChangeLog.old
+
+ # Write new entry
+
+ echo "*${newPF} (${curdate})"
+ echo
+ echo " ${curdate}; ${AUTHORNAME} <${AUTHOREMAIL}> ${filelist}"
+
+ # If we don't have a commit message, add comment
+ if [ -z "${opt_commitmessage}" ] ; then
+ echo " # INSERT ENTRY HERE"
+ if [ "${opt_delete_old}" == "y" ] && [ ! -z "${delfiles}" ] ; then
+ echo " Removed ${delfiles}."
+ fi
+ echo
+ else
+ echo " ${opt_commitmessage}"
+ echo
+ fi
+
+ # Write tail of old ChangeLog
+ nl=$(wc -l ChangeLog.old | sed -r "s/^([0-9]+).*/\1/")
+ tail -n $[nl - 4] ChangeLog.old
+ ) > ChangeLog
+ rm ChangeLog.old
+
+ einfo "Added ChangeLog entry"
+ fi
+
+ #
+ # (Optional) Add CVS entry for all new files
+ #
+
+ if [ "${opt_add_cvs}" == "y" ] ; then
+
+ # Add all new files to CVS
+ for x in ${addfiles} ; do
+ if [ -d ${x} ] ; then
+ find ${x} | xargs echo cvs add
+ else
+ cvs add ${x}
+ fi
+ done
+ einfo "Added ${addfiles} to CVS"
+ fi
+
+
+ #
+ # (Optional) Delete previous entry
+ #
+
+ if [ "${opt_delete_old}" == "y" ] ; then
+
+ for x in ${delfiles} ; do
+ cvs remove -f ${x}
+ done
+ einfo "Removed ${delfiles} from CVS"
+ fi
+
+}
+
+original_params=${#}
+
+#
+# Global options
+#
+opt_verbosity=1
+opt_warn_on_delete=y
+opt_add_changelog=y
+opt_add_cvs=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-cvs)
+ opt_add_cvs=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
+
+process_ebuild ${ebuild_arg}
+
+# TODO:
+# - put cli parser into separate functions
diff --git a/src/ebump/ebump.1 b/src/ebump/ebump.1
new file mode 100644
index 0000000..6a64a0a
--- /dev/null
+++ b/src/ebump/ebump.1
@@ -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 CVS, and a
+dummy ChangeLog entry will be made.
+
+.LP
+You must stand in the directory of the ebuild to be bumped.
+
+.SH "OPTIONS"
+.LP
+\fB\-C\fR
+.br
+\fB--no-cvs\fB
+.IP
+Do not add new files to CVS.
+
+.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 CVS. 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_cvs\fR (default \fIy\fR) - add new files to CVS
+.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~/.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\fR and \fIego\fR.
+
+.SH "AUTHORS"
+.LP
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
diff --git a/src/echangelog/AUTHORS b/src/echangelog/AUTHORS
new file mode 100644
index 0000000..36d5bfd
--- /dev/null
+++ b/src/echangelog/AUTHORS
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/src/echangelog/ChangeLog b/src/echangelog/ChangeLog
new file mode 100644
index 0000000..c1c5885
--- /dev/null
+++ b/src/echangelog/ChangeLog
@@ -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/src/echangelog/Makefile b/src/echangelog/Makefile
new file mode 100644
index 0000000..61c5e57
--- /dev/null
+++ b/src/echangelog/Makefile
@@ -0,0 +1,23 @@
+# 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: echangelog.1
+
+dist: echangelog.1
+ mkdir -p ../../$(distdir)/src/echangelog/
+ cp Makefile AUTHORS README TODO ChangeLog echangelog echangelog.pod echangelog.1 ../../$(distdir)/src/echangelog/
+
+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/src/echangelog/README b/src/echangelog/README
new file mode 100644
index 0000000..77a7930
--- /dev/null
+++ b/src/echangelog/README
@@ -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/src/echangelog/TODO b/src/echangelog/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/echangelog/TODO
diff --git a/src/echangelog/echangelog b/src/echangelog/echangelog
new file mode 100644
index 0000000..1a4bee7
--- /dev/null
+++ b/src/echangelog/echangelog
@@ -0,0 +1,493 @@
+#!/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::Find;
+
+# 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, $version, $year, $vcs);
+
+my %vcs = ( cvs => { diff => "cvs -f diff -U0",
+ status => "cvs -fn up",
+ add => "cvs -f add",
+ skip => 6,
+ regex => qr/^Index: ()(([^\/]*?)\.ebuild)\s*$/ },
+ svn => { diff => "svn diff -N",
+ status => "svn status",
+ add => "svn add",
+ skip => 6,
+ regex => qr/^Index: ()(([^\/]*?)\.ebuild)\s*$/ },
+ git => { diff => "git diff",
+ status => "git diff-index HEAD --name-status",
+ add => "git add",
+ skip => 4,
+ regex => qr/^diff \-\-git \S*\/((\S*)\.ebuild)/ }
+
+);
+
+# Figure out what kind of repo we are in.
+
+if ( -d "CVS" ) {
+ $vcs = "cvs";
+} elsif ( -d '.svn' ) {
+ $vcs = "svn";
+} elsif ( -f '/usr/bin/git' and open GIT, "git rev-parse --git-dir |" ) {
+ $vcs = "git";
+ close GIT;
+} else {
+ die "No CVS, .git, .svn directories found, what kind of repo is this?";
+}
+
+# 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 envvar PORTDIR |" or die "Can't find PORTDIR";
+ my ($new) = <C>;
+ close C;
+ $new =~ s/\s+$//;
+ open I, "< $new/skel.ChangeLog"
+ or die "Can't open $new/skel.ChangeLog for input: $!\n";
+ { local $/ = undef; $text = <I>; }
+ close I;
+ my ($cwd) = getcwd();
+ $cwd =~ m|.*/(\w+-\w+)/([^/]+)|
+ or die "Can't figure out category/package.. sorry!\n";
+ my ($category, $package_name) = ($1, $2);
+ $text =~ s/^\*.*//ms; # don't need the fake entry
+ $text =~ s/<CATEGORY>/$category/;
+ $text =~ s/<PACKAGE_NAME>/$package_name/;
+ } 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>) {
+ if (/^C\s+(\S+)/) {
+ if($vcs eq "git") {
+ my $filename = $2;
+ $filename =~ /\S*\/(\S*)/;
+ if( -d $1 ) {
+ next;
+ }
+ push @conflicts, $1;
+ next;
+ }
+ push @conflicts, $1;
+ next;
+ } elsif (/^\?\s+(\S+)/) {
+ if($vcs eq "git") {
+ my $filename = $2;
+ $filename =~ /\S*\/(\S*)/;
+ if( -d $1 ) {
+ next;
+ }
+ push @unknown, $1;
+ next;
+ } else {
+ push @unknown, $1;
+ }
+ $actions{$1} = '+';
+ next;
+ } elsif (/^([ARMD])\s+(\S+)/) {
+ my ($status, $filename) = ($1,$2);
+ if($vcs eq "git") {
+ open P, "git-rev-parse --sq --show-prefix |";
+ my $prefix = <P>;
+ $prefix = substr($prefix, 0, -1);
+ close P;
+
+ if ($filename =~ /$prefix(\S*)/) {
+ $filename = $1 ;
+ }
+ else {
+ next;
+ }
+ }
+ if( -d $filename ) {
+ next;
+ }
+ push @files, $filename;
+ ($actions{$filename} = $status) =~ tr/DARM/-+-/d;
+ }
+}
+
+# git only shows files already added so we need to check for unknown files
+# separately here.
+if($vcs eq "git") {
+ find(\&git_unknown_objects, "./");
+}
+
+sub git_unknown_objects {
+ my $object = $_;
+ my ($dev,$ino,$mode,$nlink,$uid,$gid);
+
+ # Ignore empty directories - git doesn't version them and cvs removes them.
+ if ((($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) && ! -d _) {
+ open C, $vcs." status $_ 2>&1 1>/dev/null |";
+
+ while (<C>) {
+ $_ = <C>;
+ push @unknown, $object;
+ };
+ close C;
+ };
+}
+
+# Separate out the trivial files for now
+@files = grep {
+ !/files.digest|Manifest|ChangeLog/ or do { push @trivial, $_; 0; }
+} @files;
+
+@unknown = grep {
+ !/files.digest|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;
+}
+@files = sort sortfunc @files;
+
+# 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/) {
+ push @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 $f;
+ if ($vcs eq "git") {
+ ($f) = ($1);
+ my $version = ($2);
+ while (<C>) {
+ last if /^deleted file mode|^index/;
+ if (/^new file mode/) {
+ push @new_versions, $version; # new ebuild, will create a new entry
+ last;
+ }
+ }
+ } else {
+ ($f) = ($2);
+ }
+
+ # 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)/) {
+ push @files, $f;
+ 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/) {
+ push @files, $1;
+ push @new_versions, $2; # new ebuild, will create a new entry
+ }
+ # other cvs output is ignored
+ $_ = <C>;
+ }
+}
+close 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";
+ @files = sort sortfunc @trivial;
+ @files = qw/ChangeLog/ unless @files; # last resort to put something in the list
+}
+
+# Get the input from the cmdline, editor or stdin
+if ($ARGV[0]) {
+ $input = "@ARGV";
+} else {
+ # Testing for defined() allows ECHANGELOG_EDITOR='' to cancel EDITOR
+ $editor = defined($ENV{'ECHANGELOG_EDITOR'}) ? $ENV{'ECHANGELOG_EDITOR'} :
+ $ENV{'EDITOR'} || undef;
+ if ($editor) {
+ 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('/usr/bin/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;
+ } else {
+ print STDERR "Error opening ChangeLog.new: $!\n";
+ print STDERR "Reverting to stdin method.\n";
+ undef $editor;
+ }
+ unlink '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 =~ s/^\s*(.*?)\s*\z/$1/s; # trim whitespace
+$input = Text::Wrap::fill(' ', ' ', $input);
+
+# Prepend the user info to the input
+unless ($user = $ENV{'ECHANGELOG_USER'}) {
+ 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";
+}
+
+sub update_copyright {
+ my ($t) = @_;
+ (my $year = $date) =~ s/.* //;
+ $t =~ s/^# Copyright \d+(?= )/$&-$year/m or
+ $t =~ s/^(# Copyright \d+)-(\d+)/$1-$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");
+ }
+}
+
+# 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:sw=4 ts=8 expandtab
diff --git a/src/echangelog/echangelog.1 b/src/echangelog/echangelog.1
new file mode 100644
index 0000000..f83eaf7
--- /dev/null
+++ b/src/echangelog/echangelog.1
@@ -0,0 +1,275 @@
+.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.13
+.\"
+.\" 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. | 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.
+. \" 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 "2004-04-02" "perl v5.8.2" "User Contributed Perl Documentation"
+.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 'cvs commit' to add this file permanently
+.Ve
+.PP
+.Vb 13
+\& $ echangelog 'New ebuild, thanks to Harvey McGillicuddy'
+\& --- 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 'cvs commit' to add this file permanently
+.Ve
+.PP
+.Vb 6
+\& $ echangelog 'Bump revision to fix bug #999'
+\& --- 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$
+.Ve
+.PP
+.Vb 6
+\& +*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)
+.Ve
+.PP
+.Vb 1
+\& 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$
+.Ve
+.PP
+.Vb 8
+\& +*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)
+.Ve
+.PP
+.Vb 1
+\& 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/src/echangelog/echangelog.pod b/src/echangelog/echangelog.pod
new file mode 100644
index 0000000..192af8d
--- /dev/null
+++ b/src/echangelog/echangelog.pod
@@ -0,0 +1,136 @@
+=head1 NAME
+
+echangelog - Gentoo: update portage ChangeLogs
+
+=head1 SYNOPSIS
+
+echangelog [ I<text> ]
+
+=head1 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 I<text> is not provided on the command-line,
+echangelog prompts for it.
+
+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 "cvs add" on your files, otherwise
+echangelog won't know those files are part of the update.
+
+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 "echangelog blah" so that the header lines
+are computed correctly, then edit and change "blah" to your preferred
+text.
+
+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.
+
+=head1 OPTIONS
+
+Presently echangelog 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 create a ChangeLog for a completely new package. The header is
+parsed from skel.ebuild.
+
+ $ cvs add metalog-0.1.ebuild
+ cvs server: use 'cvs commit' to add this file permanently
+
+ $ echangelog 'New ebuild, thanks to Harvey McGillicuddy'
+ --- 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
+ +
+
+To bump a revision. Note you need to "cvs add" so that echangelog
+will notice the new file.
+
+ $ cvs add metalog-0.1-r1.ebuild
+ cvs server: use 'cvs commit' to add this file permanently
+
+ $ echangelog 'Bump revision to fix bug #999'
+ --- 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 :
+
+For a multi-line entry, omit the command-line arg.
+
+ $ 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 :
+
+=head1 ENVIRONMENT VARIABLES
+
+=over
+
+=item ECHANGELOG_USER
+
+If echangelog can't figure out your username for the entry, you should
+set ECHANGELOG_USER. For example, export ECHANGELOG_USER="Aron
+Griffis <agriffis@gentoo.org>"
+
+=back
+
+=head1 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 "new" ChangeLog format was never agreed upon by
+the Gentoo developers. Unfortunately the existence of both formats
+will undoubtedly cause much confusion.
+
+This also means that the examples above are wrong, since I just copied
+them from some old email. However they're not much wrong. ;-)
+
+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/src/eclean/AUTHORS b/src/eclean/AUTHORS
new file mode 100644
index 0000000..9263cbb
--- /dev/null
+++ b/src/eclean/AUTHORS
@@ -0,0 +1 @@
+Thomas de Grenier de Latour (tgl) <degrenier@easyconnect.fr>
diff --git a/src/eclean/ChangeLog b/src/eclean/ChangeLog
new file mode 100644
index 0000000..36d9a28
--- /dev/null
+++ b/src/eclean/ChangeLog
@@ -0,0 +1,27 @@
+2005-12-19 Paul Varner <fuzzyray@gentoo.org>
+ * Add support for reqular expression matching for file names in the
+ exclude files.
+
+2005-08-28 Thomas de Grenier de Latour (tgl) <degrenier@easyconnect.fr>
+ * Version 0.4.1
+ * added support for some "eclean-dist" and "eclean-pkg" symlinks on eclean
+ (and thus refactored command-line parsing and help screen code)
+ * accept file names in exclude files for specific distfiles protection
+ (useful to protect the OOo i18n files for instance, which are not in
+ $SRC_URI but put there manually)
+ * minor rewrite of some findDistfiles() code
+ * added /usr/lib/portage/pym python path, just to be sure it comes first
+ (after all, "ouput" is a pretty generic name for a python module...)
+ * updated manpage
+
+2005-08-27 Thomas de Grenier de Latour (tgl) <degrenier@easyconnect.fr>
+ * Version 0.4
+ * added exclusion files support
+ * added time limit option
+ * added size limit option (for distfiles only)
+ * added fetch-restricted distfile optionnal protection
+ * added --package-names option for protection of all versions of installed
+ packages.
+ * removed support of multiple actions on command-line. That would have been
+ hell with action-specific options.
+ * updated manpage
diff --git a/src/eclean/Makefile b/src/eclean/Makefile
new file mode 100644
index 0000000..79c5895
--- /dev/null
+++ b/src/eclean/Makefile
@@ -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
+
+all:
+
+dist:
+ mkdir -p ../../$(distdir)/src/eclean
+ cp eclean eclean.1 Makefile *.exclude ../../$(distdir)/src/eclean
+ cp AUTHORS THANKS TODO ChangeLog ../../$(distdir)/src/eclean
+
+install:
+ install -m 0755 eclean $(bindir)/
+ ln -sf eclean $(bindir)/eclean-pkg
+ ln -sf eclean $(bindir)/eclean-dist
+ install -d $(sysconfdir)/eclean
+ install -m 0644 distfiles.exclude packages.exclude $(sysconfdir)/eclean/
+ install -d $(docdir)/eclean
+ install -m 0644 AUTHORS THANKS TODO ChangeLog $(docdir)/eclean/
+ install -m 0644 eclean.1 $(mandir)/
diff --git a/src/eclean/THANKS b/src/eclean/THANKS
new file mode 100644
index 0000000..6b8dc2e
--- /dev/null
+++ b/src/eclean/THANKS
@@ -0,0 +1,7 @@
+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/src/eclean/TODO b/src/eclean/TODO
new file mode 100644
index 0000000..04e64ca
--- /dev/null
+++ b/src/eclean/TODO
@@ -0,0 +1,16 @@
+- exclusion file syntax could be improved (maybe it should support real
+ dep-atoms, or wildcards, etc.)
+
+- some policy to keep the X latest versions of a package (in each of its
+ SLOT maybe) would be really cool...
+
+- add an option to protect system binary packages
+ => yup, but later... (needs some portage modifications to be done right)
+
+- add actions for PORT_LOGDIR and/or /var/tmp/portage cleaning?
+ => bah, don't know... imho tmpreaper or find+rm onliners are enough here
+
+- cleanup of DISTDIR/cvs-src when action=distfiles
+ => i never use cvs ebuilds, i should check what it does exactly
+
+- rewrite for a decent Portage API if there ever is one
diff --git a/src/eclean/distfiles.exclude b/src/eclean/distfiles.exclude
new file mode 100644
index 0000000..a31be55
--- /dev/null
+++ b/src/eclean/distfiles.exclude
@@ -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/src/eclean/eclean b/src/eclean/eclean
new file mode 100644
index 0000000..4fdec10
--- /dev/null
+++ b/src/eclean/eclean
@@ -0,0 +1,807 @@
+#!/usr/bin/env python
+# Copyright 2003-2005 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+
+###############################################################################
+# Meta:
+__author__ = "Thomas de Grenier de Latour (tgl)"
+__email__ = "degrenier@easyconnect.fr"
+__version__ = "0.4.1"
+__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
+try:
+ import portage
+except ImportError:
+ sys.path.insert(0, "/usr/lib/portage/pym")
+ import portage
+try:
+ from portage.output import *
+except ImportError:
+ from 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 (version %s) - %s" \
+ % (__productname__, __version__, __description__)
+ print "Author: %s <%s>" % (__author__,__email__)
+ print "Copyright 2003-2005 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( \
+ 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
+ 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( \
+ exclude_dict={}, \
+ destructive=False, \
+ time_limit=0, \
+ package_names=False):
+ clean_dict = {}
+ # create a full package dictionnary
+ 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,...
+ 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
+ # 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( \
+ exclude_dict=exclude_dict, \
+ destructive=myoptions['destructive'], \
+ package_names=myoptions['package-names'], \
+ time_limit=myoptions['time-limit'])
+ else:
+ clean_dict = findDistfiles( \
+ 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/src/eclean/eclean.1 b/src/eclean/eclean.1
new file mode 100644
index 0000000..7d785af
--- /dev/null
+++ b/src/eclean/eclean.1
@@ -0,0 +1,176 @@
+.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
+.TP
+Yacleaner, one of the other similar tools:
+.B http://blog.tacvbo.net/data/files/yacleaner/
+.SH "AUTHORS"
+Thomas de Grenier de Latour (tgl) <degrenier@easyconnect.fr>
diff --git a/src/eclean/packages.exclude b/src/eclean/packages.exclude
new file mode 100644
index 0000000..8277155
--- /dev/null
+++ b/src/eclean/packages.exclude
@@ -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/src/ego/AUTHOR b/src/ego/AUTHOR
new file mode 100644
index 0000000..36d5bfd
--- /dev/null
+++ b/src/ego/AUTHOR
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/src/ego/AUTHORS b/src/ego/AUTHORS
new file mode 100644
index 0000000..36d5bfd
--- /dev/null
+++ b/src/ego/AUTHORS
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/src/ego/ChangeLog b/src/ego/ChangeLog
new file mode 100644
index 0000000..503d0da
--- /dev/null
+++ b/src/ego/ChangeLog
@@ -0,0 +1,2 @@
+2004-15-01 Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Added Makefile
diff --git a/src/ego/Makefile b/src/ego/Makefile
new file mode 100644
index 0000000..b27a2cb
--- /dev/null
+++ b/src/ego/Makefile
@@ -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/src/ego/README b/src/ego/README
new file mode 100644
index 0000000..6c44b4c
--- /dev/null
+++ b/src/ego/README
@@ -0,0 +1,2 @@
+[ -f ${HOME}/scripts/ego.bash ] &&
+alias ego="unalias ego ; source ${HOME}/scripts/ego.bash ; ego "
diff --git a/src/ego/TODO b/src/ego/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/ego/TODO
diff --git a/src/ego/ego b/src/ego/ego
new file mode 100644
index 0000000..f1691f2
--- /dev/null
+++ b/src/ego/ego
@@ -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/src/ekeyword/AUTHORS b/src/ekeyword/AUTHORS
new file mode 100644
index 0000000..36d5bfd
--- /dev/null
+++ b/src/ekeyword/AUTHORS
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/src/ekeyword/ChangeLog b/src/ekeyword/ChangeLog
new file mode 100644
index 0000000..d0fa818
--- /dev/null
+++ b/src/ekeyword/ChangeLog
@@ -0,0 +1,37 @@
+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/src/ekeyword/Makefile b/src/ekeyword/Makefile
new file mode 100644
index 0000000..c3aed80
--- /dev/null
+++ b/src/ekeyword/Makefile
@@ -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
+
+%.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)/
+
diff --git a/src/ekeyword/README b/src/ekeyword/README
new file mode 100644
index 0000000..ec7ff5e
--- /dev/null
+++ b/src/ekeyword/README
@@ -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/src/ekeyword/TODO b/src/ekeyword/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/ekeyword/TODO
diff --git a/src/ekeyword/ekeyword b/src/ekeyword/ekeyword
new file mode 100644
index 0000000..e75ce93
--- /dev/null
+++ b/src/ekeyword/ekeyword
@@ -0,0 +1,114 @@
+#!/usr/bin/perl -w
+#
+# Copyright 2003-2004, 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
+# 12c12
+# < KEYWORDS="x86 ppc sparc"
+# ---
+# > KEYWORDS="x86 ppc sparc ~alpha"
+
+
+my ($kw_re) = '^(?:([-~^]?)(\w[\w-]*)|([-^]\*))$';
+my (@kw);
+
+# make sure the cmdline consists of keywords and ebuilds
+unless (@ARGV > 0) {
+ die "syntax: ekeyword { arch | ~[arch] | -[arch] } ebuild...\n"
+}
+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";
+}
+
+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;
+
+ while (<I>) {
+ /^KEYWORDS/ or print, next;
+
+ # extract the quoted section from KEYWORDS
+ (my $quoted = $_) =~ s/^.*?["'](.*?)["'].*/$1/s;
+
+ # 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 cmp $sb;
+ } split " ", $quoted;
+
+ # re-insert quoted to KEYWORDS
+ s/(["']).*?["']/$1$quoted$1/;
+
+ print $_, <I> or die "Can't write $f.new: $!\n";
+ }
+
+ close I;
+ close O;
+ select STDOUT;
+
+ system "diff $f $f.new | grep -v '^[0-1]'";
+ rename "$f.new", "$f" or die "Can't rename: $!\n";
+}
+
+# vim:ts=8 sw=4
diff --git a/src/ekeyword/ekeyword.pod b/src/ekeyword/ekeyword.pod
new file mode 100644
index 0000000..1fac8ef
--- /dev/null
+++ b/src/ekeyword/ekeyword.pod
@@ -0,0 +1,74 @@
+=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="~x86 ~ppc ~sparc ~alpha ~mips ~hppa ~amd64 ~ia64"
+ ---
+ > KEYWORDS="~x86 ~ppc ~sparc alpha ~mips ~hppa ~amd64 ~ia64"
+
+When bumping a package, to mark all arches for testing:
+
+ $ ekeyword ~all metalog-0.7-r2.ebuild
+ metalog-0.7-r2.ebuild
+ < KEYWORDS="x86 ppc sparc alpha mips hppa amd64 ia64"
+ ---
+ > KEYWORDS="~x86 ~ppc ~sparc ~alpha ~mips ~hppa ~amd64 ~ia64"
+
+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="~x86 ~ppc ~sparc ~alpha ~mips ~hppa ~amd64 ~ia64"
+ ---
+ > 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="~x86 ~ppc ~sparc ~alpha ~mips ~hppa ~amd64 ~ia64"
+ ---
+ > KEYWORDS="~x86 ~ppc ~sparc alpha ~mips ~hppa ~amd64 ~ia64"
+ metalog-0.7-r2.ebuild
+ < KEYWORDS="x86 ppc sparc alpha mips hppa amd64 ia64"
+ ---
+ > KEYWORDS="~x86 ~ppc ~sparc ~alpha ~mips ~hppa ~amd64 ~ia64"
+ metalog-0.7-r3.ebuild
+ < KEYWORDS="~x86 ~ppc ~sparc ~alpha ~mips ~hppa ~amd64 ~ia64"
+ ---
+ > 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/src/epkginfo/Makefile b/src/epkginfo/Makefile
new file mode 100644
index 0000000..6a8de9a
--- /dev/null
+++ b/src/epkginfo/Makefile
@@ -0,0 +1,17 @@
+# Copyright 2007 Gentoo Foundation.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+ echo "CLIXBY (adj.) Politely rude. Bliskly vague. Firmly uninformative."
+
+dist:
+ mkdir -p ../../$(distdir)/src/epkginfo
+ cp Makefile epkginfo epkginfo.1 ../../$(distdir)/src/epkginfo/
+
+install:
+ install -m 0755 epkginfo $(bindir)/
+ install -m 0644 epkginfo.1 $(mandir)/
diff --git a/src/epkginfo/epkginfo b/src/epkginfo/epkginfo
new file mode 100755
index 0000000..637deff
--- /dev/null
+++ b/src/epkginfo/epkginfo
@@ -0,0 +1,210 @@
+#!/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
+try:
+ import portage
+except ImportError:
+ sys.path.insert(0, "/usr/lib/portage/pym")
+ import portage
+import re
+from stat import *
+try:
+ from portage.output import *
+except ImportError:
+ from output import *
+from xml.sax import saxutils, make_parser, handler
+from xml.sax.handler import feature_namespaces
+
+version="0.4.1"
+
+def getvar(pkg, var):
+ file = open(pkg + ".ebuild")
+ for line in file.readlines():
+ line = line.rstrip()
+ if re.match("^"+var+"=",line):
+ vars = re.split("\"",line)[1]
+ file.close
+ return re.split(" ",vars)
+ file.close
+
+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)))
+
+ for pkg in ebuildlist:
+ keywords = getvar(pkg, "KEYWORDS")
+ for arch in keywords:
+ if arch == "":
+ arch = None
+ archdict[arch] = pkg
+
+ archlist = archdict.keys();
+ archlist.sort()
+
+ for pkg in ebuildlist:
+ print darkgreen("Keywords: ") + pkg + ":",
+ for value in archlist:
+ if (value and archdict[value] == pkg):
+ if value[0] == "-":
+ print red(value),
+ elif "~" == value[0]:
+ print blue(value),
+ else:
+ print green(value),
+ print ""
+
+
+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 = 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 hed 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 len(handler._herd) < 1:
+ print darkgreen("Herd: ") + red("Error (No Herd)")
+ return 1
+ else:
+ print darkgreen("Herd: ") + handler._herd
+
+ if len(handler._maintainers) < 1:
+ print darkgreen("Maintainer: ") + handler._herd
+ else:
+ print darkgreen("Maintainer: ") + ", ".join(handler._maintainers)
+
+ 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 v" + version + "\n")
+ 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 ""
+ except:
+ print red("Error: "+pkg+"\n")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/epkginfo/epkginfo.1 b/src/epkginfo/epkginfo.1
new file mode 100644
index 0000000..cefe602
--- /dev/null
+++ b/src/epkginfo/epkginfo.1
@@ -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/src/epkgmove/AUTHORS b/src/epkgmove/AUTHORS
new file mode 100644
index 0000000..38fdca1
--- /dev/null
+++ b/src/epkgmove/AUTHORS
@@ -0,0 +1,2 @@
+Maintainer:
+Ian Leitch <port001@gentoo.org>
diff --git a/src/epkgmove/ChangeLog b/src/epkgmove/ChangeLog
new file mode 100644
index 0000000..6bfb8d7
--- /dev/null
+++ b/src/epkgmove/ChangeLog
@@ -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/src/epkgmove/Makefile b/src/epkgmove/Makefile
new file mode 100644
index 0000000..ce9b950
--- /dev/null
+++ b/src/epkgmove/Makefile
@@ -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/src/epkgmove/README b/src/epkgmove/README
new file mode 100644
index 0000000..4668fa3
--- /dev/null
+++ b/src/epkgmove/README
@@ -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/src/epkgmove/TODO b/src/epkgmove/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/epkgmove/TODO
diff --git a/src/epkgmove/epkgmove b/src/epkgmove/epkgmove
new file mode 100644
index 0000000..42b6e7d
--- /dev/null
+++ b/src/epkgmove/epkgmove
@@ -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/src/equery/AUTHORS b/src/equery/AUTHORS
new file mode 100644
index 0000000..9935ef7
--- /dev/null
+++ b/src/equery/AUTHORS
@@ -0,0 +1,3 @@
+Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Initial version
+
diff --git a/src/equery/Makefile b/src/equery/Makefile
new file mode 100644
index 0000000..177427d
--- /dev/null
+++ b/src/equery/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2003 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+ echo "YADDLETHORPE (vb.) (Of offended pooves.) To exit huffily from a boutique."
+
+dist:
+ mkdir -p ../../$(distdir)/src/equery/
+ cp Makefile AUTHORS README TODO equery equery.1 ../../$(distdir)/src/equery/
+
+install:
+ install -m 0755 equery $(bindir)/
+ install -d $(docdir)/equery
+ install -m 0644 README AUTHORS $(docdir)/equery/
+ install -m 0644 equery.1 $(mandir)/
diff --git a/src/equery/README b/src/equery/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/equery/README
diff --git a/src/equery/TODO b/src/equery/TODO
new file mode 100644
index 0000000..5f38e60
--- /dev/null
+++ b/src/equery/TODO
@@ -0,0 +1,63 @@
+- sqlite
+ - regexp comparisons
+ - check /var/log/emerge.log for database up-to-dateness
+
+
+-------------------------
+- pkgquery language:
+ Query ::= NewStyle | OldStyle | with OldStyle NewStyle
+ NewStyle ::= NameVar in /RegExp/
+ | VerVar in [ VerExpr ]
+ | SetVar in [ SetExpr ]
+ NameVar ::= PC | PN | DESCRIPTION | SRC_URI | HOMEPAGE
+
+ SetVar ::= LICENSE | KEYWORDS | IUSE
+ VerVar ::= SLOT | PV | DEPEND | RDEPEND
+
+ BinaryOp ::= and | or
+ UnaryOp ::= not
+
+ VerExpr ::= SingleVer
+ | VerExpr BinOp VerExpr
+ | UnaryOp UnaryOp
+
+ SetExpr ::= Element
+ | Element BinOp Element
+ | UnaryOp Element
+
+ SingleVer ::= PrefixOp VersionBody ( VersionSuffix )? ( - Revision )?
+ PrefixOp ::= ! | < | > | <= | >= | = | ~
+ VersionBody ::= Number ( . Number )+ ( . * )?
+ VersionSuffix ::= _ ( pre | beta | alpha | rc ) Number?
+ | [a-z]
+ Revision ::= r Number
+
+------
+
+ PC in /dev-java/ and
+ PN in /ant/ and
+ PV in [ >=1.0 or <=2.3 and =2.0.* ] and
+ IUSE in [ java or junit ]
+
+
+--
+ with >=dev-java/ant-1.0*
+ IUSE in [ java or junit ] and
+ SLOT in [ >=1.0 ]
+
+
+----------
+
+old cruft:
+
+ SingleVer ::= ( Operator )? ( Category / ) PackageName ( - Version )?
+ Operator ::= = | > | >= | < | <= | ~ | !
+ Category ::= PackageName
+ PackageName ::= NamePart ( - NamePart )+
+ NamePart ::= [a-zA-Z+]+
+ Version ::= VersionPart ( - VersionPart )+ ( _ VersionSuffix )? ( - Revision )?
+ VersionSuffix ::= ( pre | rc | beta | alpha ) ( Number ) ?
+
+ old style: >=dev-java/ant-1.0*
+
+
diff --git a/src/equery/equery b/src/equery/equery
new file mode 100755
index 0000000..63eb8b3
--- /dev/null
+++ b/src/equery/equery
@@ -0,0 +1,1846 @@
+#!/usr/bin/python
+#
+# Copyright 2003-2004 Karl Trygve Kalleberg
+# Copyright 2003-2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+# Author: Karl Trygve Kalleberg <karltk@gentoo.org>
+
+__author__ = "Karl Trygve Kalleberg"
+__email__ = "karltk@gentoo.org"
+__version__ = "0.1.4"
+__productname__ = "equery"
+__description__ = "Gentoo Package Query Tool"
+
+import os
+import re
+import sys
+import time
+
+# portage (output module) and gentoolkit need special path modifications
+sys.path.insert(0, "/usr/lib/gentoolkit/pym")
+
+import gentoolkit
+try:
+ import portage
+except ImportError:
+ sys.path.insert(0, "/usr/lib/portage/pym")
+ import portage
+
+try:
+ import portage.checksum as checksum
+ from portage.util import unique_array
+except ImportError:
+ import portage_checksum as checksum
+ from portage_util import unique_array
+
+import gentoolkit.pprinter as pp
+from gentoolkit.pprinter import print_info, print_error, print_warn, die
+
+# Auxiliary functions
+
+def fileAsStr(name, fdesc, showType=0, showMD5=0, showTimestamp=0):
+ """
+ Return file in fdesc as a filename
+ @param name:
+ @param fdesc:
+ @param showType:
+ @param showMD5:
+ @param showTimestamp:
+ @rtype: string
+ """
+ type = ""; fname = ""; stamp = ""; md5sum = ""
+
+ if fdesc[0] == 'obj':
+ type = "file"
+ fname = name
+ stamp = timestampAsStr(int(fdesc[1]))
+ md5sum = fdesc[2]
+ elif fdesc[0] == "dir":
+ type = "dir"
+ fname = pp.path(name)
+ elif fdesc[0] == "sym":
+ type = "symlink"
+ stamp = timestampAsStr(int(fdesc[1].replace(")","")))
+ tgt = fdesc[2].split()[0]
+ if Config["piping"]:
+ fname = name
+ else:
+ fname = pp.path_symlink(name + " -> " + tgt)
+ elif fdesc[0] == "fif":
+ type = "fifo"
+ fname = name
+ elif fdesc[0] == "dev":
+ type = "device"
+ fname = name
+ else:
+ raise Exception(name + " has unknown type: " + fdesc[0])
+
+ s = ""
+ if showType:
+ s += "%6s " % type
+ s += fname
+ if showTimestamp:
+ s += " " + stamp + " "
+ if showMD5:
+ s += " " + md5sum + " "
+ return s
+
+def timestampAsStr(timestamp):
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
+
+
+class Command:
+ """
+ Abstract root class for all equery commands
+ """
+ def __init__(self):
+ pass
+ def shortHelp(self):
+ """Return a help formatted to fit a single line, approx 70 characters.
+ Must be overridden in the subclass."""
+ return " - not implemented yet"
+ def longHelp(self):
+ """Return full, multiline, color-formatted help.
+ Must be overridden in the subclass."""
+ return "help for syntax and options"
+ def perform(self, args):
+ """Stub code for performing the command.
+ Must be overridden in the subclass"""
+ pass
+ def parseArgs(self, args):
+ """Stub code for parsing command line arguments for this command.
+ Must be overridden in the subclass."""
+ pass
+
+
+class CmdListFiles(Command):
+ """List files owned by a particular package"""
+ def __init__(self):
+ self.default_options = {
+ "showType": 0,
+ "showTimestamp": 0,
+ "showMD5": 0,
+ "tree": 0,
+ "filter": None
+ }
+
+ def parseArgs(self,args):
+ query = ""
+ need_help = 0
+ opts = self.default_options
+ for x in args:
+ if x in ["-h", "--help"]:
+ need_help = 1
+ elif x in ["--md5sum"]:
+ opts["showMD5"] = 1
+ elif x in ["--timestamp"]:
+ opts["showTimestamp"] = 1
+ elif x in ["--type"]:
+ opts["showType"] = 1
+ elif x in ["--tree"]:
+ opts["tree"] = 1
+ elif x[:9] == "--filter=":
+ opts["filter"] = x[9:].split(',')
+ elif x[0] == "/":
+ die(2, "The query '" + pp.pkgquery(x) + "' does not appear to be a valid package specification")
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def filterContents(self, cnt, filter):
+ if filter in [None,[]]:
+ return cnt
+
+ mycnt = {}
+
+ for mytype in filter:
+ # Filter elements by type (as recorded in CONTENTS).
+ if mytype in ["dir","obj","sym","dev","fif"]:
+ for mykey in cnt.keys():
+ if cnt[mykey][0] == mytype:
+ mycnt[mykey] = cnt[mykey]
+
+ if "cmd" in filter:
+ # List files that are in $PATH.
+ userpath = map(os.path.normpath,os.environ["PATH"].split(os.pathsep))
+ for mykey in cnt.keys():
+ if cnt[mykey][0] in ['obj','sym'] \
+ and os.path.dirname(mykey) in userpath:
+ mycnt[mykey] = cnt[mykey]
+
+ if "path" in filter:
+ # List only dirs where some files where actually installed,
+ # and also skip their subdirs.
+ mykeys = cnt.keys()
+ mykeys.sort()
+ while len(mykeys):
+ mykey = mykeys.pop(0)
+ if cnt[mykey][0] == 'dir':
+ i = 0
+ while i < len(mykeys) :
+ if cnt[mykeys[i]][0] != "dir" \
+ and os.path.dirname(mykeys[i]) == mykey:
+ mycnt[mykey] = cnt[mykey]
+ break
+ i += 1
+ if i < len(mykeys):
+ while len(mykeys) \
+ and len(mykey+"/") < len(mykeys[0]) \
+ and mykey+"/" == mykeys[0][:len(mykey)+1]:
+ mykeys.pop(0)
+
+ if "conf" in filter:
+ # List configuration files.
+ conf_path = gentoolkit.settings["CONFIG_PROTECT"].split()
+ conf_mask_path = gentoolkit.settings["CONFIG_PROTECT_MASK"].split()
+ conf_path = map(os.path.normpath, conf_path)
+ conf_mask_path = map(os.path.normpath, conf_mask_path)
+ for mykey in cnt.keys():
+ is_conffile = False
+ if cnt[mykey][0] == 'obj':
+ for conf_dir in conf_path:
+ if conf_dir+"/" == mykey[:len(conf_dir)+1]:
+ is_conffile = True
+ for conf_mask_dir in conf_mask_path:
+ if conf_mask_dir+"/" == mykey[:len(conf_mask_dir)+1]:
+ is_conffile = False
+ break
+ break
+ if is_conffile:
+ mycnt[mykey] = cnt[mykey]
+
+
+ for mydoctype in ["doc","man","info"]:
+ # List only files from /usr/share/{doc,man,info}
+ mydocpath = "/usr/share/"+mydoctype+"/"
+ if mydoctype in filter:
+ for mykey in cnt.keys():
+ if cnt[mykey][0] == 'obj' \
+ and mykey[:len(mydocpath)] == mydocpath :
+ mycnt[mykey] = cnt[mykey]
+
+ return mycnt
+
+ def perform(self, args):
+
+ (query, opts) = self.parseArgs(args)
+
+ # Turn off filtering for tree output
+ if opts["tree"]:
+ opts["filter"] = None
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
+
+ pkgs = gentoolkit.find_installed_packages(query, True)
+ for x in pkgs:
+ if not x.is_installed():
+ continue
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(1, pp.section("* ") + "Contents of " + pp.cpv(x.get_cpv()) + ":")
+
+ cnt = self.filterContents(x.get_contents(),opts["filter"])
+
+ filenames = cnt.keys()
+ filenames.sort()
+
+ last=[]
+ for name in filenames:
+ if not opts["tree"]:
+ print_info(0, fileAsStr(name,
+ cnt[name],
+ showType=opts["showType"],
+ showTimestamp=opts["showTimestamp"],
+ showMD5=opts["showMD5"]))
+ else:
+ c = name.split( "/" )[1:]
+ if cnt[name][0] == "dir":
+ if len(last) == 0:
+ last = c
+ print pp.path(" /" + c[0])
+ continue
+ numol = 0
+ for d in c:
+ if d in last:
+ numol = last.index(d) + 1
+ continue
+ last = c
+ if len(last) == 1:
+ print pp.path(" " + last[0])
+ continue
+ print pp.path(" " * ( numol * 3 ) + "> " + "/" + last[-1])
+ elif cnt[name][0] == "sym":
+ print pp.path(" " * ( bl * 3 ) + "+ ") + pp.path_symlink(c[-1] + " -> " + cnt[name][2])
+ else:
+ bl = len(last)
+ print pp.path(" " * ( bl * 3 ) + "+ ") + c[-1]
+
+ def longHelp(self):
+ return "List files owned by a particular package\n" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("files") + pp.localoption(" <local-opts> ") + pp.pkgquery("<cat/>packagename<-version>") + "\n" + \
+ "\n" + \
+ "Note: category and version parts are optional. \n" + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("--timestamp") + " - append timestamp\n" + \
+ " " + pp.localoption("--md5sum") + " - append md5sum\n" + \
+ " " + pp.localoption("--type") + " - prepend file type\n" + \
+ " " + pp.localoption("--tree") + " - display results in a tree (turns off other options)\n" + \
+ " " + pp.localoption("--filter=<rules>") + " - filter output\n" + \
+ " " + pp.localoption("<rules>") + " is a comma separated list of elements you want to see:\n" + \
+ " " + " " + pp.localoption("dir") + \
+ ", " + pp.localoption("obj") + \
+ ", " + pp.localoption("sym") + \
+ ", " + pp.localoption("dev") + \
+ ", " + pp.localoption("fifo") + \
+ ", " + pp.localoption("path") + \
+ ", " + pp.localoption("conf") + \
+ ", " + pp.localoption("cmd") + \
+ ", " + pp.localoption("doc") + \
+ ", " + pp.localoption("man") + \
+ ", " + pp.localoption("info")
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list files owned by " + pp.pkgquery("pkgspec")
+
+
+class CmdListBelongs(Command):
+ """List all packages owning a particular file"""
+ def __init__(self):
+ self.default_opts = {
+ "category": "*",
+ "fullRegex": 0,
+ "earlyOut": 0,
+ "nameOnly": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = []
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-c", "--category"]:
+ opts["category"] = args[i+1]
+ skip = 1
+ elif x in ["-e", "--earlyout"]:
+ opts["earlyOut"] = 1
+ elif x in ["-f", "--full-regex"]:
+ opts["fullRegex"] = 1
+ elif x in ["-n", "--name-only"]:
+ opts["nameOnly"] = 1
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query.append(x)
+
+ if need_help or query == []:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ if opts["fullRegex"]:
+ q = query
+ else:
+ # Trim trailing and multiple slashes from query
+ for i in range(0, len(query)):
+ query[i] = re.compile('/+').sub('/', query[i])
+ query[i] = query[i].rstrip('/')
+ q = map(lambda x: ((len(x) and x[0] == "/") and "^" or "/")
+ + re.escape(x) + "$", query)
+ print q
+ try:
+ q = "|".join(q)
+ rx = re.compile(q)
+ except:
+ die(2, "The query '" + pp.regexpquery(q) + "' does not appear to be a valid regular expression")
+
+ # Pick out only selected categories
+ cat = opts["category"]
+ filter_fn = None
+ if cat != "*":
+ filter_fn = lambda x: x.find(cat+"/")==0
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for file(s) " + pp.regexpquery(",".join(query)) + " in " + pp.cpv(cat) + "... ]")
+
+ matches = portage.db["/"]["vartree"].dbapi.cpv_all()
+ #matches = gentoolkit.find_all_installed_packages(filter_fn)
+
+ found = 0
+
+ def dumpToPipe(pkg):
+ mysplit = pkg.split("/")
+ cnt = portage.dblink(mysplit[0], mysplit[1], "/", gentoolkit.settings).getcontents()
+ #cnt = pkg.get_contents()
+ if not cnt: return
+ for file in cnt.keys():
+ if rx.search(file) and (opts["category"] == "*" or portage.catpkgsplit(pkg)[0] == opts["category"]):
+ if opts["nameOnly"]:
+ x = portage.catpkgsplit(pkg)
+ print x[0]+"/"+x[1]
+ else:
+ print pkg
+ return
+
+ class DummyExp:
+ pass
+
+ def dumpToScreen(pkg):
+ mysplit = pkg.split("/")
+ cnt = portage.dblink(mysplit[0], mysplit[1], "/", gentoolkit.settings).getcontents()
+ #cnt = pkg.get_contents()
+ if not cnt: return
+ for file in cnt.keys():
+ if rx.search(file) and (opts["category"] == "*" or portage.catpkgsplit(pkg)[0] == opts["category"]):
+ if opts["nameOnly"]:
+ x = portage.catpkgsplit(pkg)
+ s = x[0]+"/"+x[1]
+ else:
+ s = pkg
+ s += " (" + pp.path(fileAsStr(file, cnt[file])) + ")"
+ print_info(0, s)
+ if opts["earlyOut"]:
+ raise DummyExp
+
+ try:
+ if Config["piping"]:
+ map(dumpToPipe, matches)
+ else:
+ map(dumpToScreen, matches)
+ except DummyExp:
+ pass
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.path("files...") + " - list all packages owning " + pp.path("files...")
+ def longHelp(self):
+ return "List all packages owning a particular set of files" + \
+ "\n" + \
+ "\n" + \
+ pp.emph("Note: ") + "Normally, only one package will own a file. If multiple packages own the same file, it usually consitutes a problem, and should be reported.\n" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("belongs") + pp.localoption(" <local-opts> ") + pp.path("filename") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-c, --category cat") + " - only search in category " + \
+ pp.pkgquery("cat") + "\n" + \
+ " " + pp.localoption("-f, --full-regex") + " - supplied query is a regex\n" + \
+ " " + pp.localoption("-e, --earlyout") + " - stop when first match is found\n" + \
+ " " + pp.localoption("-n, --name-only") + " - don't print the version."
+
+class CmdDisplayUSEs(Command):
+ """Advanced report of a package's USE flags"""
+ def __init__(self):
+ self.default_opts = {
+ "allPackages" : False
+ }
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-a", "--all"]:
+ opts["allPackages"] = True
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+
+ (query, opts) = self.parseArgs(args)
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
+
+ if not opts["allPackages"]:
+ matches = gentoolkit.find_installed_packages(query, True)
+ if not matches:
+ matches = gentoolkit.find_packages(query, False)
+ if matches:
+ matches = gentoolkit.sort_package_list(matches)
+ matches = matches[-1:]
+ else:
+ matches = gentoolkit.find_packages(query, True)
+
+ if not matches:
+ die(3, "No matching packages found for \"" + pp.pkgquery(query) + "\"")
+
+
+ useflags = gentoolkit.settings["USE"].split()
+ usedesc = {}
+ uselocaldesc = {}
+
+ # Load global USE flag descriptions
+ try:
+ fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.desc")
+ usedesc = {}
+ for line in fd.readlines():
+ if line[0] == "#":
+ continue
+ fields = line.split(" - ", 1)
+ if len(fields) == 2:
+ usedesc[fields[0].strip()] = fields[1].strip()
+ except IOError:
+ print_warn(5, "Could not load USE flag descriptions from " + ppath(gentoolkit.settings["PORTDIR"] + "/profiles/use.desc"))
+
+ # Load local USE flag descriptions
+ try:
+ fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.local.desc")
+ for line in fd.readlines():
+ if line[0] == "#":
+ continue
+ fields = line.split(" - ", 1)
+ if len(fields) == 2:
+ catpkguse = re.search("(.*):(.*)", fields[0])
+ if catpkguse:
+ if not uselocaldesc.has_key(catpkguse.group(1).strip()):
+ uselocaldesc[catpkguse.group(1).strip()] = {catpkguse.group(2).strip() : fields[1].strip()}
+ else:
+ uselocaldesc[catpkguse.group(1).strip()][catpkguse.group(2).strip()] = fields[1].strip()
+ except IOError:
+ print_warn(5, "Could not load USE flag descriptions from " + path(gentoolkit.settings["PORTDIR"] + "/profiles/use.local.desc"))
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Colour Code : " + pp.useflagon("set") + " " + pp.useflagoff("unset") + " ]")
+ print_info(3, "[ Legend : Left column (U) - USE flags from make.conf ]")
+ print_info(3, "[ : Right column (I) - USE flags packages was installed with ]")
+
+ # Iterate through matches, printing a report for each package
+ matches = gentoolkit.sort_package_list(matches)
+ matches_found = 0
+ for p in matches:
+
+ matches_found += 1
+
+ bestver = p.get_cpv()
+ iuse = p.get_env_var("IUSE")
+
+ if iuse:
+ # Fix Bug #91623 by making sure the list of USE flags is unique
+ # Added sort to make output prettier
+ usevar = unique_array(iuse.split())
+
+ # Remove prefixed +/- from flags in IUSE, Bug #232019
+ for i in range(len(usevar)):
+ if usevar[i][0] == "+" or usevar[i][0] == "-":
+ usevar[i] = usevar[i][1:]
+
+ usevar.sort()
+ else:
+ usevar = []
+
+ inuse = []
+ if p.is_installed():
+ used = p.get_use_flags().split()
+ else:
+ # cosmetic issue here as noninstalled packages don't have "used" flags
+ used = useflags
+
+ # store (inuse, inused, flag, desc)
+ output = []
+
+ for u in usevar:
+ inuse = 0
+ inused = 0
+ try:
+ desc = usedesc[u]
+ except KeyError:
+ try:
+ desc = uselocaldesc[p.get_category() + "/" + p.get_name()][u]
+ except KeyError:
+ desc = ""
+
+ if u in p.get_settings("USE").split():
+ inuse = 1
+ if u in used:
+ inused = 1
+
+ output.append((inuse, inused, u, desc))
+
+ # pretty print
+ if output:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(0, "[ Found these USE variables for " + pp.cpv(bestver) + " ]")
+ print_info(3, pp.emph(" U I"))
+ maxflag_len = 0
+ for inuse, inused, u, desc in output:
+ if len(u) > maxflag_len:
+ maxflag_len = len(u)
+
+ for in_makeconf, in_installed, flag, desc in output:
+ markers = ["-","+"]
+ colour = [pp.useflagoff, pp.useflagon]
+ if Config["piping"]:
+ print_info(0, markers[in_makeconf] + flag)
+ else:
+ if in_makeconf != in_installed:
+ print_info(0, pp.emph(" %s %s" % (markers[in_makeconf], markers[in_installed])), False)
+ else:
+ print_info(0, " %s %s" % (markers[in_makeconf], markers[in_installed]), False)
+
+ print_info(0, " " + colour[in_makeconf](flag.ljust(maxflag_len)), False)
+
+ # print description
+ if desc:
+ print_info(0, " : " + desc)
+ else:
+ print_info(0, " : <unknown>")
+ else:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(1, "[ No USE flags found for " + pp.cpv(p.get_cpv()) + "]")
+
+ if Config["verbosityLevel"] >= 2:
+ if matches_found == 0:
+ s = ""
+ die(3, "No " + s + "packages found for " + pp.pkgquery(query))
+
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - display USE flags for " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ return "Display USE flags for a given package\n" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("uses") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is: \n" + \
+ " " + pp.localoption("-a, --all") + " - include all package versions\n"
+
+
+class CmdDisplayDepGraph(Command):
+ """Display tree graph of dependencies for a query"""
+
+ def __init__(self):
+ self.default_opts = {
+ "displayUSEFlags": 1,
+ "fancyFormatting": 1,
+ "depth": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-U","--no-useflags"]:
+ opts["displayUSEFlags"] = 0
+ elif x in ["-l","--linear"]:
+ opts["fancyFormatting"] = 0
+ elif x[:8] == "--depth=":
+ opts["depth"] = int(x[8:])
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
+
+ matches = gentoolkit.find_packages(query, True)
+
+ for pkg in matches:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, pp.section("* ") + "dependency graph for " + pp.cpv(pkg.get_cpv()))
+ else:
+ print_info(0, pkg.get_cpv() + ":")
+
+ stats = { "maxdepth": 0, "packages": 0 }
+ self._graph(pkg, opts, stats, 0, [], "")
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(0, "[ " + pp.cpv(pkg.get_cpv()) + " stats: packages (" + pp.number(str(stats["packages"])) + \
+ "), max depth (" + pp.number(str(stats["maxdepth"])) + ") ]")
+
+ def _graph(self, pkg, opts, stats, level=0, pkgtbl=[], suffix=""):
+
+ stats["packages"] += 1
+ stats["maxdepth"] = max(stats["maxdepth"], level)
+
+ cpv = pkg.get_cpv()
+
+ pfx = ""
+ if opts["fancyFormatting"]:
+ pfx = level * " " + "`-- "
+ print_info(0, pfx + cpv + suffix)
+
+ pkgtbl.append(cpv)
+
+ pkgdeps = pkg.get_runtime_deps() + pkg.get_compiletime_deps() + pkg.get_postmerge_deps()
+ for x in pkgdeps:
+ suffix = ""
+ cpv = x[2]
+ pkg = gentoolkit.find_best_match(x[0] + cpv)
+ if not pkg:
+ continue
+ if pkg.get_cpv() in pkgtbl:
+ continue
+ if cpv.find("virtual") == 0:
+ suffix += " (" + pp.cpv(cpv) + ")"
+ if len(x[1]) and opts["displayUSEFlags"]:
+ suffix += " [ " + pp.useflagon(' '.join(x[1])) + " ]"
+ if (level < opts["depth"] or opts["depth"] <= 0):
+ pkgtbl = self._graph(pkg, opts, stats, level+1, pkgtbl, suffix)
+ return pkgtbl
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - display a dependency tree for " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ return "Display a dependency tree for a given package\n" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("depgraph") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-U, --no-useflags") + " - do not show USE flags\n" + \
+ " " + pp.localoption("-l, --linear") + " - do not use fancy formatting\n" + \
+ " " + pp.localoption(" --depth=n") + " - limit dependency graph to specified depth"
+
+
+class CmdDisplaySize(Command):
+ """Display disk size consumed by a package"""
+ def __init__(self):
+ self.default_opts = {
+ "regex": 0,
+ "exact": 0,
+ "reportSizeInBytes": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-b","--bytes"]:
+ opts["reportSizeInBytes"] = 1
+ elif x in ["-f", "--full-regex"]:
+ opts["regex"] = 1
+ elif x in ["-e", "--exact-name"]:
+ opts["exact"] = 1
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+# if need_help or query == "":
+ if need_help:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ rev = ""
+ name = ""
+ ver = ""
+ cat = ""
+
+ if query != "":
+ (cat, name, ver, rev) = gentoolkit.split_package_name(query)
+ if rev == "r0": rev = ""
+
+ # replace empty strings with .* and escape regular expression syntax
+ if query != "":
+ if not opts["regex"]:
+ cat, name, ver, rev = [re.sub('^$', ".*", re.escape(x)) for x in cat, name, ver, rev]
+ else:
+ cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
+
+ try:
+ if opts["exact"]:
+ filter_fn = lambda x: re.match(cat+"/"+name, x)
+ else:
+ filter_fn = lambda x: re.match(cat+"/.*"+name, x)
+ matches = gentoolkit.find_all_installed_packages(filter_fn)
+ except:
+ die(2, "The query '" + pp.regexpquery(query) + "' does not appear to be a valid regular expression")
+ else:
+ cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
+ matches = gentoolkit.find_all_installed_packages()
+
+ matches = gentoolkit.sort_package_list(matches)
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(3, "[ Searching for packages matching " + pp.pkgquery(query) + "... ]")
+
+ # If no version supplied, fix regular expression
+ if ver == ".*": ver = "[0-9]+[^-]*"
+
+ if rev != ".*": # revision supplied
+ ver = ver + "-" + rev
+
+ if opts["exact"]:
+ rx = re.compile(cat + "/" + name + "-" + ver)
+ else:
+ rx = re.compile(cat + "/.*" + name + ".*-" + ver)
+
+ for pkg in matches:
+ if rx.search(pkg.get_cpv()):
+ (size, files, uncounted) = pkg.size()
+
+ if Config["piping"]:
+ print_info(0, pkg.get_cpv() + ": total(" + str(files) + "), inaccessible(" + str(uncounted) + \
+ "), size(" + str(size) + ")")
+ else:
+ print_info(0, pp.section("* ") + "size of " + pp.cpv(pkg.get_cpv()))
+ print_info(0, " Total files : ".rjust(25) + pp.number(str(files)))
+
+ if uncounted:
+ print_info(0, " Inaccessible files : ".rjust(25) + pp.number(str(uncounted)))
+
+ sz = "%.2f KiB" % (size/1024.0)
+ if opts["reportSizeInBytes"]:
+ sz = pp.number(str(size)) + " bytes"
+
+ print_info(0, "Total size : ".rjust(25) + pp.number(sz))
+
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - print size of files contained in package " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ return "Print size total size of files contained in a given package" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("size") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is: \n" + \
+ " " + pp.localoption("-b, --bytes") + " - report size in bytes\n" \
+ " " + pp.localoption("-f, --full-regex") + " - query is a regular expression\n" + \
+ " " + pp.localoption("-e, --exact-name") + " - list only those packages that exactly match\n"
+
+class CmdDisplayChanges(Command):
+ """Display changes for pkgQuery"""
+ pass
+
+class CheckException:
+ def __init__(self, s):
+ self.s = s
+
+class CmdCheckIntegrity(Command):
+ """Check timestamps and md5sums for files owned by pkgspec"""
+ def __init__(self):
+ self.default_opts = {
+ "showSummary" : 1,
+ "showGoodFiles" : 0,
+ "showBadFiles" : 1,
+ "checkTimestamp" : 1,
+ "checkMD5sum": 1
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def getMD5sum(self, file):
+ return checksum.perform_md5(file, calc_prelink=1)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ if query == "":
+ matches=gentoolkit.find_all_installed_packages()
+ else:
+ matches = gentoolkit.find_packages(query, True)
+
+ matches = gentoolkit.sort_package_list(matches)
+
+ for pkg in matches:
+ if not pkg.is_installed():
+ continue
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(1, "[ Checking " + pp.cpv(pkg.get_cpv()) + " ]")
+ else:
+ print_info(0, pkg.get_cpv() + ":")
+
+ files = pkg.get_contents()
+ checked_files = 0
+ good_files = 0
+ for file in files.keys():
+ type = files[file][0]
+ try:
+ st = os.lstat(file)
+ if type == "dir":
+ if not os.path.isdir(file):
+ raise CheckException(file + " exists, but is not a directory")
+ elif type == "obj":
+ mtime = files[file][1]
+ md5sum = files[file][2]
+ if opts["checkMD5sum"]:
+ try:
+ actual_checksum = self.getMD5sum(file)
+ except:
+ raise CheckException("Failed to calculate MD5 sum for " + file)
+
+ if self.getMD5sum(file) != md5sum:
+ raise CheckException(file + " has incorrect md5sum")
+ if opts["checkTimestamp"]:
+ if int(st.st_mtime) != int(mtime):
+ raise CheckException(file + (" has wrong mtime (is %d, should be %s)" % (st.st_mtime, mtime)))
+ elif type == "sym":
+ # FIXME: nastry strippery; portage should have this fixed!
+ t = files[file][2]
+ # target = os.path.normpath(t.strip())
+ target = t.strip()
+ if not os.path.islink(file):
+ raise CheckException(file + " exists, but is not a symlink")
+ tgt = os.readlink(file)
+ if tgt != target:
+ raise CheckException(file + " does not point to " + target)
+ elif type == "fif":
+ pass
+ else:
+ pp.print_error(file)
+ pp.print_error(files[file])
+ pp.print_error(type)
+ raise CheckException(file + " has unknown type " + type)
+ good_files += 1
+ except CheckException, (e):
+ print_error(e.s)
+ except OSError:
+ print_error(file + " does not exist")
+ checked_files += 1
+ print_info(0, pp.section(" * ") + pp.number(str(good_files)) + " out of " + pp.number(str(checked_files)) + " files good")
+
+ def shortHelp(self):
+ return pp.pkgquery("pkgspec") + " - check MD5sums and timestamps of " + pp.pkgquery("pkgspec") + "'s files"
+ def longHelp(self):
+ return "Check package's files against recorded MD5 sums and timestamps" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("check") + pp.pkgquery(" pkgspec")
+
+class CmdDisplayStatistics(Command):
+ """Display statistics about installed and uninstalled packages"""
+ pass
+
+class CmdWhich(Command):
+ """Display the filename of the ebuild for a given package
+ that would be used by Portage with the current configuration."""
+ def __init__(self):
+ self.default_opts = {
+ "includeMasked": False
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-m", "--include-masked"]:
+ opts["includeMasked"] = True
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ matches = gentoolkit.find_packages(query, opts["includeMasked"])
+ matches = gentoolkit.sort_package_list(matches)
+
+ if matches:
+ pkg = matches[-1]
+ ebuild_path = pkg.get_ebuild_path()
+ if ebuild_path:
+ print_info(0, os.path.normpath(ebuild_path))
+ else:
+ print_warn("There are no ebuilds to satisfy %s" % pkg.get_name())
+ else:
+ print_error("No masked or unmasked packages found for " + pp.pkgquery(query))
+
+ def shortHelp(self):
+ return pp.pkgquery("pkgspec") + " - print full path to ebuild for package " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ # Not documenting --include-masked at this time, since I'm not sure that it is needed. - FuzzyRay
+ return "Print full path to ebuild for a given package" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("which ") + pp.pkgquery("pkgspec")
+
+class CmdListGLSAs(Command):
+ """List outstanding GLSAs."""
+ pass
+
+class CmdListDepends(Command):
+ """List all packages directly or indirectly depending on pkgQuery"""
+ def __init__(self):
+ self.default_opts = {
+ "onlyDirect": 1,
+ "onlyInstalled": 1,
+ "spacing": 0,
+ "depth": -1
+ }
+ # Used to cache and detect looping
+ self.pkgseen = []
+ self.pkglist = []
+ self.pkgdeps = {}
+ self.deppkgs = {}
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-d", "--direct"]:
+ opts["onlyDirect"] = 1
+ elif x in ["-D", "--indirect"]:
+ opts["onlyDirect"] = 0
+ elif x in ["-a", "--all-packages"]:
+ opts["onlyInstalled"] = 0
+ elif x[:10] == "--spacing=":
+ opts["spacing"] = int(x[10:])
+ elif x[:8] == "--depth=":
+ opts["depth"] = int(x[8:])
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help or query == "":
+ print self.longHelp()
+ sys.exit(-1)
+ return (query, opts)
+
+ def perform(self, args):
+
+ (query, opts) = self.parseArgs(args)
+
+ # We call ourself recursively if --indirect specified. spacing is used to control printing the tree.
+ spacing = opts["spacing"]
+
+ if not Config["piping"] and Config["verbosityLevel"] >= 3 and not spacing:
+ print_info(3, "[ Searching for packages depending on " + pp.pkgquery(query) + "... ]")
+
+ isdepend = gentoolkit.split_package_name(query)
+ isdepends = map((lambda x: x.get_cpv()), gentoolkit.find_packages(query))
+ if not isdepends:
+ print_warn("Warning: No packages found matching %s" % query)
+
+ # Cache the list of packages
+ if not self.pkglist:
+ if opts["onlyInstalled"]:
+ packages = gentoolkit.find_all_installed_packages()
+ else:
+ packages = gentoolkit.find_all_packages()
+
+ packages = gentoolkit.sort_package_list(packages)
+ self.pkglist = packages
+ else:
+ packages = self.pkglist
+
+ for pkg in packages:
+ pkgcpv = pkg.get_cpv()
+ if not pkgcpv in self.pkgdeps:
+ try:
+ deps = pkg.get_runtime_deps() + pkg.get_compiletime_deps() + pkg.get_postmerge_deps()
+ except KeyError, e:
+ # If the ebuild is not found...
+ continue
+ # Remove duplicate deps
+ deps = unique_array(deps)
+ self.pkgdeps[pkgcpv] = deps
+ else:
+ deps = self.pkgdeps[pkgcpv]
+ isdep = 0
+ for dependency in deps:
+ # TODO determine if dependency is enabled by USE flag
+ # Find all packages matching the dependency
+ depstr = dependency[0]+dependency[2]
+ if not depstr in self.deppkgs:
+ try:
+ depcpvs = map((lambda x: x.get_cpv()), gentoolkit.find_packages(depstr))
+ self.deppkgs[depstr] = depcpvs
+ except KeyError, e:
+ print_warn("")
+ print_warn("Package: " + pkgcpv + " contains invalid dependency specification.")
+ print_warn("Portage error: " + str(e))
+ print_warn("")
+ continue
+ else:
+ depcpvs = self.deppkgs[depstr]
+ for x in depcpvs:
+ cpvs=gentoolkit.split_package_name(x)
+ if x in isdepends:
+ cat_match=1
+ name_match=1
+ ver_match=1
+ else:
+ cat_match=0
+ name_match=0
+ ver_match=0
+ # Match Category
+ if not isdepend[0] or cpvs[0] == isdepend[0]:
+ cat_match=1
+ # Match Name
+ if cpvs[1] == isdepend[1]:
+ name_match=1
+ # Match Version
+ if not isdepend[2] or ( cpvs[2] == isdepend[2] and (isdepend[3] \
+ or isdepend[3] == "r0" or cpvs[3] == isdepend[3])):
+ ver_match=1
+
+ if cat_match and ver_match and name_match:
+ if not isdep:
+ if dependency[1]:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print " " * (spacing * 2) + pp.cpv(pkg.get_cpv()),
+ print "(" + \
+ pp.useflag(" & ".join(dependency[1]) + "? ") + \
+ pp.pkgquery(dependency[0]+dependency[2]) + ")"
+ else:
+ print " " * (spacing * 2) + pkg.get_cpv()
+ else:
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print " " * (spacing * 2) + pp.cpv(pkg.get_cpv()),
+ print "(" + pp.pkgquery(dependency[0]+dependency[2]) + ")"
+ else:
+ print " " * (spacing * 2) + pkg.get_cpv()
+ isdep = 1
+ elif not Config["piping"] and Config["verbosityLevel"] >= 3:
+ if dependency[1]:
+ print " "*len(pkg.get_cpv()) + " " * (spacing * 2) + \
+ " (" + pp.useflag("&".join(dependency[1]) + "? ") + \
+ pp.pkgquery(dependency[0]+dependency[2]) + ")"
+ else:
+ print " "*len(pkg.get_cpv()) + " " * (spacing * 2) + " (" + \
+ pp.pkgquery(dependency[0]+dependency[2]) + ")"
+
+ break
+
+ # if --indirect specified, call ourselves again with the dependency
+ # Do not call, if we have already called ourselves.
+ if isdep and not opts["onlyDirect"] and pkg.get_cpv() not in self.pkgseen \
+ and (spacing < opts["depth"] or opts["depth"] == -1):
+ self.pkgseen.append(pkg.get_cpv())
+ self.perform(['=' + pkg.get_cpv(), '--indirect', '--spacing=' + str(int(opts["spacing"]+1))])
+ opts["spacing"] = spacing;
+
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list all direct dependencies matching " + \
+ pp.pkgquery("pkgspec")
+
+ def longHelp(self):
+ return "List all direct dependencies matching a query pattern" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("depends") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-a, --all-packages") + " - search in all available packages (slow)\n" + \
+ " " + pp.localoption("-d, --direct") + " - search direct dependencies only (default)\n" + \
+ " " + pp.localoption("-D, --indirect") + " - search indirect dependencies (VERY slow)\n" + \
+ " " + pp.localoption(" --depth=n") + " - limit indirect dependency tree to specified depth"
+
+
+class CmdListPackages(Command):
+ """List packages satisfying pkgQuery"""
+ def __init__(self):
+ self.default_opts = {
+ "category": "*",
+ "includeInstalled": 1,
+ "includePortTree": 0,
+ "includeOverlayTree": 0,
+ "includeMasked": 1,
+ "regex": 0,
+ "exact": 0,
+ "duplicates": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-i", "--installed"]:
+ opts["includeInstalled"] = 1
+ elif x in ["-I", "--exclude-installed"]:
+ # If -I is the only option, warn
+ # (warning located in perform())
+ opts["includeInstalled"] = 0
+ elif x in ["-p", "--portage-tree"]:
+ opts["includePortTree"] = 1
+ elif x in ["-o", "--overlay-tree"]:
+ opts["includeOverlayTree"] = 1
+ elif x in ["-m", "--exclude-masked"]:
+ opts["includeMasked"] = 0
+ elif x in ["-f", "--full-regex"]:
+ opts["regex"] = 1
+ elif x in ["-e", "--exact-name"]:
+ opts["exact"] = 1
+ elif x in ["-d", "--duplicates"]:
+ opts["duplicates"] = 1
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ # Only search installed packages when listing duplicated packages
+ if opts["duplicates"]:
+ opts["includeInstalled"] = 1
+ opts["includePortTree"] = 0
+ opts["includeOverlayTree"] = 0
+
+ if need_help:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ rev = ""
+ name = ""
+ ver = ""
+ cat = ""
+
+ if query != "":
+ try: (cat, name, ver, rev) = gentoolkit.split_package_name(query)
+ except ValueError, e:
+ if str(e) == 'too many values to unpack':
+ print_error("A pattern to match against package names was expected, ")
+ warn_msg = "but %s has too many slashes ('/') to match any package."
+ die (1, warn_msg % query)
+ else: raise ValueError(e)
+ if rev == "r0": rev = ""
+
+ package_finder = None
+
+ if opts["includeInstalled"]:
+ if opts["includePortTree"] or opts["includeOverlayTree"]:
+ package_finder = gentoolkit.find_all_packages
+ else:
+ package_finder = gentoolkit.find_all_installed_packages
+ elif opts["includePortTree"] or opts["includeOverlayTree"]:
+ package_finder = gentoolkit.find_all_uninstalled_packages
+ else:
+ # -I was specified, and no selection of what packages to list was made
+ print_warn("With -I you must specify one of -i, -p or -o. Assuming -p")
+ opts["includePortTree"] = 1
+ package_finder = gentoolkit.find_all_uninstalled_packages
+
+ filter_fn = None
+
+ if Config["verbosityLevel"] >= 3:
+ scat = "'" + cat + "'"
+ if not cat:
+ scat = "all categories"
+ sname = "package '" + name + "'"
+ if not name:
+ sname = "all packages"
+ if not Config["piping"] and Config["verbosityLevel"] >= 3:
+ print_info(1, "[ Searching for " + pp.cpv(sname) + " in " + pp.cpv(scat) + " among: ]")
+
+ # replace empty strings with .* and escape regular expression syntax
+ if query != "":
+ if not opts["regex"]:
+ cat, name, ver, rev = [re.sub('^$', ".*", re.escape(x)) for x in cat, name, ver, rev]
+ else:
+ cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
+
+ try:
+ if opts["exact"]:
+ filter_fn = lambda x: re.match(cat+"/"+name, x)
+ else:
+ filter_fn = lambda x: re.match(cat+"/.*"+name, x)
+ matches = package_finder(filter_fn)
+ except:
+ die(2, "The query '" + pp.regexpquery(query) + "' does not appear to be a valid regular expression")
+ else:
+ cat, name, ver, rev = [re.sub('^$', ".*", x) for x in cat, name, ver, rev]
+ filter_fn = lambda x: True
+ matches = package_finder(filter_fn)
+
+ # Find duplicate packages
+ if opts["duplicates"]:
+ dups = {}
+ newmatches = []
+ for pkg in matches:
+ mykey = pkg.get_category() + "/" + pkg.get_name()
+ if dups.has_key(mykey):
+ dups[mykey].append(pkg)
+ else:
+ dups[mykey] = [pkg]
+
+ for mykey in dups.keys():
+ if len(dups[mykey]) > 1:
+ newmatches += dups[mykey]
+
+ matches = newmatches
+
+ matches = gentoolkit.sort_package_list(matches)
+
+ # If no version supplied, fix regular expression
+ if ver == ".*": ver = "[0-9]+[^-]*"
+
+ if rev != ".*": # revision supplied
+ ver = ver + "-" + rev
+
+ if opts["exact"]:
+ rx = re.compile(cat + "/" + name + "-" + ver)
+ else:
+ rx = re.compile(cat + "/.*" + name + ".*-" + ver)
+
+ if opts["includeInstalled"]:
+ self._print_installed(matches, rx)
+
+ if opts["includePortTree"]:
+ self._print_porttree(matches, rx)
+
+ if opts["includeOverlayTree"]:
+ self._print_overlay(matches, rx)
+
+ def _get_mask_status(self, pkg):
+ pkgmask = 0
+ if pkg.is_masked():
+ # Uncomment to only have package masked files show an 'M'
+ # maskreasons = portage.getmaskingstatus(pkg.get_cpv())
+ # if "package.mask" in maskreasons:
+ pkgmask = pkgmask + 3
+ keywords = pkg.get_env_var("KEYWORDS").split()
+ if gentoolkit.settings["ARCH"] not in keywords:
+ if "~" + gentoolkit.settings["ARCH"] in keywords:
+ pkgmask = pkgmask + 1
+ elif "-" + gentoolkit.settings["ARCH"] in keywords or "-*" in keywords:
+ pkgmask = pkgmask + 2
+ return pkgmask
+
+ def _generic_print(self, header, exclude, matches, rx, status):
+ if Config["verbosityLevel"] >= 3:
+ print_info(1, header)
+
+ pfxmodes = [ "---", "I--", "-P-", "--O" ]
+ maskmodes = [ " ", " ~", " -", "M ", "M~", "M-" ]
+
+ for pkg in matches:
+ if exclude(pkg):
+ continue
+
+ pkgmask = self._get_mask_status(pkg)
+
+ slot = pkg.get_env_var("SLOT")
+
+ if rx.search(pkg.get_cpv()):
+ if Config["piping"]:
+ print_info(0, pkg.get_cpv())
+ else:
+ print_info(0, "[" + pp.installedflag(pfxmodes[status]) + "] [" + pp.maskflag(maskmodes[pkgmask]) + "] " + pp.cpv(pkg.get_cpv()) + " (" + pp.slot(slot) + ")")
+
+ def _print_overlay(self, matches, rx):
+ self._generic_print(
+ pp.section(" *") + " overlay tree (" + pp.path(gentoolkit.settings["PORTDIR_OVERLAY"]) + ")",
+ lambda x: not x.is_overlay(),
+ matches, rx, 3)
+
+ def _print_porttree(self, matches, rx):
+ self._generic_print(
+ pp.section(" *") + " Portage tree (" + pp.path(gentoolkit.settings["PORTDIR"]) + ")",
+ lambda x: x.is_overlay() or x.is_installed(),
+ matches, rx, 2)
+
+ def _print_installed(self, matches, rx):
+ self._generic_print(
+ pp.section(" *") + " installed packages",
+ lambda x: not x.is_installed(),
+ matches, rx, 1)
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("pkgspec") + " - list all packages matching " + pp.pkgquery("pkgspec")
+ def longHelp(self):
+ return "List all packages matching a query pattern" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("list") + pp.localoption(" <local-opts> ") + pp.pkgquery("pkgspec") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-i, --installed") + " - search installed packages (default)\n" + \
+ " " + pp.localoption("-I, --exclude-installed") + " - do not search installed packages\n" + \
+ " " + pp.localoption("-p, --portage-tree") + " - also search in portage tree (" + gentoolkit.settings["PORTDIR"] + ")\n" + \
+ " " + pp.localoption("-o, --overlay-tree") + " - also search in overlay tree (" + gentoolkit.settings["PORTDIR_OVERLAY"] + ")\n" + \
+ " " + pp.localoption("-f, --full-regex") + " - query is a regular expression\n" + \
+ " " + pp.localoption("-e, --exact-name") + " - list only those packages that exactly match\n" + \
+ " " + pp.localoption("-d, --duplicates") + " - list only installed duplicate packages\n"
+
+class CmdFindUSEs(Command):
+ """Find all packages with a particular USE flag."""
+ def __init__(self):
+ self.default_opts = {
+ "category": "*",
+ "includeInstalled": 1,
+ "includePortTree": 0,
+ "includeOverlayTree": 0,
+ "includeMasked": 1,
+ "regex": 0
+ }
+
+ def parseArgs(self, args):
+
+ query = ""
+ need_help = 0
+ opts = self.default_opts
+ skip = 0
+
+ for i in xrange(len(args)):
+
+ if skip:
+ skip -= 1
+ continue
+ x = args[i]
+
+ if x in ["-h","--help"]:
+ need_help = 1
+ break
+ elif x in ["-i", "--installed"]:
+ opts["includeInstalled"] = 1
+ elif x in ["-I", "--exclude-installed"]:
+ opts["includeInstalled"] = 0
+ elif x in ["-p", "--portage-tree"]:
+ opts["includePortTree"] = 1
+ elif x in ["-o", "--overlay-tree"]:
+ opts["includeOverlayTree"] = 1
+ elif x in ["-m", "--exclude-masked"]:
+ opts["includeMasked"] = 0
+ elif x[0] == "-":
+ print_warn("unknown local option %s, ignoring" % x)
+ else:
+ query = x
+
+ if need_help:
+ print_info(0, self.longHelp())
+ sys.exit(-1)
+
+ return (query, opts)
+
+ def perform(self, args):
+ (query, opts) = self.parseArgs(args)
+
+ rev = ".*"
+ name = ".*"
+ ver = ".*"
+ cat = ".*"
+
+ package_finder = None
+
+ if opts["includeInstalled"] and (opts["includePortTree"] or opts["includeOverlayTree"]):
+ package_finder = gentoolkit.find_all_packages
+ elif opts["includeInstalled"]:
+ package_finder = gentoolkit.find_all_installed_packages
+ elif opts["includePortTree"] or opts["includeOverlayTree"]:
+ package_finder = gentoolkit.find_all_uninstalled_packages
+
+ if not package_finder:
+ die(2,"You must specify one of -i, -p or -o")
+
+ filter_fn = lambda x: True
+
+ if Config["verbosityLevel"] >= 3:
+ scat = "'" + cat + "'"
+ if cat == ".*":
+ scat = "all categories"
+ if not Config["piping"]:
+ print_info(2, "[ Searching for USE flag " + pp.useflag(query) + " in " + pp.cpv(scat) + " among: ]")
+ if opts["includeInstalled"]:
+ print_info(1, pp.section(" *") + " installed packages")
+ if opts["includePortTree"]:
+ print_info(1, pp.section(" *") + " Portage tree (" + pp.path(gentoolkit.settings["PORTDIR"]) + ")")
+ if opts["includeOverlayTree"]:
+ print_info(1, pp.section(" *") + " overlay tree (" + pp.path(gentoolkit.settings["PORTDIR_OVERLAY"]) + ")")
+
+ matches = package_finder(filter_fn)
+
+ rx = re.compile(cat + "/" + name + "-" + ver + "(-" + rev + ")?")
+ pfxmodes = [ "---", "I--", "-P-", "--O" ]
+ maskmodes = [ " ", " ~", " -", "M ", "M~", "M-" ]
+ for pkg in matches:
+ status = 0
+ if pkg.is_installed():
+ status = 1
+ elif pkg.is_overlay():
+ status = 3
+ else:
+ status = 2
+
+ useflags = pkg.get_env_var("IUSE").split()
+
+ if query not in useflags:
+ continue
+
+ # Determining mask status
+ pkgmask = 0
+ if pkg.is_masked():
+ pkgmask = pkgmask + 3
+ keywords = pkg.get_env_var("KEYWORDS").split()
+ if "~"+gentoolkit.settings["ARCH"] in keywords:
+ pkgmask = pkgmask + 1
+ elif "-*" in keywords or "-"+gentoolkit.settings["ARCH"] in keywords:
+ pkgmask = pkgmask + 2
+
+ # Determining SLOT value
+ slot = pkg.get_env_var("SLOT")
+
+ if (status == 1 and opts["includeInstalled"]) or \
+ (status == 2 and opts["includePortTree"]) or \
+ (status == 3 and opts["includeOverlayTree"]):
+ if Config["piping"]:
+ print_info(0, pkg.get_cpv())
+ else:
+ print_info(0, "[" + pp.installedflag(pfxmodes[status]) + "] [" + pp.maskflag(maskmodes[pkgmask]) + "] " + pp.cpv(pkg.get_cpv()) + " (" + pp.slot(slot) + ")")
+
+ def shortHelp(self):
+ return pp.localoption("<local-opts> ") + pp.pkgquery("useflag") + " - list all packages with " + pp.pkgquery("useflag")
+ def longHelp(self):
+ return "List all packages with a particular USE flag" + \
+ "\n" + \
+ "Syntax:\n" + \
+ " " + pp.command("list") + pp.localoption(" <local-opts> ") + pp.pkgquery("useflag") + \
+ "\n" + \
+ pp.localoption("<local-opts>") + " is either of: \n" + \
+ " " + pp.localoption("-i, --installed") + " - search installed packages (default)\n" + \
+ " " + pp.localoption("-I, --exclude-installed") + " - do not search installed packages\n" + \
+ " " + pp.localoption("-p, --portage-tree") + " - also search in portage tree (" + gentoolkit.settings["PORTDIR"] + ")\n" + \
+ " " + pp.localoption("-o, --overlay-tree") + " - also search in overlay tree (" + gentoolkit.settings["PORTDIR_OVERLAY"] + ")\n"
+
+#
+# Command line tokens to commands mapping
+#
+
+Known_commands = {
+ "list" : CmdListPackages(),
+ "files" : CmdListFiles(),
+ "belongs" : CmdListBelongs(),
+ "depends" : CmdListDepends(),
+ "hasuse" : CmdFindUSEs(),
+ "uses" : CmdDisplayUSEs(),
+ "depgraph" : CmdDisplayDepGraph(),
+ "changes" : CmdDisplayChanges(),
+ "size" : CmdDisplaySize(),
+ "check" : CmdCheckIntegrity(),
+ "stats" : CmdDisplayStatistics(),
+ "glsa" : CmdListGLSAs(),
+ "which": CmdWhich()
+ }
+
+# Short command line tokens
+
+Short_commands = {
+ "a" : "glsa",
+ "b" : "belongs",
+ "c" : "changes",
+ "d" : "depends",
+ "f" : "files",
+ "g" : "depgraph",
+ "h" : "hasuse",
+ "k" : "check",
+ "l" : "list",
+ "s" : "size",
+ "t" : "stats",
+ "u" : "uses",
+ "w" : "which"
+}
+
+from gentoolkit import Config
+
+Config = {
+ # Query will include packages installed on the system
+ "installedPackages": 1,
+ # Query will include packages available for installation
+ "uninstalledPackages": 0,
+ # Query will include overlay packages (iff uninstalledPackages==1)
+ "overlayPackages": 1,
+ # Query will include masked packages (iff uninstalledPackages==1)
+ "maskedPackages": 0,
+ # Query will only consider packages in the following categories, empty means all.
+ "categoryFilter": [],
+ # Enable color output (-1 = use Portage setting, 0 = force off, 1 = force on)
+ "color": -1,
+ # Level of detail on the output
+ "verbosityLevel": 3,
+ # Query will display info for multiple SLOTed versions
+ "considerDuplicates": 1,
+ # Are we writing to a pipe?
+ "piping": 0
+}
+
+def printVersion():
+ """Print the version of this tool to the console."""
+ print_info(0, __productname__ + "(" + __version__ + ") - " + \
+ __description__)
+ print_info(0, "Author(s): " + __author__)
+
+def buildReverseMap(m):
+ r = {}
+ for x in m.keys():
+ r[m[x]] = x
+ return r
+
+def printUsage():
+ """Print full usage information for this tool to the console."""
+
+ short_cmds = buildReverseMap(Short_commands);
+
+ print_info(0, pp.emph("Usage: ") + pp.productname(__productname__) + pp.globaloption(" <global-opts> ") + pp.command("command") + pp.localoption(" <local-opts>"))
+ print_info(0, "where " + pp.globaloption("<global-opts>") + " is one of")
+ print_info(0, pp.globaloption(" -q, --quiet") + " - minimal output")
+ print_info(0, pp.globaloption(" -C, --nocolor") + " - turn off colours")
+ print_info(0, pp.globaloption(" -h, --help") + " - this help screen")
+ print_info(0, pp.globaloption(" -V, --version") + " - display version info")
+ print_info(0, pp.globaloption(" -N, --no-pipe") + " - turn off pipe detection")
+
+ print_info(0, "where " + pp.command("command") + "(" + pp.command("short") + ") is one of")
+ keys = Known_commands.keys()
+ keys.sort()
+ for x in keys:
+ print_info(0, " " + pp.command(x) + "(" + pp.command(short_cmds[x]) + ") " + \
+ Known_commands[x].shortHelp())
+ print
+
+def configure():
+ """Set up default configuration.
+ """
+
+ # Guess colour output
+ if (Config["color"] == -1 and \
+ ((not sys.stdout.isatty()) or (gentoolkit.settings["NOCOLOR"] in ["yes","true"]))):
+ pp.output.nocolor()
+
+ # Guess piping output
+ if not sys.stdout.isatty():
+ Config["piping"] = True
+ else:
+ Config["piping"] = False
+
+
+def parseArgs(args):
+ """Parse tool-specific arguments.
+
+ Arguments are on the form equery <tool-specific> [command] <command-specific>
+
+ This function will only parse the <tool-specific> bit.
+ """
+ command = None
+ local_opts = []
+ showhelp = 0
+
+ def expand(x):
+ if x in Short_commands.keys():
+ return Short_commands[x]
+ return x
+
+ for i in xrange(len(args)):
+ x = args[i]
+ if 0:
+ pass
+ elif x in ["-h", "--help"]:
+ showhelp = True
+ elif x in ["-V", "--version"]:
+ printVersion()
+ sys.exit(0)
+ elif x in ["-C", "--nocolor"]:
+ Config["color"] = 0
+ pp.output.nocolor()
+ elif x in ["-N", "--no-pipe"]:
+ Config["piping"] = False
+ elif x in ["-q","--quiet"]:
+ Config["verbosityLevel"] = 0
+ elif expand(x) in Known_commands.keys():
+ command = Known_commands[expand(x)]
+ local_opts.extend(args[i+1:])
+ if showhelp:
+ local_opts.append("--help")
+ break
+ else:
+ print_warn("unknown global option %s, reusing as local option" % x)
+ local_opts.append(x)
+
+ if not command and showhelp:
+ printUsage()
+ sys.exit(0)
+
+ return (command, local_opts)
+
+if __name__ == "__main__":
+ configure()
+ (cmd, local_opts) = parseArgs(sys.argv[1:])
+ if cmd:
+ try:
+ cmd.perform(local_opts)
+ except KeyError, e:
+ if e and e[0].find("Specific key requires an operator") >= 0:
+ print_error("Invalid syntax: missing operator")
+ print_error("If you want only specific versions please use one of")
+ print_error("the following operators as prefix for the package name:")
+ print_error(" > >= = <= <")
+ print_error("Example to only match gcc versions greater or equal 3.2:")
+ print_error(" >=sys-devel/gcc-3.2")
+ print_error("")
+ print_error("Note: The symbols > and < are used for redirection in the shell")
+ print_error("and must be quoted if either one is used.")
+
+ else:
+ print_error("Internal portage error, terminating")
+ if len(e[0]):
+ print_error(str(e))
+ sys.exit(2)
+ except ValueError, e:
+ if isinstance(e[0], list):
+ print_error("Ambiguous package name " + pp.emph("\"" + local_opts[0] + "\""))
+ print_error("Please use one of the following long names:")
+ for p in e[0]:
+ print_error(" " + str(p))
+ else:
+ print_error("Internal portage error, terminating")
+ if len(e[0]):
+ print_error(str(e[0]))
+ sys.exit(2)
+ except KeyboardInterrupt:
+ print_info(0, "Interrupted by user, aborting.")
+ else:
+ print_error("No command or unknown command given")
+ printUsage()
+
diff --git a/src/equery/equery.1 b/src/equery/equery.1
new file mode 100644
index 0000000..27b8078
--- /dev/null
+++ b/src/equery/equery.1
@@ -0,0 +1,278 @@
+.TH "equery" "1" "Oct 2005" "gentoolkit" ""
+.SH "NAME"
+equery \- Gentoo: Package Query Tool
+.SH "SYNOPSIS"
+.B equery
+.I [global\-opts] command [local\-opts]
+.PP
+
+.SH "DESCRIPTION"
+equery is a flexible utility which may display various information about
+packages, such as the files they own, their USE flags, the md5sum
+of each file owned by a given package, and many other things.
+
+.SH "OPTIONS"
+The 'command' is the only mandatory option to equery. Most commands require
+a 'pkgspec' option, which is described by <cat/>packagename<\-version>;
+namely, the package name is mandatory, while the category and version are
+optional.
+
+[global\-opts] may be one of:
+
+.B \-q, \-\-quiet
+causes minimal output to be emitted
+.PP
+.B \-C, \-\-nocolor
+turns off colours
+.PP
+.B \-h, \-\-help
+displays a help summary
+.PP
+.B \-V, \-\-version
+displays the equery version
+.PP
+.B \-N, \-\-no\-pipe
+turns off pipe detection
+.PP
+
+Only one command will actually be run, at most. The possible commands are:
+.TP
+.B belongs <local\-opts> file
+This command lists all packages owning the specified file.
+.br
+Note: Normally, only one package will own a file. If multiple packages own the
+same file, it usually consitutes a problem, and should be reported (http://bugs.gentoo.org).
+.br
+.IP
+<local\-opts> is either or both of:
+.br
+.B \-c, \-\-category cat
+only search in category cat
+.br
+.B \-f, \-\-full\-regex
+supplied query is a regex
+.br
+.B \-e, \-\-earlyout
+stop when first match found
+
+.PP
+.B check pkgspec
+This command checks the files of the specified package against recorded MD5
+sums and timestamps.
+.PP
+.TP
+.B depends <local\-opts> pkgspec
+This command displays all dependencies matching pkgspec.
+.br
+<local\-opts> is either or both of:
+.br
+.B \-a, \-\-all\-packages
+search in all available packages (slow)
+.br
+.B \-d, \-\-direct
+search direct dependencies only (default)
+.br
+.B \-D, \-\-indirect
+search indirect dependencies (very slow)
+.br
+.B \-\-depth=n
+Limit depth of indirect dependency tree to n levels. Setting \-\-depth=0 is the same as not specifing \-\-indirect.
+.PP
+.TP
+.B depgraph <local\-opts> pkgspec
+This command display a dependency tree for pkgspec, by default indented to reflect
+how dependancies relate to each other.
+.br
+.IP
+<local\-opts> is either or both of:
+.br
+.B \-U, \-\-no\-useflags
+do not show USE flags.
+.br
+.B \-l, \-\-linear
+do not use fancy formatting
+.br
+.B \-\-depth=n
+Limit depth of dependency graph to n levels.
+.PP
+.TP
+.B files <local\-opts> pkgspec
+This lists files owned by a particular package, optionally with extra
+information specified by <local\-opts>
+.br
+
+<local\-opts> is any combination of:
+.br
+.B \-\-timestamp
+output the timestamp of each file
+.br
+.B \-\-md5sum
+output the md5sum of each file
+.br
+.B \-\-type
+output the type of each file
+.br
+.B \-\-tree
+display results in a tree (turns off all other options)
+.br
+.B \-\-filter=<rules>
+filter output based on files type or path
+.br
+.B \t<rules>
+is a comma separated list of filtering rules. Available rules are:
+.br
+.B \t\tdir\
+regular directories
+.br
+.B \t\tobj\
+regular files
+.br
+.B \t\tsym\
+symbolic links
+.br
+.B \t\tdev\
+device nodes
+.br
+.B \t\tfifo
+named pipes
+.br
+.B \t\tpath
+shortest paths where some files where installed
+.br
+.B \t\tconf
+configuration files (based on $CONFIG_PROTECT)
+.br
+.B \t\tcmd\
+user commands (based on $PATH)
+.br
+.B \t\tdoc\
+documentation files (from /usr/share/doc)
+.br
+.B \t\tman\
+manpages (from /usr/share/man)
+.br
+.B \t\tinfo
+info pages (from /usr/share/info)
+.PP
+.TP
+.B hasuse <local\-opts> useflag
+This command lists packages matching a particular USE flag in a user\-specified combination
+of installed packages, packages which are not installed, the portage tree, and
+the portage overlay tree.
+
+<local\-opts> must not include only \-I;
+if \-I is used, \-p and/or \-o must be also be present. By default, only installed
+packages are searched. \-o searches only the overlay tree [and possibly
+installed packages],
+.I not
+the main portage tree.
+
+.B \-i, \-\-installed
+search installed packages (default)
+.br
+.B \-I, \-\-exclude\-installed
+do not search installed packages
+.br
+.B \-p, \-\-portage\-tree
+also search in portage tree (/usr/portage)
+.br
+.B \-o, \-\-overlay\-tree
+also search in overlay tree (/usr/local/portage)
+.PP
+.TP
+.B list <local\-opts> pkgspec
+This command lists packages matching pkgspec in a user\-specified combination
+of installed packages, packages which are not installed, the portage tree, and
+the portage overlay tree. By default the list command searches for partial name matches.
+
+<local\-opts> \-I cannot be used by itself;
+if \-I is used, \-p and/or \-o must be also be present. By default, only installed
+packages are searched. \-o searches only the overlay tree [and possibly
+installed packages],
+\fInot\fR the main portage tree.
+
+.B \-i, \-\-installed
+search installed packages (default)
+.br
+.B \-I, \-\-exclude\-installed
+do not search installed packages
+.br
+.B \-p, \-\-portage\-tree
+also search in portage tree (/usr/portage)
+.br
+.B \-o, \-\-overlay\-tree
+also search in overlay tree (/usr/local/portage)
+.br
+.B \-f, \-\-full\-regex
+query is a regular expression
+.br
+.B \-e, \-\-exact\-name
+list only those packages that exactly match
+.br
+.B \-d, \-\-duplicates
+only list installed duplicate packages
+.br
+
+\fBOutput:\fR
+
+.br
+The list command searches packages for the name given. If found, the following info will be displayed: the package location between the first square brackets (I for Installed packages, P for Portage, O for Overlay), the possible masks between the second (~ by keyword, - by arch or M hard masked), then the category and complete name and last of all, the slot in which the package is stored.
+
+\fBExamples:\fR
+
+equery list zilla \- list all installed versions of packages containing the string 'zilla'
+
+equery list \-\-exact\-name x11\-libs/gtk+ \- list all installed versions of x11\-libs/gtk+
+
+equery list \-\-full\-regex '(mozilla\-firefox|mozilla\-thunderbird)' \- list all installed versions of mozilla\-firefox and mozilla\-thunderbird
+
+equery list \-\-duplicates \- list all installed slotted packages
+.PP
+.TP
+.B size <local\-opts> pkgspec
+This command outputs the number of files in the specified package, as well as
+their total size in an appropriate unit.
+
+The possible values for <local\-opts>, if specified, are:
+.br
+.B \-b, \-\-bytes
+report size in bytes
+.br
+.B \-f, \-\-full\-regex
+query is a regular expression
+.br
+.B \-e, \-\-exact\-name
+list only those packages that exactly match
+.PP
+.TP
+.B uses <local\-opts> pkgspec
+display USE flags for pkgspec.
+
+The only possible value for <local\-opts>, if specified, is:
+.br
+.B \-a, \-\-all
+include all package versions
+.PP
+.B which pkgspec
+print full path to ebuild for package pkgspec
+.PP
+
+.SH "Unimplemented Options"
+.PP
+.B changes
+.PP
+.B glsa \fR \- use glsa\-check for the time being.
+.PP
+.B stats
+
+
+
+.SH "BUGS"
+Many options aren't implemented. Command\-line parsing could use some work.
+.br
+Submit bug reports to http://bugs.gentoo.org
+.SH "AUTHORS"
+equery, original man page: Karl Trygve Kalleberg <karltk@gentoo.org>, 2003.
+.br
+Massive man page updates: Katerina Barone\-Adesi <katerinab@gmail.com>, 2004.
+
diff --git a/src/equery/tests/common-functions.sh b/src/equery/tests/common-functions.sh
new file mode 100644
index 0000000..f065a0a
--- /dev/null
+++ b/src/equery/tests/common-functions.sh
@@ -0,0 +1,52 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+function tempfilename() {
+ fn=$(date "+%s")
+ if [ ! -f ${fn}.tmp ] ; then
+ echo ${fn}.tmp
+ fi
+}
+
+function report_pass() {
+ printf "%-40s - passed\n" ${1}
+}
+
+function report_failure() {
+ printf "%-40s - FAILED!\n" ${1}
+}
+
+function assert_samefile() {
+ diff $2 $3 && report_pass $1 || report_failure $1
+}
+
+function assert_eq() {
+ if [ $2 -eq $3 ] ; then
+ report_pass $1
+ else
+ printf "FAIL: $2 ! -eq $3\n"
+ report_failure $1
+ fi
+}
+
+function assert_ge() {
+ if [ $2 -ge $3 ] ; then
+ report_pass $1
+ else
+ printf "FAIL: $2 ! -ge $3\n"
+ report_failure $1
+ fi
+}
+
+function assert_exists() {
+ if [ -f $2 ] ; then
+ report_pass $1
+ else
+ printf "FAIL: $2 does not exist\n"
+ report_failure $1
+ fi
+} \ No newline at end of file
diff --git a/src/equery/tests/run-all-tests.sh b/src/equery/tests/run-all-tests.sh
new file mode 100755
index 0000000..4fe2dfe
--- /dev/null
+++ b/src/equery/tests/run-all-tests.sh
@@ -0,0 +1,12 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+for x in belongs check depends depgraph files help list size uses which ; do
+ ./test-${x}.sh
+done
diff --git a/src/equery/tests/test-belongs-help.out b/src/equery/tests/test-belongs-help.out
new file mode 100644
index 0000000..0d2f583
--- /dev/null
+++ b/src/equery/tests/test-belongs-help.out
@@ -0,0 +1,11 @@
+List all packages owning a particular set of files
+
+Note: Normally, only one package will own a file. If multiple packages own the same file, it usually consitutes a problem, and should be reported.
+
+Syntax:
+ belongs <local-opts> filename
+<local-opts> is either of:
+ -c, --category cat - only search in category cat
+ -f, --full-regex - supplied query is a regex
+ -e, --earlyout - stop when first match is found
+
diff --git a/src/equery/tests/test-belongs.sh b/src/equery/tests/test-belongs.sh
new file mode 100755
index 0000000..bd0eac4
--- /dev/null
+++ b/src/equery/tests/test-belongs.sh
@@ -0,0 +1,24 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_belongs() {
+ equery belongs $(which gcc) > ${tmpfile}
+
+ x=$(grep "gcc-config" ${tmpfile} | wc -l)
+
+ assert_eq ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+test_belongs
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-changes-help.out b/src/equery/tests/test-changes-help.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/equery/tests/test-changes-help.out
diff --git a/src/equery/tests/test-check-help.out b/src/equery/tests/test-check-help.out
new file mode 100644
index 0000000..1e6afcf
--- /dev/null
+++ b/src/equery/tests/test-check-help.out
@@ -0,0 +1,3 @@
+Check package's files against recorded MD5 sums and timestamps
+Syntax:
+ size pkgspec
diff --git a/src/equery/tests/test-check.sh b/src/equery/tests/test-check.sh
new file mode 100755
index 0000000..803299f
--- /dev/null
+++ b/src/equery/tests/test-check.sh
@@ -0,0 +1,39 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_check() {
+ equery check gcc > ${tmpfile}
+
+ x=$(grep "sys-devel/gcc" ${tmpfile} | wc -l)
+
+ assert_ge ${FUNCNAME} ${x} 1
+
+ x=$(egrep "[0-9]+ out of [0-9]+" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} ${x} 1
+}
+
+test_check_permissions() {
+ equery check sudo > ${tmpfile}
+
+ x=$(grep "app-admin/sudo" ${tmpfile} | wc -l)
+
+ assert_ge ${FUNCNAME} ${x} 1
+
+ x=$(egrep "[0-9]+ out of [0-9]+" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+test_check
+test_check_permissions
+
+rm -f ${tmpfile}
diff --git a/src/equery/tests/test-depends-help.out b/src/equery/tests/test-depends-help.out
new file mode 100644
index 0000000..356cd53
--- /dev/null
+++ b/src/equery/tests/test-depends-help.out
@@ -0,0 +1,8 @@
+List all direct dependencies matching a query pattern
+Syntax:
+ depends <local-opts> pkgspec
+<local-opts> is either of:
+ -d, --direct - search direct dependencies only (default)
+ -D, --indirect - search indirect dependencies (VERY slow)
+ -i, --only-installed - search installed in installed packages only
+
diff --git a/src/equery/tests/test-depends.sh b/src/equery/tests/test-depends.sh
new file mode 100755
index 0000000..e46d614
--- /dev/null
+++ b/src/equery/tests/test-depends.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_depends() {
+# equery skel gcc > ${tmpfile}
+
+# x=$(grep "app-shells/bash" ${tmpfile} | wc -l)
+ true
+# assert_eq ${FUNCNAME} ${x} 1
+
+# x=$(grep "virtual/libc" ${tmpfile} | wc -l)
+# assert_eq ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+#test_skel
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-depgraph-help.out b/src/equery/tests/test-depgraph-help.out
new file mode 100644
index 0000000..5b9fd22
--- /dev/null
+++ b/src/equery/tests/test-depgraph-help.out
@@ -0,0 +1,7 @@
+Display a dependency tree for a given package
+
+Syntax:
+ depgraph <local-opts> pkgspec
+<local-opts> is either of:
+ -U, --no-useflags - do not show USE flags
+ -l, --linear - do not use fancy formatting
diff --git a/src/equery/tests/test-depgraph.sh b/src/equery/tests/test-depgraph.sh
new file mode 100755
index 0000000..016bb37
--- /dev/null
+++ b/src/equery/tests/test-depgraph.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_depgraph() {
+ equery depgraph gcc > ${tmpfile}
+
+ x=$(grep "app-shells/bash" ${tmpfile} | wc -l)
+
+ assert_eq ${FUNCNAME} ${x} 1
+
+ x=$(grep "virtual/libc" ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+test_depgraph
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-files-help.out b/src/equery/tests/test-files-help.out
new file mode 100644
index 0000000..846151f
--- /dev/null
+++ b/src/equery/tests/test-files-help.out
@@ -0,0 +1,11 @@
+List files owned by a particular package
+
+Syntax:
+ files <local-opts> <cat/>packagename<-version>
+
+Note: category and version parts are optional.
+
+<local-opts> is either of:
+ --timestamp - append timestamp
+ --md5sum - append md5sum
+ --type - prepend file type
diff --git a/src/equery/tests/test-files.sh b/src/equery/tests/test-files.sh
new file mode 100755
index 0000000..ad0a5ea
--- /dev/null
+++ b/src/equery/tests/test-files.sh
@@ -0,0 +1,61 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+strip_versioned_files() {
+ grep -v "/usr/share/doc"
+}
+
+test_files() {
+ equery files bash > ${tmpfile}
+
+ x=$(grep man ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 5
+
+ x=$(cat ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 25
+}
+
+test_files_timestamp() {
+ equery files --timestamp bash > ${tmpfile}
+
+ x=$(grep "/bin/bash .*....-..-.. ..:..:.." ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} $x 1
+}
+
+test_files_md5sum() {
+ equery files --md5sum bash > ${tmpfile}
+
+ x=$(egrep "/bin/bash .*[0-9a-z]{30}" ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} $x 1
+}
+
+test_files_type() {
+
+ equery files --type bash > ${tmpfile}
+
+ x=$(grep "file.*/bin/bash$" ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} $x 1
+
+ x=$(grep "symlink.*/bin/rbash" ${tmpfile} | wc -l)
+ assert_eq ${FUNCNAME} $x 1
+
+ x=$(grep "dir.*/usr/share/man" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 1
+}
+
+# Run tests
+
+test_files
+test_files_timestamp
+test_files_md5sum
+test_files_type
+
+rm ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-glsa-help.out b/src/equery/tests/test-glsa-help.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/equery/tests/test-glsa-help.out
diff --git a/src/equery/tests/test-hasuses-help.out b/src/equery/tests/test-hasuses-help.out
new file mode 100644
index 0000000..1a05645
--- /dev/null
+++ b/src/equery/tests/test-hasuses-help.out
@@ -0,0 +1,9 @@
+List all packages with a particular USE flag
+Syntax:
+ list <local-opts> useflag
+<local-opts> is either of:
+ -i, --installed - search installed packages (default)
+ -I, --exclude-installed - do not search installed packages
+ -p, --portage-tree - also search in portage tree (/usr/portage)
+ -o, --overlay-tree - also search in overlay tree (/usr/local/portage /usr/local/overlays/gentoo-boblycat)
+
diff --git a/src/equery/tests/test-help.out b/src/equery/tests/test-help.out
new file mode 100644
index 0000000..26517f4
--- /dev/null
+++ b/src/equery/tests/test-help.out
@@ -0,0 +1,21 @@
+Usage: equery <global-opts> command <local-opts>
+where <global-opts> is one of
+ -q, --quiet - minimal output
+ -C, --nocolor - turn off colours
+ -h, --help - this help screen
+ -V, --version - display version info
+where command(short) is one of
+ belongs(b) <local-opts> files... - list all packages owning files...
+ changes(c) - not implemented yet
+ check(k) pkgspec - check MD5sums and timestamps of pkgspec's files
+ depends(d) <local-opts> pkgspec - list all direct dependencies matching pkgspec
+ depgraph(g) <local-opts> pkgspec - display a dependency tree for pkgspec
+ files(f) <local-opts> pkgspec - list files owned by pkgspec
+ glsa(a) - not implemented yet
+ hasuses(h) <local-opts> pkgspec - list all packages with useflag
+ list(l) <local-opts> pkgspec - list all packages matching pkgspec
+ size(s) <local-opts> pkgspec - print size of files contained in package pkgspec
+ stats(t) - not implemented yet
+ uses(u) <local-opts> pkgspec - display USE flags for pkgspec
+ which(w) pkgspec - print full path to ebuild for package pkgspec
+
diff --git a/src/equery/tests/test-help.sh b/src/equery/tests/test-help.sh
new file mode 100755
index 0000000..618aac1
--- /dev/null
+++ b/src/equery/tests/test-help.sh
@@ -0,0 +1,105 @@
+#! /bin/sh
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_equery_help() {
+ equery --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-help.out
+
+}
+
+test_belongs_help() {
+ equery belongs --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-belongs-help.out
+}
+
+test_changes_help() {
+ equery changes --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-changes-help.out
+}
+
+test_check_help() {
+ equery check --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-check-help.out
+}
+
+test_depends_help() {
+ equery depends --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-depends-help.out
+}
+
+test_depgraph_help() {
+ equery depgraph --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-depgraph-help.out
+}
+
+test_files_help() {
+ equery files --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-files-help.out
+}
+
+test_glsa_help() {
+ equery glsa --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-glsa-help.out
+}
+
+test_hasuses_help() {
+ equery hasuses --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-hasuses-help.out
+}
+
+test_list_help() {
+ equery list --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-list-help.out
+}
+
+test_size_help() {
+ equery size --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-size-help.out
+}
+
+test_stats_help() {
+ equery stats --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-stats-help.out
+}
+
+test_uses_help() {
+ equery uses --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-uses-help.out
+}
+
+test_which_help() {
+ equery which --help > ${tmpfile}
+ assert_samefile ${FUNCNAME} ${tmpfile} test-which-help.out
+}
+
+
+# run tests
+
+if [ "`hostname`" != "sky" ] ; then
+ echo "Testing framework is beta and machine dependent; some tests will fail!"
+fi
+
+test_equery_help
+test_belongs_help
+test_check_help
+test_changes_help
+test_depends_help
+test_depgraph_help
+test_files_help
+test_glsa_help
+test_hasuses_help
+test_list_help
+test_size_help
+test_stats_help
+test_uses_help
+test_which_help
+
+rm -f *.tmp \ No newline at end of file
diff --git a/src/equery/tests/test-list-help.out b/src/equery/tests/test-list-help.out
new file mode 100644
index 0000000..82d1fb0
--- /dev/null
+++ b/src/equery/tests/test-list-help.out
@@ -0,0 +1,9 @@
+List all packages matching a query pattern
+Syntax:
+ list <local-opts> pkgspec
+<local-opts> is either of:
+ -i, --installed - search installed packages (default)
+ -I, --exclude-installed - do not search installed packages
+ -p, --portage-tree - also search in portage tree (/usr/portage)
+ -o, --overlay-tree - also search in overlay tree (/usr/local/portage /usr/local/overlays/gentoo-boblycat)
+
diff --git a/src/equery/tests/test-list.sh b/src/equery/tests/test-list.sh
new file mode 100755
index 0000000..a43048b
--- /dev/null
+++ b/src/equery/tests/test-list.sh
@@ -0,0 +1,40 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_list() {
+ equery list > ${tmpfile}
+
+# should test tty output as well
+# pkgs=$(cat ${tmpfile} | wc -l)
+# x=$(grep "[I--]" ${tmpfile} | wc -l)
+# assert_eq ${FUNCNAME} ${pkgs} ${x}
+
+ x=$(grep "app-shells/bash" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 1
+}
+
+test_list_installed() {
+ test_list
+}
+
+test_list_portage_tree() {
+ equery list -I -p > ${tmpfile}
+}
+
+test_list_overlay_tree() {
+ equery list -I -o > ${tmpfile}
+}
+
+# Run tests
+
+test_list
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-size-help.out b/src/equery/tests/test-size-help.out
new file mode 100644
index 0000000..c0c63a4
--- /dev/null
+++ b/src/equery/tests/test-size-help.out
@@ -0,0 +1,6 @@
+Print size total size of files contained in a given package
+Syntax:
+ size <local-opts> pkgspec
+<local-opts> is either of:
+ -b, --bytes - report size in bytes
+
diff --git a/src/equery/tests/test-size.sh b/src/equery/tests/test-size.sh
new file mode 100755
index 0000000..126a5db
--- /dev/null
+++ b/src/equery/tests/test-size.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_size() {
+ equery size gcc > ${tmpfile}
+
+ x=$(grep "sys-devel/gcc" ${tmpfile} | wc -l)
+
+ assert_ge ${FUNCNAME} ${x} 1
+
+ x=$(egrep "size\([0-9]+\)" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} ${x} 1
+}
+
+# Run tests
+
+test_size
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-stats-help.out b/src/equery/tests/test-stats-help.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/equery/tests/test-stats-help.out
diff --git a/src/equery/tests/test-uses-help.out b/src/equery/tests/test-uses-help.out
new file mode 100644
index 0000000..d350e00
--- /dev/null
+++ b/src/equery/tests/test-uses-help.out
@@ -0,0 +1,7 @@
+Display USE flags for a given package
+
+Syntax:
+ uses <local-opts> pkgspec
+<local-opts> is either of:
+ -a, --all - include non-installed packages
+
diff --git a/src/equery/tests/test-uses.sh b/src/equery/tests/test-uses.sh
new file mode 100755
index 0000000..d694483
--- /dev/null
+++ b/src/equery/tests/test-uses.sh
@@ -0,0 +1,39 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_uses() {
+ equery uses gcc > ${tmpfile}
+
+ x=$(grep "static" ${tmpfile} | wc -l)
+
+ assert_eq ${FUNCNAME} ${x} 1
+
+ x=$(cat ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 7
+}
+
+test_uses_all() {
+ equery uses -a uclibc > ${tmpfile}
+
+ x=$(grep "static" ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} ${x} 1
+
+ x=$(cat ${tmpfile} | wc -l)
+ assert_ge ${FUNCNAME} $x 5
+
+}
+
+# Run tests
+
+test_uses
+test_uses_all
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/equery/tests/test-which-help.out b/src/equery/tests/test-which-help.out
new file mode 100644
index 0000000..8bf337e
--- /dev/null
+++ b/src/equery/tests/test-which-help.out
@@ -0,0 +1,3 @@
+Print full path to ebuild for a given package
+Syntax:
+ size pkgspec
diff --git a/src/equery/tests/test-which.sh b/src/equery/tests/test-which.sh
new file mode 100755
index 0000000..491868f
--- /dev/null
+++ b/src/equery/tests/test-which.sh
@@ -0,0 +1,22 @@
+#! /bin/bash
+#
+# Copyright (c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright (c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+
+. common-functions.sh
+
+tmpfile=$(tempfilename)
+
+test_which() {
+ file=$(equery which gcc)
+
+ assert_exists ${FUNCNAME} ${file}
+}
+
+# Run tests
+
+test_which
+
+rm -f ${tmpfile} \ No newline at end of file
diff --git a/src/eread/AUTHORS b/src/eread/AUTHORS
new file mode 100644
index 0000000..68064ce
--- /dev/null
+++ b/src/eread/AUTHORS
@@ -0,0 +1,2 @@
+Author: Donnie Berkholz <dberkholz@gentoo.org>
+Updated by: Uwe Klosa <uwe.klosa@gmail.com>
diff --git a/src/eread/Makefile b/src/eread/Makefile
new file mode 100644
index 0000000..1d9b284
--- /dev/null
+++ b/src/eread/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2006 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+ echo "ELY (n.) The first, tiniest inkling you get that something, somewhere, has gone terribly wrong."
+
+dist:
+ mkdir -p ../../$(distdir)/src/eread
+ cp AUTHORS Makefile eread eread.1 ../../$(distdir)/src/eread/
+
+install:
+
+ install -m 0755 eread $(bindir)/
+ install -d $(docdir)/eread
+ install -m 0644 AUTHORS $(docdir)/eread/
+ install -m 0644 eread.1 $(mandir)/
diff --git a/src/eread/eread b/src/eread/eread
new file mode 100755
index 0000000..c6d4de1
--- /dev/null
+++ b/src/eread/eread
@@ -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/src/eread/eread.1 b/src/eread/eread.1
new file mode 100644
index 0000000..5e18214
--- /dev/null
+++ b/src/eread/eread.1
@@ -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/src/etc-update/AUTHORS b/src/etc-update/AUTHORS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/etc-update/AUTHORS
diff --git a/src/etc-update/ChangeLog b/src/etc-update/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/etc-update/ChangeLog
diff --git a/src/etc-update/Makefile b/src/etc-update/Makefile
new file mode 100644
index 0000000..95838ad
--- /dev/null
+++ b/src/etc-update/Makefile
@@ -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/src/etc-update/README b/src/etc-update/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/etc-update/README
diff --git a/src/etc-update/etc-update b/src/etc-update/etc-update
new file mode 100755
index 0000000..f566dff
--- /dev/null
+++ b/src/etc-update/etc-update
@@ -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/src/etc-update/etc-update.1 b/src/etc-update/etc-update.1
new file mode 100644
index 0000000..53477d8
--- /dev/null
+++ b/src/etc-update/etc-update.1
@@ -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/src/etcat/AUTHORS b/src/etcat/AUTHORS
new file mode 100644
index 0000000..5da0b07
--- /dev/null
+++ b/src/etcat/AUTHORS
@@ -0,0 +1,5 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Authors:
+Alastair Tse <liquidx@gentoo.org> (original author)
diff --git a/src/etcat/ChangeLog b/src/etcat/ChangeLog
new file mode 100644
index 0000000..bdadcb6
--- /dev/null
+++ b/src/etcat/ChangeLog
@@ -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/src/etcat/Makefile b/src/etcat/Makefile
new file mode 100644
index 0000000..2281646
--- /dev/null
+++ b/src/etcat/Makefile
@@ -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/src/etcat/README b/src/etcat/README
new file mode 100644
index 0000000..50bd2f3
--- /dev/null
+++ b/src/etcat/README
@@ -0,0 +1,2 @@
+For more information, this tool was originally hosted on:
+http://www.liquidx.net/projects/etcat/
diff --git a/src/etcat/TODO b/src/etcat/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/etcat/TODO
diff --git a/src/etcat/etcat b/src/etcat/etcat
new file mode 100755
index 0000000..5137683
--- /dev/null
+++ b/src/etcat/etcat
@@ -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/src/etcat/etcat.1 b/src/etcat/etcat.1
new file mode 100644
index 0000000..6704f3b
--- /dev/null
+++ b/src/etcat/etcat.1
@@ -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/src/euse/AUTHORS b/src/euse/AUTHORS
new file mode 100644
index 0000000..12e6db7
--- /dev/null
+++ b/src/euse/AUTHORS
@@ -0,0 +1,2 @@
+* original perl version: Arun Bhanu <codebear@gentoo.org>
+* new bash version: Marius Mauch <genone@gentoo.org>
diff --git a/src/euse/ChangeLog b/src/euse/ChangeLog
new file mode 100644
index 0000000..cb50dbb
--- /dev/null
+++ b/src/euse/ChangeLog
@@ -0,0 +1,9 @@
+
+2004-01-07 Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Added Makefile
+ * Updated from app-portage/gentoolkit
+ * Reformatted ChangeLog
+
+2003-05-09 Arun Bhanu <codebear@gentoo.org>
+ * Initial commit to gentoolkit.
+
diff --git a/src/euse/Makefile b/src/euse/Makefile
new file mode 100644
index 0000000..d1ad804
--- /dev/null
+++ b/src/euse/Makefile
@@ -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 "PIMPERNE (n.) One of those rubber nodules found on the underneath side of a lavatory seat."
+
+dist:
+ mkdir -p ../../$(distdir)/src/euse/
+ cp Makefile AUTHORS ChangeLog euse euse.1 ../../$(distdir)/src/euse/
+
+install:
+ install -m 0755 euse $(bindir)/
+ install -d $(docdir)/euse
+ install -m 0644 AUTHORS ChangeLog $(docdir)/euse/
+ install -m 0644 euse.1 $(mandir)/
diff --git a/src/euse/euse b/src/euse/euse
new file mode 100755
index 0000000..ed70932
--- /dev/null
+++ b/src/euse/euse
@@ -0,0 +1,544 @@
+#!/bin/bash
+
+# $Header$
+
+# bash replacement for the original euse by Arun Bhanu
+# Author: Marius Mauch <genone@gentoo.org>
+# Version: 0.2
+# Licensed under the GPL v2
+
+PROGRAM_NAME=euse
+PROGRAM_VERSION=0.1
+
+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 | --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
+
+ 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"
+ [ ! -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} v${PROGRAM_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} v${PROGRAM_VERSION}
+Written by Marius Mauch
+
+Copyright (C) 2004-2008 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 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) }'))
+ 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
+ get_flagstatus "${flag}"
+ 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
+}
+
+# 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="$?"
+ [ "${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/src/euse/euse.1 b/src/euse/euse.1
new file mode 100644
index 0000000..b5148fd
--- /dev/null
+++ b/src/euse/euse.1
@@ -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/src/eviewcvs/AUTHORS b/src/eviewcvs/AUTHORS
new file mode 100644
index 0000000..36d5bfd
--- /dev/null
+++ b/src/eviewcvs/AUTHORS
@@ -0,0 +1 @@
+Aron Griffis <agriffis@gentoo.org>
diff --git a/src/eviewcvs/Makefile b/src/eviewcvs/Makefile
new file mode 100644
index 0000000..ee4208f
--- /dev/null
+++ b/src/eviewcvs/Makefile
@@ -0,0 +1,22 @@
+# 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)/
diff --git a/src/eviewcvs/README b/src/eviewcvs/README
new file mode 100644
index 0000000..c7258d7
--- /dev/null
+++ b/src/eviewcvs/README
@@ -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/src/eviewcvs/eviewcvs b/src/eviewcvs/eviewcvs
new file mode 100755
index 0000000..280ec0b
--- /dev/null
+++ b/src/eviewcvs/eviewcvs
@@ -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=""
+ cyan=""
+ green=""
+ red=""
+ off=""
+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/src/eviewcvs/eviewcvs.pod b/src/eviewcvs/eviewcvs.pod
new file mode 100644
index 0000000..b4403c8
--- /dev/null
+++ b/src/eviewcvs/eviewcvs.pod
@@ -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/src/genpkgindex/Makefile b/src/genpkgindex/Makefile
new file mode 100644
index 0000000..9f0ea2c
--- /dev/null
+++ b/src/genpkgindex/Makefile
@@ -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/src/genpkgindex/genpkgindex b/src/genpkgindex/genpkgindex
new file mode 100644
index 0000000..c079b83
--- /dev/null
+++ b/src/genpkgindex/genpkgindex
@@ -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/src/genpkgindex/genpkgindex.1 b/src/genpkgindex/genpkgindex.1
new file mode 100644
index 0000000..8a3956e
--- /dev/null
+++ b/src/genpkgindex/genpkgindex.1
@@ -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/src/gensync/AUTHORS b/src/gensync/AUTHORS
new file mode 100644
index 0000000..389c51b
--- /dev/null
+++ b/src/gensync/AUTHORS
@@ -0,0 +1,5 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Original Author:
+Matthew Schick <matt@breakmygentoo.net>
diff --git a/src/gensync/ChangeLog b/src/gensync/ChangeLog
new file mode 100644
index 0000000..7ec2b86
--- /dev/null
+++ b/src/gensync/ChangeLog
@@ -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/src/gensync/Makefile b/src/gensync/Makefile
new file mode 100644
index 0000000..d5b879a
--- /dev/null
+++ b/src/gensync/Makefile
@@ -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/src/gensync/README b/src/gensync/README
new file mode 100644
index 0000000..cf54dec
--- /dev/null
+++ b/src/gensync/README
@@ -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/src/gensync/TODO b/src/gensync/TODO
new file mode 100644
index 0000000..733a959
--- /dev/null
+++ b/src/gensync/TODO
@@ -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/src/gensync/bmg-gnome-current.syncsource b/src/gensync/bmg-gnome-current.syncsource
new file mode 100644
index 0000000..51dd908
--- /dev/null
+++ b/src/gensync/bmg-gnome-current.syncsource
@@ -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/src/gensync/bmg-main.syncsource b/src/gensync/bmg-main.syncsource
new file mode 100644
index 0000000..8d74ca2
--- /dev/null
+++ b/src/gensync/bmg-main.syncsource
@@ -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/src/gensync/gensync b/src/gensync/gensync
new file mode 100755
index 0000000..52f25ed
--- /dev/null
+++ b/src/gensync/gensync
@@ -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/src/gensync/gensync.1 b/src/gensync/gensync.1
new file mode 100644
index 0000000..17d85fc
--- /dev/null
+++ b/src/gensync/gensync.1
@@ -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/src/gensync/gensync.conf b/src/gensync/gensync.conf
new file mode 100644
index 0000000..389e020
--- /dev/null
+++ b/src/gensync/gensync.conf
@@ -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/src/gentoolkit/AUTHORS b/src/gentoolkit/AUTHORS
new file mode 100644
index 0000000..0dfa694
--- /dev/null
+++ b/src/gentoolkit/AUTHORS
@@ -0,0 +1,2 @@
+Original author:
+Karl Trygve Kalleberg <karltk@gentoo.org> \ No newline at end of file
diff --git a/src/gentoolkit/Makefile b/src/gentoolkit/Makefile
new file mode 100644
index 0000000..831bcfd
--- /dev/null
+++ b/src/gentoolkit/Makefile
@@ -0,0 +1,22 @@
+# 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 "LISTOWEL (n.) The small mat on the bar designed to be more absorbent than the bar, but not as absorbent as your elbows."
+
+dist:
+ mkdir -p ../../${distdir}/src/gentoolkit
+ cp Makefile AUTHORS README TODO errors.py package.py helpers.py pprinter.py __init__.py ../../${distdir}/src/gentoolkit/
+
+install:
+ install -d $(docdir)/gentoolkit
+ install -m 0644 AUTHORS README TODO $(docdir)/gentoolkit/
+ install -d $(DESTDIR)/usr/lib/gentoolkit/pym/gentoolkit
+ install -m 0644 package.py pprinter.py helpers.py errors.py $(DESTDIR)/usr/lib/gentoolkit/pym/gentoolkit/
+ install -m 0644 __init__.py $(DESTDIR)/usr/lib/gentoolkit/pym/gentoolkit/
+
diff --git a/src/gentoolkit/README b/src/gentoolkit/README
new file mode 100644
index 0000000..916dc81
--- /dev/null
+++ b/src/gentoolkit/README
@@ -0,0 +1,17 @@
+
+Package : gentoolkit
+Version : see __init__.py
+Author : See AUTHORS
+
+MOTIVATION
+
+This is the Python API for Gentoolkit. It contains common functionality shared
+by the Gentoolkit tools written in Python.
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+N/A
diff --git a/src/gentoolkit/TODO b/src/gentoolkit/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/gentoolkit/TODO
diff --git a/src/gentoolkit/__init__.py b/src/gentoolkit/__init__.py
new file mode 100644
index 0000000..28b56be
--- /dev/null
+++ b/src/gentoolkit/__init__.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+#
+# Copyright 2003-2004 Karl Trygve Kalleberg
+# Copyright 2003-2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+# Author: Karl Trygve Kalleberg <karltk@gentoo.org>
+#
+# Portions written ripped from
+# - etcat, by Alistair Tse <liquidx@gentoo.org>
+#
+
+__author__ = "Karl Trygve Kalleberg"
+__email__ = "karltk@gentoo.org"
+__version__ = "0.1.1"
+__productname__ = "gentoolkit"
+__description__ = "Gentoolkit Common Library"
+
+import os
+import sys
+try:
+ import portage
+except ImportError:
+ sys.path.insert(0, "/usr/lib/portage/pym")
+ import portage
+import re
+try:
+ from threading import Lock
+except ImportError:
+ # If we don't have thread support, we don't need to worry about
+ # locking the global settings object. So we define a "null" Lock.
+ class Lock:
+ def acquire(self):
+ pass
+ def release(self):
+ pass
+
+try:
+ import portage.exception as portage_exception
+except ImportError:
+ import portage_exception
+
+try:
+ settingslock = Lock()
+ settings = portage.config(clone=portage.settings)
+ porttree = portage.db[portage.root]["porttree"]
+ vartree = portage.db[portage.root]["vartree"]
+ virtuals = portage.db[portage.root]["virtuals"]
+except portage_exception.PermissionDenied, e:
+ sys.stderr.write("Permission denied: '%s'\n" % str(e))
+ sys.exit(e.errno)
+
+Config = {
+ "verbosityLevel": 3
+}
+
+from helpers import *
+from package import *
diff --git a/src/gentoolkit/errors.py b/src/gentoolkit/errors.py
new file mode 100644
index 0000000..db81721
--- /dev/null
+++ b/src/gentoolkit/errors.py
@@ -0,0 +1,14 @@
+#! /usr/bin/python2
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+class FatalError:
+ def __init__(self, s):
+ self._message = s
+ def get_message(self):
+ return self._message \ No newline at end of file
diff --git a/src/gentoolkit/helpers.py b/src/gentoolkit/helpers.py
new file mode 100644
index 0000000..bf2b1b1
--- /dev/null
+++ b/src/gentoolkit/helpers.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python2
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+import portage
+from gentoolkit import *
+from package import *
+from pprinter import print_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."""
+ 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."""
+ 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 installed candidate that
+ matched the search key."""
+ t = portage.db["/"]["vartree"].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"""
+ 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"""
+ 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"""
+ 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."""
+ 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
+
+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/src/gentoolkit/package.py b/src/gentoolkit/package.py
new file mode 100644
index 0000000..3319860
--- /dev/null
+++ b/src/gentoolkit/package.py
@@ -0,0 +1,241 @@
+#! /usr/bin/python2
+#
+# Copyright(c) 2004, Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright(c) 2004, Gentoo Foundation
+#
+# Licensed under the GNU General Public License, v2
+#
+# $Header$
+
+from errors import FatalError
+import portage
+from gentoolkit import *
+
+class Package:
+ """Package descriptor. Contains convenience functions for querying the
+ state of a package, its contents, name manipulation, ebuild info and
+ similar."""
+
+ def __init__(self,cpv):
+ self._cpv = cpv
+ self._scpv = portage.catpkgsplit(self._cpv)
+
+ if not self._scpv:
+ raise FatalError("invalid cpv: %s" % cpv)
+ self._db = None
+ self._settings = settings
+ self._settingslock = settingslock
+
+ def get_name(self):
+ """Returns base name of package, no category nor version"""
+ return self._scpv[1]
+
+ def get_version(self):
+ """Returns version of package, with revision number"""
+ v = self._scpv[2]
+ if self._scpv[3] != "r0":
+ v += "-" + self._scpv[3]
+ return v
+
+ def get_category(self):
+ """Returns category of package"""
+ return self._scpv[0]
+
+ def get_settings(self, key):
+ """Returns the value of the given key for this package (useful
+ for package.* files."""
+ self._settingslock.acquire()
+ self._settings.setcpv(self._cpv)
+ v = self._settings[key]
+ self._settingslock.release()
+ return v
+
+ def get_cpv(self):
+ """Returns full Category/Package-Version string"""
+ return self._cpv
+
+ def get_provide(self):
+ """Return a list of provides, if any"""
+ if not self.is_installed():
+ try:
+ x = [self.get_env_var('PROVIDE')]
+ except KeyError:
+ x = []
+ return x
+ else:
+ return vartree.get_provide(self._cpv)
+
+ def get_dependants(self):
+ """Retrieves a list of CPVs for all packages depending on this one"""
+ raise NotImplementedError("Not implemented yet!")
+
+ def get_runtime_deps(self):
+ """Returns a linearised list of first-level run time dependencies for this package, on
+ the form [(comparator, [use flags], cpv), ...]"""
+ # Try to use the portage tree first, since emerge only uses the tree when calculating dependencies
+ try:
+ cd = self.get_env_var("RDEPEND", porttree).split()
+ except KeyError:
+ cd = self.get_env_var("RDEPEND", vartree).split()
+ r,i = self._parse_deps(cd)
+ return r
+
+ def get_compiletime_deps(self):
+ """Returns a linearised list of first-level compile time dependencies for this package, on
+ the form [(comparator, [use flags], cpv), ...]"""
+ # Try to use the portage tree first, since emerge only uses the tree when calculating dependencies
+ try:
+ rd = self.get_env_var("DEPEND", porttree).split()
+ except KeyError:
+ rd = self.get_env_var("DEPEND", vartree).split()
+ r,i = self._parse_deps(rd)
+ return r
+
+ def get_postmerge_deps(self):
+ """Returns a linearised list of first-level post merge dependencies for this package, on
+ the form [(comparator, [use flags], cpv), ...]"""
+ # Try to use the portage tree first, since emerge only uses the tree when calculating dependencies
+ try:
+ pd = self.get_env_var("PDEPEND", porttree).split()
+ except KeyError:
+ pd = self.get_env_var("PDEPEND", vartree).split()
+ r,i = self._parse_deps(pd)
+ return r
+
+ def _parse_deps(self,deps,curuse=[],level=0):
+ # store (comparator, [use predicates], cpv)
+ r = []
+ comparators = ["~","<",">","=","<=",">="]
+ end = len(deps)
+ i = 0
+ while i < end:
+ tok = deps[i]
+ if tok == ')':
+ return r,i
+ if tok[-1] == "?":
+ tok = tok.replace("?","")
+ sr,l = self._parse_deps(deps[i+2:],curuse=curuse+[tok],level=level+1)
+ r += sr
+ i += l + 3
+ continue
+ if tok == "||":
+ sr,l = self._parse_deps(deps[i+2:],curuse,level=level+1)
+ r += sr
+ i += l + 3
+ continue
+ # conjonction, like in "|| ( ( foo bar ) baz )" => recurse
+ if tok == "(":
+ sr,l = self._parse_deps(deps[i+1:],curuse,level=level+1)
+ r += sr
+ i += l + 2
+ continue
+ # pkg block "!foo/bar" => ignore it
+ if tok[0] == "!":
+ i += 1
+ continue
+ # pick out comparator, if any
+ cmp = ""
+ for c in comparators:
+ if tok.find(c) == 0:
+ cmp = c
+ tok = tok[len(cmp):]
+ r.append((cmp,curuse,tok))
+ i += 1
+ return r,i
+
+ def is_installed(self):
+ """Returns true if this package is installed (merged)"""
+ self._initdb()
+ return os.path.exists(self._db.getpath())
+
+ def is_overlay(self):
+ """Returns true if the package is in an overlay."""
+ dir,ovl = portage.portdb.findname2(self._cpv)
+ return ovl != settings["PORTDIR"]
+
+ 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 = portage.portdb.xmatch("match-visible", "=" + self._cpv)
+ return self._cpv not in unmasked
+
+ def get_ebuild_path(self,in_vartree=0):
+ """Returns the complete path to the .ebuild file"""
+ if in_vartree:
+ return vartree.getebuildpath(self._cpv)
+ else:
+ return portage.portdb.findname(self._cpv)
+
+ def get_package_path(self):
+ """Returns the path to where the ChangeLog, Manifest, .ebuild files reside"""
+ p = self.get_ebuild_path()
+ sp = p.split("/")
+ if len(sp):
+ return "/".join(sp[:-1])
+
+ def get_env_var(self, var, tree=""):
+ """Returns one of the predefined env vars DEPEND, RDEPEND, SRC_URI,...."""
+ if tree == "":
+ mytree = vartree
+ if not self.is_installed():
+ mytree = porttree
+ else:
+ mytree = tree
+ r = mytree.dbapi.aux_get(self._cpv,[var])
+ if not r:
+ raise FatalError("Could not find the package tree")
+ if len(r) != 1:
+ raise FatalError("Should only get one element!")
+ return r[0]
+
+ def get_use_flags(self):
+ """Returns the USE flags active at time of installation"""
+ self._initdb()
+ if self.is_installed():
+ return self._db.getfile("USE")
+ return ""
+
+ def get_contents(self):
+ """Returns the full contents, as a dictionary, on the form
+ [ '/bin/foo' : [ 'obj', '1052505381', '45ca8b8975d5094cd75bdc61e9933691' ], ... ]"""
+ self._initdb()
+ if self.is_installed():
+ return self._db.getcontents()
+ return {}
+
+ def compare_version(self,other):
+ """Compares this package's version to another's CPV; returns -1, 0, 1"""
+ v1 = self._scpv
+ v2 = portage.catpkgsplit(other.get_cpv())
+ # if category is different
+ if v1[0] != v2[0]:
+ return cmp(v1[0],v2[0])
+ # if name is different
+ elif v1[1] != v2[1]:
+ return cmp(v1[1],v2[1])
+ # Compare versions
+ else:
+ return portage.pkgcmp(v1[1:],v2[1:])
+
+ def size(self):
+ """Estimates the installed size of the contents of this package, if possible.
+ Returns [size, number of files in total, number of uncounted files]"""
+ contents = self.get_contents()
+ size = 0
+ uncounted = 0
+ files = 0
+ for x in contents:
+ try:
+ size += os.lstat(x).st_size
+ files += 1
+ except OSError:
+ uncounted += 1
+ return [size, files, uncounted]
+
+ def _initdb(self):
+ """Internal helper function; loads package information from disk,
+ when necessary"""
+ if not self._db:
+ cat = self.get_category()
+ pnv = self.get_name()+"-"+self.get_version()
+ self._db = portage.dblink(cat,pnv,settings["ROOT"],settings)
diff --git a/src/gentoolkit/pprinter.py b/src/gentoolkit/pprinter.py
new file mode 100644
index 0000000..39665f8
--- /dev/null
+++ b/src/gentoolkit/pprinter.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+#
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+import sys
+import gentoolkit
+
+try:
+ import portage.output as output
+except ImportError:
+ import output
+
+
+def print_error(s):
+ """Prints an error string to stderr."""
+ sys.stderr.write(output.red("!!! ") + s + "\n")
+
+def print_info(lv, s, line_break = True):
+ """Prints an informational string to stdout."""
+ if gentoolkit.Config["verbosityLevel"] >= lv:
+ sys.stdout.write(s)
+ if line_break:
+ sys.stdout.write("\n")
+
+def print_warn(s):
+ """Print a warning string to stderr."""
+ sys.stderr.write("!!! " + s + "\n")
+
+def die(err, s):
+ """Print an error string and die with an error code."""
+ print_error(s)
+ sys.exit(-err)
+
+# Colour settings
+
+def cpv(s):
+ """Print a category/package-<version> string."""
+ return output.green(s)
+
+def slot(s):
+ """Print a slot string"""
+ return output.bold(s)
+
+def useflag(s):
+ """Print a USE flag strign"""
+ return output.blue(s)
+
+def useflagon(s):
+ """Print an enabled USE flag string"""
+ # FIXME: Collapse into useflag with parameter
+ return output.red(s)
+
+def useflagoff(s):
+ """Print a disabled USE flag string"""
+ # FIXME: Collapse into useflag with parameter
+ return output.blue(s)
+
+def maskflag(s):
+ """Print a masking flag string"""
+ return output.red(s)
+
+def installedflag(s):
+ """Print an installed flag string"""
+ return output.bold(s)
+
+def number(s):
+ """Print a number string"""
+ return output.turquoise(s)
+
+def pkgquery(s):
+ """Print a package query string."""
+ return output.bold(s)
+
+def regexpquery(s):
+ """Print a regular expression string"""
+ return output.bold(s)
+
+def path(s):
+ """Print a file or directory path string"""
+ return output.bold(s)
+
+def path_symlink(s):
+ """Print a symlink string."""
+ return output.turquoise(s)
+
+def productname(s):
+ """Print a product name string, i.e. the program name."""
+ return output.turquoise(s)
+
+def globaloption(s):
+ """Print a global option string, i.e. the program global options."""
+ return output.yellow(s)
+
+def localoption(s):
+ """Print a local option string, i.e. the program local options."""
+ return output.green(s)
+
+def command(s):
+ """Print a program command string."""
+ return output.green(s)
+
+def section(s):
+ """Print a string as a section header."""
+ return output.turquoise(s)
+
+def subsection(s):
+ """Print a string as a subsection header."""
+ return output.turquoise(s)
+
+def emph(s):
+ """Print a string as emphasized."""
+ return output.bold(s)
diff --git a/src/glsa-check/Makefile b/src/glsa-check/Makefile
new file mode 100644
index 0000000..9ad5717
--- /dev/null
+++ b/src/glsa-check/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2003 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+all:
+ echo "YADDLETHORPE (vb.) (Of offended pooves.) To exit huffily from a boutique."
+
+dist:
+ mkdir -p ../../$(distdir)/src/glsa-check/
+ cp Makefile glsa.py glsa-check glsa-check.1 ../../$(distdir)/src/glsa-check/
+
+install:
+ install -d $(DESTDIR)/usr/lib/gentoolkit/pym/
+ install -m 0755 glsa-check $(bindir)/
+ install -m 0644 glsa.py $(DESTDIR)/usr/lib/gentoolkit/pym/
+ install -m 0644 glsa-check.1 $(mandir)/
diff --git a/src/glsa-check/glsa-check b/src/glsa-check/glsa-check
new file mode 100644
index 0000000..98e5708
--- /dev/null
+++ b/src/glsa-check/glsa-check
@@ -0,0 +1,364 @@
+#!/usr/bin/python
+
+# $Header: $
+# This program is licensed under the GPL, version 2
+
+import os
+import sys
+sys.path.insert(0, "/usr/lib/gentoolkit/pym")
+try:
+ import portage
+except ImportError:
+ sys.path.insert(0, "/usr/lib/portage/pym")
+ import portage
+
+try:
+ from portage.output import *
+except ImportError:
+ from output import *
+
+from getopt import getopt,GetoptError
+
+__program__ = "glsa-check"
+__author__ = "Marius Mauch <genone@gentoo.org>"
+__version__ = "0.9"
+
+optionmap = [
+["-l", "--list", "list all unapplied GLSA"],
+["-d", "--dump", "--print", "show all information about the given GLSA"],
+["-t", "--test", "test if this system is affected by the given GLSA"],
+["-p", "--pretend", "show the necessary commands to apply this GLSA"],
+["-f", "--fix", "try to auto-apply this GLSA (experimental)"],
+["-i", "--inject", "inject the given GLSA into the checkfile"],
+["-n", "--nocolor", "disable colors (option)"],
+["-e", "--emergelike", "do not use a least-change algorithm (option)"],
+["-h", "--help", "show this help message"],
+["-V", "--version", "some information about this tool"],
+["-v", "--verbose", "print more information (option)"],
+["-c", "--cve", "show CAN ids in listing mode (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])])
+# ["dump", "print", "list", "pretend", "fix", "inject", "help", "verbose", "version", "test", "nocolor", "cve", "mail"])
+ 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)
+
+ # 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("new")
+
+# show help message
+if mode == "help":
+ sys.stderr.write("\nSyntax: glsa-check <option> [glsa-list]\n\n")
+ for m in optionmap:
+ sys.stderr.write(m[0] + "\t" + m[1] + " \t: " + m[-1] + "\n")
+ for o in m[2:-1]:
+ sys.stderr.write("\t" + o + "\n")
+ sys.stderr.write("\nglsa-list can contain an arbitrary number of GLSA ids, \n")
+ sys.stderr.write("filenames containing GLSAs or the special identifiers \n")
+ sys.stderr.write("'all', 'new' and 'affected'\n")
+ sys.exit(1)
+
+# we need root priviledges for write access
+if mode in ["fix", "inject"] and os.geteuid() != 0:
+ sys.stderr.write("\nThis tool needs root access to "+mode+" this GLSA\n\n")
+ sys.exit(2)
+
+# show version and copyright information
+if mode == "version":
+ sys.stderr.write("\n"+ __program__ + ", version " + __version__ + "\n")
+ sys.stderr.write("Author: " + __author__ + "\n")
+ sys.stderr.write("This program is licensed under the GPL, version 2\n\n")
+ sys.exit(0)
+
+# delay this for speed increase
+from glsa import *
+
+glsaconfig = checkconfig(portage.config(clone=portage.settings))
+
+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:
+ glsalist = todolist
+ params.remove("new")
+
+if "all" in params:
+ glsalist = completelist
+ params.remove("all")
+if "affected" in params:
+ # replaced completelist with todolist on request of wschlich
+ 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):
+ fd2.write(white("[A]")+" means this GLSA was already applied,\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")
+
+ 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.isApplied():
+ 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(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 "+myid+"\n")
+ mergelist = myglsa.getMergeList(least_change=least_change)
+ 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)
+ myglsa.inject()
+ elif mode == "pretend":
+ sys.stdout.write("Checking GLSA "+myid+"\n")
+ mergelist = myglsa.getMergeList(least_change=least_change)
+ if mergelist:
+ sys.stdout.write("The following updates will be performed for this GLSA:\n")
+ for pkg in mergelist:
+ oldver = None
+ for x in vardb.match(portage.dep_getkey(pkg)):
+ if vardb.aux_get(x, ["SLOT"]) == portdb.aux_get(pkg, ["SLOT"]):
+ oldver = x
+ if oldver == None:
+ raise ValueError("could not find old version for package %s" % pkg)
+ oldver = oldver[len(portage.dep_getkey(oldver))+1:]
+ sys.stdout.write(" " + pkg + " (" + oldver + ")\n")
+ else:
+ sys.stdout.write("Nothing to do for this GLSA\n")
+ elif mode == "inject":
+ sys.stdout.write("injecting " + myid + "\n")
+ myglsa.inject()
+ sys.stdout.write("\n")
+ 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():
+ if verbose:
+ outputlist.append(str(myglsa.nr)+" ( "+myglsa.title+" ) ")
+ else:
+ outputlist.append(str(myglsa.nr))
+ if len(outputlist) > 0:
+ sys.stderr.write("This system is affected by the following GLSAs:\n")
+ 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 glsaconfig.has_key("PORTAGE_ELOG_MAILURI"):
+ myrecipient = glsaconfig["PORTAGE_ELOG_MAILURI"].split()[0]
+ else:
+ myrecipient = "root@localhost"
+
+ if glsaconfig.has_key("PORTAGE_ELOG_MAILFROM"):
+ 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()
+
+ 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/src/glsa-check/glsa-check.1 b/src/glsa-check/glsa-check.1
new file mode 100644
index 0000000..5a7a525
--- /dev/null
+++ b/src/glsa-check/glsa-check.1
@@ -0,0 +1,57 @@
+.TH "glsa-check" "1" "0.6" "Marius Mauch" "gentoolkit"
+.SH "NAME"
+.LP
+glsa\-check \- Gentoo: Tool to locally monitor and manage GLSA's
+.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', 'new' 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/proj/en/portage/glsa\-integration.xml
+.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 all unapplied GLSA
+.TP
+.B \-d, \-\-dump, \-\-print
+show all information about the given GLSA
+.TP
+.B \-t, \-\-test
+test if this system is affected by the given GLSA
+.TP
+.B \-p, \-\-pretend
+show the necessary commands to apply this GLSA
+.TP
+.B \-f, \-\-fix
+try to auto\-apply this GLSA (experimental)
+.TP
+.B \-i, \-\-inject
+inject the given GLSA into the checkfile
+.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 CAN ids in listing mode (option)
+.TP
+.B \-m, \-\-mail
+send a mail with the given GLSAs to the administrator
diff --git a/src/glsa-check/glsa.py b/src/glsa-check/glsa.py
new file mode 100644
index 0000000..4c8f280
--- /dev/null
+++ b/src/glsa-check/glsa.py
@@ -0,0 +1,644 @@
+# $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 time
+import codecs
+import re
+import xml.dom.minidom
+
+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/cache/edb/glsa",
+ "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:
+ 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
+ """
+ rValue = []
+ if not listnode.nodeName in ["ul", "ol"]:
+ raise GlsaFormatException("Invalid function call: listnode is not <ul> or <ol>")
+ for li in listnode.childNodes:
+ if li.nodeType != xml.dom.Node.ELEMENT_NODE:
+ continue
+ rValue.append(getText(li, format="strip"))
+ return rValue
+
+def getText(node, format):
+ """
+ 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.
+ @rtype: String
+ @return: the (formatted) content of the node and its subnodes
+ """
+ rValue = ""
+ if format in ["strip", "keep"]:
+ if node.nodeName in ["uri", "mail"]:
+ rValue += node.childNodes[0].data+": "+node.getAttribute("link")
+ else:
+ for subnode in node.childNodes:
+ if subnode.nodeName == "#text":
+ rValue += subnode.data
+ else:
+ rValue += getText(subnode, format)
+ else:
+ for subnode in node.childNodes:
+ if subnode.nodeName == "p":
+ for p_subnode in subnode.childNodes:
+ if p_subnode.nodeName == "#text":
+ rValue += p_subnode.data.strip()
+ elif p_subnode.nodeName in ["uri", "mail"]:
+ rValue += p_subnode.childNodes[0].data
+ rValue += " ( "+p_subnode.getAttribute("link")+" )"
+ rValue += NEWLINE_ESCAPE
+ elif subnode.nodeName == "ul":
+ for li in getListElements(subnode):
+ rValue += "-"+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" "
+ elif subnode.nodeName == "ol":
+ i = 0
+ for li in getListElements(subnode):
+ i = i+1
+ rValue += str(i)+"."+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" "
+ elif subnode.nodeName == "code":
+ rValue += getText(subnode, format="keep").replace("\n", NEWLINE_ESCAPE)
+ if rValue[-1*len(NEWLINE_ESCAPE):] != NEWLINE_ESCAPE:
+ rValue += NEWLINE_ESCAPE
+ elif subnode.nodeName == "#text":
+ rValue += subnode.data
+ else:
+ raise GlsaFormatException("Invalid Tag found: ", subnode.nodeName)
+ if format == "strip":
+ rValue = rValue.strip(" \n\t")
+ rValue = re.sub("[\s]{2,}", " ", rValue)
+ # Hope that the utf conversion doesn't break anything else
+ return rValue.encode("utf_8")
+
+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 = []
+ for e in rootnode.getElementsByTagName(tagname):
+ rValue.append(getText(e, format))
+ 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")
+ 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
+ """
+ return opMapping[versionNode.getAttribute("range")] \
+ +getText(versionNode, format="strip")
+
+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"):
+ mylist = portdb.match(re.sub("-r[0-9]+$", "", 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 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 = None
+ v_installed = []
+ u_installed = []
+ for v in vulnerableList:
+ v_installed += match(v, "vartree")
+
+ for u in unaffectedList:
+ u_installed += match(u, "vartree")
+
+ install_unaffected = True
+ for i in v_installed:
+ if i not in u_installed:
+ install_unaffected = False
+
+ if install_unaffected:
+ return rValue
+
+ for u in unaffectedList:
+ mylist = match(u, "porttree", match_type="match-all")
+ for c in mylist:
+ c_pv = portage.catpkgsplit(c)
+ i_pv = portage.catpkgsplit(portage.best(v_installed))
+ if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 \
+ and (rValue == None \
+ or not match("="+rValue, "porttree") \
+ or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(rValue)[1:]) > 0)) \
+ and match("="+c, "porttree")) \
+ and portage.db["/"]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db["/"]["vartree"].dbapi.aux_get(portage.best(v_installed), ["SLOT"]):
+ rValue = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
+ if c_pv[3] != "r0": # we don't like -r0 for display
+ rValue += "-"+c_pv[3]
+ return rValue
+
+
+# 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":
+ 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 = getText(myroot.getElementsByTagName("announced")[0], format="strip")
+ self.revised = getText(myroot.getElementsByTagName("revised")[0], format="strip")
+
+ # 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):
+ """
+ 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 latin1).
+
+ @type outstream: File
+ @param outfile: Stream that should be used for writing
+ (defaults to sys.stdout)
+ """
+ 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\n\n" % self.revised)
+ 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: ")
+ for i in range(0, len(self.bugs)):
+ outstream.write(self.bugs[i])
+ if i < len(self.bugs)-1:
+ outstream.write(", ")
+ else:
+ 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 = ""
+ for r in self.references:
+ myreferences += (r.replace(" ", SPACE_ESCAPE)+NEWLINE_ESCAPE+" ")
+ 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
+ """
+ vList = []
+ 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 (len(match(v, "vartree")) > 0 \
+ and getMinUpgrade(path["vul_atoms"], path["unaff_atoms"]))
+ return rValue
+
+ def isApplied(self):
+ """
+ Looks if the GLSA IDis in the GLSA checkfile to check if this
+ GLSA was already applied.
+
+ @rtype: Boolean
+ @returns: True if the GLSA was applied, False if not
+ """
+ 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.isApplied():
+ 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
+ """
+ rValue = []
+ 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:
+ rValue.append(update)
+ return rValue
diff --git a/src/lintool/AUTHORS b/src/lintool/AUTHORS
new file mode 100644
index 0000000..fe436cb
--- /dev/null
+++ b/src/lintool/AUTHORS
@@ -0,0 +1 @@
+Karl Trygve Kalleberg <karltk@gentoo.org>
diff --git a/src/lintool/COPYING b/src/lintool/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/src/lintool/COPYING
@@ -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.
+
+ 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.)
+
+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.
+
+ 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.
+
+ 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
+
+ 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/src/lintool/ChangeLog b/src/lintool/ChangeLog
new file mode 100644
index 0000000..0e8c1e9
--- /dev/null
+++ b/src/lintool/ChangeLog
@@ -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/src/lintool/NEWS b/src/lintool/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/lintool/NEWS
diff --git a/src/lintool/README b/src/lintool/README
new file mode 100644
index 0000000..1494aad
--- /dev/null
+++ b/src/lintool/README
@@ -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/src/lintool/lintool.1 b/src/lintool/lintool.1
new file mode 100644
index 0000000..11883e8
--- /dev/null
+++ b/src/lintool/lintool.1
@@ -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/src/lintool/lintool.py b/src/lintool/lintool.py
new file mode 100755
index 0000000..721c744
--- /dev/null
+++ b/src/lintool/lintool.py
@@ -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/src/lintool/lintool/__init__.py b/src/lintool/lintool/__init__.py
new file mode 100644
index 0000000..4d2e3a6
--- /dev/null
+++ b/src/lintool/lintool/__init__.py
@@ -0,0 +1,3 @@
+import ebuild
+import changelog
+import digest
diff --git a/src/lintool/lintool/changelog.py b/src/lintool/lintool/changelog.py
new file mode 100644
index 0000000..1dad779
--- /dev/null
+++ b/src/lintool/lintool/changelog.py
@@ -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/src/lintool/lintool/digest.py b/src/lintool/lintool/digest.py
new file mode 100644
index 0000000..5f174ae
--- /dev/null
+++ b/src/lintool/lintool/digest.py
@@ -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/src/lintool/lintool/ebuild.py b/src/lintool/lintool/ebuild.py
new file mode 100644
index 0000000..c8df0a2
--- /dev/null
+++ b/src/lintool/lintool/ebuild.py
@@ -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/src/lintool/lintool/test.py b/src/lintool/lintool/test.py
new file mode 100644
index 0000000..0cc56ff
--- /dev/null
+++ b/src/lintool/lintool/test.py
@@ -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/src/moo/AUTHORS b/src/moo/AUTHORS
new file mode 100644
index 0000000..fe436cb
--- /dev/null
+++ b/src/moo/AUTHORS
@@ -0,0 +1 @@
+Karl Trygve Kalleberg <karltk@gentoo.org>
diff --git a/src/moo/README b/src/moo/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/moo/README
diff --git a/src/moo/TODO b/src/moo/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/moo/TODO
diff --git a/src/moo/moo b/src/moo/moo
new file mode 100755
index 0000000..828df08
--- /dev/null
+++ b/src/moo/moo
@@ -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/src/moo/moo.1 b/src/moo/moo.1
new file mode 100644
index 0000000..534aa0c
--- /dev/null
+++ b/src/moo/moo.1
@@ -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/src/old-scripts/Makefile b/src/old-scripts/Makefile
new file mode 100644
index 0000000..8f61cb5
--- /dev/null
+++ b/src/old-scripts/Makefile
@@ -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/src/old-scripts/dep-clean b/src/old-scripts/dep-clean
new file mode 100644
index 0000000..89c6364
--- /dev/null
+++ b/src/old-scripts/dep-clean
@@ -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/src/old-scripts/dep-clean.1 b/src/old-scripts/dep-clean.1
new file mode 100644
index 0000000..9747ce4
--- /dev/null
+++ b/src/old-scripts/dep-clean.1
@@ -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/src/old-scripts/ewhich b/src/old-scripts/ewhich
new file mode 100755
index 0000000..345ec34
--- /dev/null
+++ b/src/old-scripts/ewhich
@@ -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/src/old-scripts/ewhich.1 b/src/old-scripts/ewhich.1
new file mode 100644
index 0000000..4bc5459
--- /dev/null
+++ b/src/old-scripts/ewhich.1
@@ -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/src/old-scripts/mkebuild b/src/old-scripts/mkebuild
new file mode 100644
index 0000000..9ee0ce1
--- /dev/null
+++ b/src/old-scripts/mkebuild
@@ -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/src/old-scripts/mkebuild.1 b/src/old-scripts/mkebuild.1
new file mode 100644
index 0000000..e800d4d
--- /dev/null
+++ b/src/old-scripts/mkebuild.1
@@ -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/src/old-scripts/pkg-clean b/src/old-scripts/pkg-clean
new file mode 100644
index 0000000..9b07337
--- /dev/null
+++ b/src/old-scripts/pkg-clean
@@ -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/src/old-scripts/pkg-clean.1 b/src/old-scripts/pkg-clean.1
new file mode 100644
index 0000000..7a295f3
--- /dev/null
+++ b/src/old-scripts/pkg-clean.1
@@ -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/src/old-scripts/pkg-size b/src/old-scripts/pkg-size
new file mode 100644
index 0000000..8770db7
--- /dev/null
+++ b/src/old-scripts/pkg-size
@@ -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/src/old-scripts/pkg-size.1 b/src/old-scripts/pkg-size.1
new file mode 100644
index 0000000..b195412
--- /dev/null
+++ b/src/old-scripts/pkg-size.1
@@ -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/src/pkg-clean/AUTHORS b/src/pkg-clean/AUTHORS
new file mode 100644
index 0000000..f126a36
--- /dev/null
+++ b/src/pkg-clean/AUTHORS
@@ -0,0 +1,5 @@
+Maintainer:
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+Authors:
+Leo Lipelis <aeoo@gentoo.org> (original author)
diff --git a/src/pkg-clean/ChangeLog b/src/pkg-clean/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pkg-clean/ChangeLog
diff --git a/src/pkg-clean/README b/src/pkg-clean/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pkg-clean/README
diff --git a/src/pkg-clean/pkg-clean b/src/pkg-clean/pkg-clean
new file mode 100644
index 0000000..abe0159
--- /dev/null
+++ b/src/pkg-clean/pkg-clean
@@ -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/src/pkg-clean/pkg-clean.1 b/src/pkg-clean/pkg-clean.1
new file mode 100644
index 0000000..7a295f3
--- /dev/null
+++ b/src/pkg-clean/pkg-clean.1
@@ -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/src/pkg-size/pkg-size b/src/pkg-size/pkg-size
new file mode 100644
index 0000000..84dd7df
--- /dev/null
+++ b/src/pkg-size/pkg-size
@@ -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/src/qpkg/AUTHORS b/src/qpkg/AUTHORS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/qpkg/AUTHORS
diff --git a/src/qpkg/ChangeLog b/src/qpkg/ChangeLog
new file mode 100644
index 0000000..b25fd25
--- /dev/null
+++ b/src/qpkg/ChangeLog
@@ -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/src/qpkg/Makefile b/src/qpkg/Makefile
new file mode 100644
index 0000000..cccdee0
--- /dev/null
+++ b/src/qpkg/Makefile
@@ -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/src/qpkg/README b/src/qpkg/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/qpkg/README
diff --git a/src/qpkg/TODO b/src/qpkg/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/qpkg/TODO
diff --git a/src/qpkg/qpkg b/src/qpkg/qpkg
new file mode 100644
index 0000000..dd8344b
--- /dev/null
+++ b/src/qpkg/qpkg
@@ -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/src/qpkg/qpkg.1 b/src/qpkg/qpkg.1
new file mode 100644
index 0000000..6d45a93
--- /dev/null
+++ b/src/qpkg/qpkg.1
@@ -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/src/qpkg/qpkg.sh b/src/qpkg/qpkg.sh
new file mode 100644
index 0000000..cdbe3c7
--- /dev/null
+++ b/src/qpkg/qpkg.sh
@@ -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/src/revdep-rebuild/99revdep-rebuild b/src/revdep-rebuild/99revdep-rebuild
new file mode 100644
index 0000000..bdaecc7
--- /dev/null
+++ b/src/revdep-rebuild/99revdep-rebuild
@@ -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/src/revdep-rebuild/AUTHORS b/src/revdep-rebuild/AUTHORS
new file mode 100644
index 0000000..b3d9b32
--- /dev/null
+++ b/src/revdep-rebuild/AUTHORS
@@ -0,0 +1,2 @@
+Stanislav Brabec (original author)
+Paul Varner <fuzzyray@gentoo.org>
diff --git a/src/revdep-rebuild/ChangeLog b/src/revdep-rebuild/ChangeLog
new file mode 100644
index 0000000..9060781
--- /dev/null
+++ b/src/revdep-rebuild/ChangeLog
@@ -0,0 +1,9 @@
+2005-06-05 Paul Varner <fuzzyray@gentoo.org>
+
+ * ChangeLog moved to main gentoolkit ChangeLog
+
+2004-01-07 Karl Trygve Kalleberg <karltk@gentoo.org>
+
+ * Added Makefile
+ * Copied revdep-rebuild script from app-portage/gentoolkit
+
diff --git a/src/revdep-rebuild/Makefile b/src/revdep-rebuild/Makefile
new file mode 100644
index 0000000..d509681
--- /dev/null
+++ b/src/revdep-rebuild/Makefile
@@ -0,0 +1,23 @@
+# 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 "AIGBURTH (AYG-berth n.) Any piece of readily identifiable anatomy found among cooked meat."
+
+dist:
+ mkdir -p ../../$(distdir)/src/revdep-rebuild
+ cp Makefile AUTHORS README TODO ChangeLog revdep-rebuild revdep-rebuild.1 99revdep-rebuild ../../$(distdir)/src/revdep-rebuild/
+
+install:
+
+ install -m 0755 revdep-rebuild $(bindir)/
+ install -d $(docdir)/revdep-rebuild
+ install -m 0644 AUTHORS README TODO $(docdir)/revdep-rebuild/
+ install -m 0644 revdep-rebuild.1 $(mandir)/
+ install -d $(sysconfdir)/revdep-rebuild
+ install -m 0644 99revdep-rebuild $(sysconfdir)/revdep-rebuild/
diff --git a/src/revdep-rebuild/README b/src/revdep-rebuild/README
new file mode 100644
index 0000000..3a51d9f
--- /dev/null
+++ b/src/revdep-rebuild/README
@@ -0,0 +1,4 @@
+This tool scans libraries and binaries for broken shared lib dependencies
+and fixes them by re-emerging those broken binaries and shared libraries.
+
+- Alastair Tse <liquidx@gentoo.org>
diff --git a/src/revdep-rebuild/TODO b/src/revdep-rebuild/TODO
new file mode 100644
index 0000000..d9f6350
--- /dev/null
+++ b/src/revdep-rebuild/TODO
@@ -0,0 +1,7 @@
+- revdep cache in /var/cache
+ - list all .so files this package depends on
+ - use timestamps of files to know when to rebuild
+ - if ts of cache is older than any of the package's contained
+ files, we must rebuild
+- update to use equery/gentoolkit
+- rewrite in python and/or eclectic module
diff --git a/src/revdep-rebuild/find_pkgs.py b/src/revdep-rebuild/find_pkgs.py
new file mode 100755
index 0000000..7013813
--- /dev/null
+++ b/src/revdep-rebuild/find_pkgs.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+# Copyright 1999-2005 Gentoo Foundation
+# $Header$
+
+# Temporary script to find package versions and slot for revdep-rebuild
+
+import sys
+
+sys.path.insert(0, "/usr/lib/gentoolkit/pym")
+import gentoolkit
+
+for pkgname in sys.argv[1:]:
+ matches = gentoolkit.find_packages(pkgname)
+ for pkg in matches:
+ (cat, name, ver, rev) = gentoolkit.split_package_name(pkg.get_cpv())
+ slot = pkg.get_env_var("SLOT")
+ if rev == "r0":
+ fullversion = ver
+ else:
+ fullversion = ver + "-" + rev
+
+ print name + " " + fullversion + " (" + slot + ")"
diff --git a/src/revdep-rebuild/revdep-rebuild b/src/revdep-rebuild/revdep-rebuild
new file mode 100755
index 0000000..3ffe904
--- /dev/null
+++ b/src/revdep-rebuild/revdep-rebuild
@@ -0,0 +1,1094 @@
+#!/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 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
+ 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."
+}
+##
+# 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 "$@"
+}
+
+print_usage() {
+cat << EOF
+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() {
+ awk '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.
+get_file_owner() {
+ local IFS=$'\n'
+ # ${*/%/ } adds a space to the end of each object name to prevent false
+ # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
+ find -L /var/db/pkg -name CONTENTS -print0 |
+ xargs -0 grep -Fl "${*/%/ }" |
+ 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[@]/%-f/--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.
+ export NOCOLOR=$(portageq envvar NOCOLOR)
+ [[ $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() {
+ umask 007 || die $? "Unable to set umask 007"
+ if [[ ! $1 ]]; then
+ die 1 'Temporary file path is unset! (This is a bug.)'
+ elif [[ -d $1 ]]; then
+ # HACK: I hate using find this way
+ if [[ $(find "$1" -type d ! \( -user $2 -perm -0700 \) ) ]]; then
+ eerror "Incorrect permissions on $1"
+ eerror "or at least one file in $1."
+ die 1 "Please make sure it's not a symlink and then remove it."
+ fi
+ 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" "$uid"
+ 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
+ find ${SEARCH_DIRS[@]} $findMask -type f \( -executable -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
+ for depend in $(
+ awk -F"[=']" '/^dependency_libs/{
+ gsub("^-[^[:space:]]*", "", $3);
+ gsub("[[:space:]]-[^[:space:]]*", "", $3);
+ print $3
+ }' "$target_file"
+ ); do
+ if [[ $depend = /* && ! -e $depend ]]; then
+ echo "obj $target_file" >> "$BROKEN_FILE"
+ echo_v " broken $target_file (requires $depend)"
+ fi
+ done
+ 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'/|}"
+ awk -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" | awk '!s[$0]++' # (omit dupes)
+ fi
+}
+##
+# Setup portage and the search paths
+setup_portage() {
+ local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
+ PORTAGE_ROOT=$(portageq envvar ROOT)
+
+ # Obey PORTAGE_NICENESS
+ if [[ $PORTAGE_NICENESS ]]; then
+ 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[@]} $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_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $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. If some inconsistency remains, it can be orphaned file, deep'
+ einfo 'dependency, binary package or specially evaluated library.'
+ 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/src/revdep-rebuild/revdep-rebuild-old b/src/revdep-rebuild/revdep-rebuild-old
new file mode 100755
index 0000000..52d6d19
--- /dev/null
+++ b/src/revdep-rebuild/revdep-rebuild-old
@@ -0,0 +1,720 @@
+#!/bin/bash
+# Copyright 1999-2007 Gentoo Foundation
+# $Header$
+
+# revdep-rebuild: Reverse dependency rebuilder.
+# Original Author: Stanislav Brabec
+# Current Maintainer: Paul Varner <fuzzyray@gentoo.org>
+
+# Known problems:
+#
+# In exact ebuild mode revdep-rebuild can fail to properly order packages,
+# which are not up to date.
+# http://bugs.gentoo.org/show_bug.cgi?id=23018
+#
+# Rebuilding using --package-names mode should be default, but emerge has no
+# feature to update to latest version of defined SLOT.
+# http://bugs.gentoo.org/show_bug.cgi?id=4698
+
+# Customizable variables:
+#
+# LD_LIBRARY_MASK - Mask of specially evaluated libraries
+# SEARCH_DIRS - List of directories to search for executables and libraries
+# SEARCH_DIRS_MASK - List of directories to not search
+#
+# 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
+
+if [ "$1" = "-h" -o "$1" = "--help" ]
+then
+ echo "Usage: $0 [OPTIONS] [--] [EMERGE_OPTIONS]"
+ echo
+ echo "Broken reverse dependency rebuilder."
+ echo
+ echo " -X, --package-names Emerge based on package names, not exact versions"
+ echo " --library NAME Emerge existing packages that use the library with NAME"
+ echo " --library=NAME NAME can be a full path to the library or a basic"
+ echo " regular expression (man grep)"
+ echo " -np, --no-ld-path Do not set LD_LIBRARY_PATH"
+ echo " -nc, --nocolor Turn off colored output"
+ echo " -i, --ignore Ignore temporary files from previous runs"
+ echo " -q, --quiet Be less verbose (also passed to emerge command)"
+ echo " -vv, --extra-verbose Be extra verbose"
+ echo
+ echo "Calls emerge, all other options are used for it (e. g. -p, --pretend)."
+ echo
+ echo "Report bugs to <http://bugs.gentoo.org>"
+ exit 0
+fi
+
+echo "Configuring search environment for revdep-rebuild"
+
+# Obey PORTAGE_NICENESS
+PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
+if [ ! -z "$PORTAGE_NICENESS" ]
+then
+ 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=$(portageq envvar ROOT)
+[ -z "$PORTAGE_ROOT" ] && PORTAGE_ROOT="/"
+
+# 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
+PRELIMINARY_SEARCH_DIRS="$SEARCH_DIRS $(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)"
+PRELIMINARY_SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK $(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)"
+PRELIMINARY_LD_LIBRARY_MASK="$LD_LIBRARY_MASK $(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)"
+
+# Add the defaults
+if [ -d /etc/revdep-rebuild ]
+then
+ for file in $(ls /etc/revdep-rebuild)
+ do
+ PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $(. /etc/revdep-rebuild/${file}; echo $SEARCH_DIRS)"
+ PRELIMINARY_SEARCH_DIRS_MASK="$PRELIMINARY_SEARCH_DIRS_MASK $(. /etc/revdep-rebuild/${file}; echo $SEARCH_DIRS_MASK)"
+ PRELIMINARY_LD_LIBRARY_MASK="$PRELIMINARY_LD_LIBRARY_MASK $(. /etc/revdep-rebuild/${file}; echo $LD_LIBRARY_MASK)"
+ done
+else
+ PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
+ PRELIMINARY_SEARCH_DIRS_MASK="$PRELIMINARY_SEARCH_DIRS_MASK /opt/OpenOffice /usr/lib/openoffice"
+ PRELIMINARY_LD_LIBRARY_MASK="$PRELIMINARY_LD_LIBRARY_MASK libodbcinst.so libodbc.so libjava.so libjvm.so"
+fi
+
+# Get the ROOTPATH and PATH from /etc/profile.env
+if [ -e "/etc/profile.env" ]
+then
+ PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $((. /etc/profile.env; echo ${ROOTPATH}:${PATH}) | tr ':' ' ')"
+fi
+
+# Get the directories from /etc/ld.so.conf
+if [ -e /etc/ld.so.conf ]
+then
+ PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $(grep -v "^#" /etc/ld.so.conf | tr '\n' ' ')"
+fi
+
+# Set the final variables
+# Note: Using $(echo $variable) removes extraneous spaces from variable assignment
+unset SEARCH_DIRS
+for i in $(echo $PRELIMINARY_SEARCH_DIRS)
+do
+ [ "$i" = "-*" ] && break
+ # Append a / at the end so that links and directories are treated the same by find
+ # Remove any existing trailing slashes to prevent double-slashes
+ SEARCH_DIRS="$(echo $SEARCH_DIRS ${i/%\//}/)"
+done
+# Remove any double-slashes from the path
+SEARCH_DIRS="$(echo $SEARCH_DIRS | sed 's:/\+:/:g')"
+
+unset SEARCH_DIRS_MASK
+for i in $(echo $PRELIMINARY_SEARCH_DIRS_MASK)
+do
+ [ "$i" = "-*" ] && break
+ SEARCH_DIRS_MASK="$(echo $SEARCH_DIRS_MASK $i)"
+done
+
+unset LD_LIBRARY_MASK
+for i in $(echo $PRELIMINARY_LD_LIBRARY_MASK)
+do
+ [ "$i" = "-*" ] && break
+ LD_LIBRARY_MASK="$(echo $LD_LIBRARY_MASK $i)"
+done
+
+# Use the color preference from portage
+NOCOLOR=$(portageq envvar NOCOLOR)
+
+# Base of temporary files names.
+touch ${HOME}/.revdep-rebuild_0.test 2>/dev/null
+if [ $? -eq 0 ]
+then
+ LIST="${HOME}/.revdep-rebuild"
+ rm ~/.revdep-rebuild_0.test
+else
+ # Try to use /var/tmp since $HOME is not available
+ touch /var/tmp/.revdep-rebuild_0.test 2>/dev/null
+ if [ $? -eq 0 ]
+ then
+ LIST="/var/tmp/.revdep-rebuild"
+ rm /var/tmp/.revdep-rebuild_0.test
+ else
+ echo
+ echo "!!! Unable to write temporary files to either $HOME or /var/tmp !!!"
+ echo
+ exit 1
+ fi
+fi
+
+shopt -s nullglob
+shopt -s expand_aliases
+unalias -a
+
+# Color Definitions
+NO="\x1b[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"
+
+# Check if portage-utils are installed
+portageq has_version $PORTAGE_ROOT portage-utils
+if [ "$?" -eq 0 ]
+then
+ PORTAGE_UTILS=true
+else
+ PORTAGE_UTILS=false
+fi
+
+alias echo_v=echo
+
+PACKAGE_NAMES=false
+SONAME="not found"
+SONAME_GREP=grep
+SEARCH_BROKEN=true
+EXTRA_VERBOSE=false
+KEEP_TEMP=false
+FULL_LD_PATH=true
+
+EMERGE_OPTIONS=""
+PRELIMINARY_CALLED_OPTIONS=""
+while [ ! -z "$1" ] ; do
+ case "$1" in
+ -X | --package-names )
+ PACKAGE_NAMES=true
+ PRELIMINARY_CALLED_OPTIONS="${PRELIMINARY_CALLED_OPTIONS} --package_names"
+ shift
+ ;;
+ -q | --quiet )
+ alias echo_v=:
+ EMERGE_OPTIONS="${EMERGE_OPTIONS} $1"
+ shift
+ ;;
+ --library=* | --soname=* | --soname-regexp=* )
+ SONAME="${1#*=}"
+ SEARCH_BROKEN=false
+ PRELIMINARY_CALLED_OPTIONS="${PRELIMINARY_CALLED_OPTIONS} --library=${SONAME}"
+ shift
+ ;;
+ --library | --soname | --soname-regexp )
+ SONAME="$2"
+ SEARCH_BROKEN=false
+ PRELIMINARY_CALLED_OPTIONS="${PRELIMINARY_CALLED_OPTIONS} --library=${SONAME}"
+ shift 2
+ ;;
+ -nc | --no-color | --nocolor )
+ NOCOLOR=true
+ shift
+ ;;
+ -np | --no-ld-path )
+ FULL_LD_PATH=false
+ PRELIMINARY_CALLED_OPTIONS="${PRELIMINARY_CALLED_OPTIONS} --no-ld-path"
+ shift
+ ;;
+ -i | --ignore )
+ rm -f ${LIST}*
+ shift
+ ;;
+ --keep-temp )
+ KEEPTEMP=true
+ shift
+ ;;
+ -vv | --extra-verbose )
+ EXTRA_VERBOSE=true
+ shift
+ ;;
+ -- )
+ shift
+ ;;
+ * )
+ EMERGE_OPTIONS="${EMERGE_OPTIONS} $1"
+ shift
+ ;;
+ esac
+done
+
+EMERGE_OPTIONS=$(echo $EMERGE_OPTIONS | sed 's/^ //')
+
+if [ -z "$PRELIMINARY_CALLED_OPTIONS" ]
+then
+ CALLED_OPTIONS=""
+else
+ for i in $(echo $PRELIMINARY_CALLED_OPTIONS | tr ' ' '\n'| sort)
+ do
+ CALLED_OPTIONS="$(echo $CALLED_OPTIONS $i)"
+ done
+fi
+
+if [ "$NOCOLOR" = "yes" -o "$NOCOLOR" = "true" ]
+then
+ NOCOLOR=true
+else
+ NOCOLOR=false
+fi
+
+# Make the NOCOLOR variable visible to emerge
+export NOCOLOR
+
+if $NOCOLOR
+then
+ NO=""
+ BR=""
+ CY=""
+ GR=""
+ RD=""
+ YL=""
+ BL=""
+fi
+
+function set_trap () {
+ trap "rm_temp $1" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+}
+
+function rm_temp () {
+ echo " terminated."
+ echo "Removing incomplete $1."
+ rm $1
+ echo
+ exit 1
+}
+
+if $SEARCH_BROKEN ; then
+ SONAME_SEARCH="$SONAME"
+ LLIST=$LIST
+ 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:0:1} == '/' ] ; then
+ # Set to "<space>$SONAME<space>"
+ SONAME_SEARCH=" $SONAME "
+ else
+ # Set to "<tab>$SONAME<space>"
+ SONAME_SEARCH=" $SONAME "
+ fi
+ LLIST=${LIST}_$(echo "$SONAME_SEARCH$SONAME" | md5sum | head -c 8)
+ HEAD_TEXT="using $SONAME"
+ OK_TEXT="There are no dynamic links to $SONAME"
+ WORKING_TEXT=""
+fi
+
+# If any of our temporary files are older than 1 day, remove them all
+[ "$(find "${LIST%/*}/." ! -name . -prune -name "${LIST##*/}*" -type f -mmin +1440)" != "" ] && rm -f ${LIST}*
+
+# Don't use our previous files if environment doesn't match
+if [ -f $LIST.0_env ]
+then
+ PREVIOUS_SEARCH_DIRS=$(. ${LIST}.0_env; echo "$SEARCH_DIRS")
+ PREVIOUS_SEARCH_DIRS_MASK=$(. ${LIST}.0_env; echo "$SEARCH_DIRS_MASK")
+ PREVIOUS_LD_LIBRARY_MASK=$(. ${LIST}.0_env; echo "$LD_LIBRARY_MASK")
+ PREVIOUS_PORTAGE_ROOT=$(. ${LIST}.0_env; echo "$PORTAGE_ROOT")
+ PREVIOUS_OPTIONS=$(. ${LIST}.0_env; echo "$CALLED_OPTIONS")
+ if [ "$PREVIOUS_SEARCH_DIRS" != "$SEARCH_DIRS" ] || \
+ [ "$PREVIOUS_SEARCH_DIRS_MASK" != "$SEARCH_DIRS_MASK" ] || \
+ [ "$PREVIOUS_LD_LIBRARY_MASK" != "$LD_LIBRARY_MASK" ] || \
+ [ "$PREVIOUS_PORTAGE_ROOT" != "$PORTAGE_ROOT" ] || \
+ [ "$PREVIOUS_OPTIONS" != "$CALLED_OPTIONS" ]
+ then
+ echo
+ echo "Environment mismatch from previous run, deleting temporary files..."
+ rm -f ${LIST}*
+ fi
+fi
+
+# Clean up no longer needed environment variables
+unset PREVIOUS_SEARCH_DIRS PREVIOUS_SEARCH_DIRS_MASK PREVIOUS_LD_LIBRARY_MASK PREVIOUS_PORTAGE_ROOT PREVIOUS_OPTIONS
+unset PRELIMINARY_SEARCH_DIRS PRELIMINARY_SEARCH_DIRS_MASK PRELIMINARY_LD_LIBRARY_MASK PRELIMINARY_CALLED_OPTIONS
+
+# Log our environment
+echo "SEARCH_DIRS=\"$SEARCH_DIRS\"" > $LIST.0_env
+echo "SEARCH_DIRS_MASK=\"$SEARCH_DIRS_MASK\"" >> $LIST.0_env
+echo "LD_LIBRARY_MASK=\"$LD_LIBRARY_MASK\"" >> $LIST.0_env
+echo "PORTAGE_ROOT=\"$PORTAGE_ROOT\"" >> $LIST.0_env
+echo "CALLED_OPTIONS=\"$CALLED_OPTIONS\"" >> $LIST.0_env
+echo "EMERGE_OPTIONS=\"$EMERGE_OPTIONS\"" >> $LIST.0_env
+
+if $EXTRA_VERBOSE
+then
+ echo
+ echo "revdep-rebuild environment:"
+ cat $LIST.0_env
+fi
+
+echo
+echo "Checking reverse dependencies..."
+echo
+echo "Packages containing binaries and libraries $HEAD_TEXT"
+echo "will be emerged."
+
+echo
+echo -n -e "${GR}Collecting system binaries and libraries...${NO}"
+
+if [ -f $LIST.1_files ]
+then
+ echo " using existing $LIST.1_files."
+else
+ # Be safe and remove any extraneous temporary files
+ rm -f ${LIST}.[1-9]_*
+
+ set_trap "$LIST.1_*"
+
+ # Hack for the different versions of find.
+ # Be extra paranoid and pipe results through sed to remove multiple slashes
+ find_results=$(find /usr/bin/revdep-rebuild -type f -perm /u+x 2>/dev/null)
+ if [ -z $find_results ]
+ then
+ find_results=$(find /usr/bin/revdep-rebuild -type f -perm +u+x 2>/dev/null)
+ if [ -z $find_results ]
+ then
+ echo -e "\n"
+ echo -e "${RD}Unable to determine how to use find to locate executable files${NO}"
+ echo -e "${RD}Open a bug at http://bugs.gentoo.org${NO}"
+ echo
+ exit 1
+ else
+ # using -perm +u+x for find command
+ find $SEARCH_DIRS -type f \( -perm +u+x -o -name '*.so' -o -name '*.so.*' -o -name '*.la' \) 2>/dev/null | sort | uniq | sed 's:/\+:/:g' >$LIST.0_files
+ fi
+ else
+ # using -perm /u+x for find command
+ find $SEARCH_DIRS -type f \( -perm /u+x -o -name '*.so' -o -name '*.so.*' -o -name '*.la' \) 2>/dev/null | sort | uniq | sed 's:/\+:/:g' >$LIST.0_files
+ fi
+
+ # Remove files that match SEARCH_DIR_MASK
+ for dir in $SEARCH_DIRS_MASK
+ do
+ grep -v "^$dir" $LIST.0_files > $LIST.1_files
+ mv $LIST.1_files $LIST.0_files
+ done
+
+ mv $LIST.0_files $LIST.1_files
+ echo -e " done.\n ($LIST.1_files)"
+fi
+
+if $SEARCH_BROKEN && $FULL_LD_PATH ; then
+ echo
+ echo -n -e "${GR}Collecting complete LD_LIBRARY_PATH...${NO}"
+ if [ -f $LIST.2_ldpath ] ; then
+ echo " using existing $LIST.2_ldpath."
+ else
+ set_trap "$LIST.2_ldpath"
+ # Ensure that the "trusted" lib directories are at the start of the path
+ (
+ echo /lib* /usr/lib* | sed 's/ /:/g'
+ sed '/^#/d;s/#.*$//' </etc/ld.so.conf
+ sed 's:/[^/]*$::' <$LIST.1_files | sort -ru
+ ) | tr '\n' : | tr -d '\r' | sed 's/:$//' >$LIST.2_ldpath
+ echo -e " done.\n ($LIST.2_ldpath)"
+ fi
+ COMPLETE_LD_LIBRARY_PATH="$(cat $LIST.2_ldpath)"
+fi
+
+echo
+echo -n -e "${GR}Checking dynamic linking$WORKING_TEXT...${NO}"
+if [ -f $LLIST.3_rebuild ] ; then
+ echo " using existing $LLIST.3_rebuild."
+else
+ echo_v
+ set_trap "$LLIST.3_rebuild"
+ LD_MASK="\\( $(echo "$LD_LIBRARY_MASK" | sed 's/\./\\./g;s/ / \\| /g') \\)"
+ echo -n >$LLIST.3_rebuild
+ echo -n >$LLIST.3_ldd_errors
+ cat $LIST.1_files | egrep -v '*\.la$' | while read FILE ; do
+ # Note: double checking seems to be faster than single
+ # with complete path (special add ons are rare).
+ if ldd "$FILE" 2>>$LLIST.3_ldd_errors | grep -v "$LD_MASK" | $SONAME_GREP -q "$SONAME_SEARCH" ; then
+ if $SEARCH_BROKEN && $FULL_LD_PATH ; then
+ if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$FILE" 2>/dev/null | grep -v "$LD_MASK" | $SONAME_GREP -q "$SONAME_SEARCH" ; then
+ # FIX: I hate duplicating code
+ # Only build missing direct dependencies
+ ALL_MISSING_LIBS=$(ldd "$FILE" 2>/dev/null | sort -u | sed -n 's/ \(.*\) => not found/\1/p' | tr '\n' ' ' | sed 's/ $//' )
+ REQUIRED_LIBS=$(objdump -x $FILE | grep NEEDED | awk '{print $2}' | tr '\n' ' ' | sed 's/ $//')
+ MISSING_LIBS=""
+ for lib in $ALL_MISSING_LIBS
+ do
+ if echo $REQUIRED_LIBS | grep -q $lib
+ then
+ MISSING_LIBS="$MISSING_LIBS $lib"
+ fi
+ done
+ if [ "$MISSING_LIBS" != "" ]
+ then
+ echo "obj $FILE" >>$LLIST.3_rebuild
+ echo_v " broken $FILE (requires ${MISSING_LIBS})"
+ fi
+ fi
+ else
+ # FIX: I hate duplicating code
+ # Only rebuild for direct dependencies
+ ALL_MISSING_LIBS=$(ldd "$FILE" 2>/dev/null | sort -u | $SONAME_GREP "$SONAME_SEARCH" | awk '{print $1}' | tr '\n' ' ' | sed 's/ $//' )
+ REQUIRED_LIBS=$(objdump -x $FILE | grep NEEDED | awk '{print $2}' | tr '\n' ' ' | sed 's/ $//')
+ MISSING_LIBS=""
+ for lib in $ALL_MISSING_LIBS
+ do
+ if echo $REQUIRED_LIBS | grep -q $lib
+ then
+ MISSING_LIBS="$MISSING_LIBS $lib"
+ fi
+ done
+ if [ "$MISSING_LIBS" != "" ]
+ then
+ echo "obj $FILE" >>$LLIST.3_rebuild
+ if $SEARCH_BROKEN ; then
+ echo_v " broken $FILE (requires ${MISSING_LIBS})"
+ else
+ echo_v " found $FILE"
+ fi
+ fi
+ fi
+ fi
+ done
+ if $SEARCH_BROKEN ; then
+ # Look for missing version
+ for FILE in $(grep "no version information available" $LLIST.3_ldd_errors | awk '{print $NF}' | sed 's/[()]//g' | sort -u) ; do
+ echo "obj $FILE" >>$LLIST.3_rebuild
+ echo_v " broken $FILE (no version information available)"
+ done
+ # Look for broken .la files
+ cat $LIST.1_files | egrep '*\.la$' | while read FILE ; do
+ for depend in $(grep '^dependency_libs' $FILE | awk -F'=' '{print $2}' | sed "s/'//g") ; do
+ [ ${depend:0:1} != '/' ] && continue
+ if [ ! -e $depend ] ; then
+ echo "obj $FILE" >>$LLIST.3_rebuild
+ echo_v " broken $FILE (requires ${depend})"
+ fi
+ done
+ done
+ fi
+ echo -e " done.\n ($LLIST.3_rebuild)"
+fi
+
+if $PACKAGE_NAMES ; then
+ EXACT_EBUILDS=false
+
+ echo
+ echo -n -e "${GR}Assigning files to packages...${NO}"
+ if [ -f $LLIST.4_packages_raw ] ; then
+ echo " using existing $LLIST.4_packages_raw."
+ else
+ set_trap "$LLIST.4_packages*"
+ echo -n >$LLIST.4_packages_raw
+ echo -n >$LLIST.4_package_owners
+ cat $LLIST.3_rebuild | while read obj FILE ; do
+ if $PORTAGE_UTILS ; then
+ EXACT_PKG="$(qfile -qvC ${FILE} )"
+ else
+ EXACT_PKG=$(find /var/db/pkg -name CONTENTS | xargs fgrep -l "obj $FILE " | sed -e 's:/var/db/pkg/\(.*\)/CONTENTS:\1:g')
+ fi
+ # Ugly sed hack to strip version information
+ PKG="$(echo $EXACT_PKG | sed 's/-r[0-9].*$//;s/\(^.*\/*\)-.*$/\1/')"
+ if [ -z "$PKG" ] ; then
+ echo -n -e "\n ${RD}*** $FILE not owned by any package is broken! ***${NO}"
+ echo "$FILE -> (none)" >> $LLIST.4_package_owners
+ echo_v -n -e "\n $FILE -> (none)"
+ else
+ echo "$EXACT_PKG" >> $LLIST.4_packages_raw
+ echo "$FILE -> $EXACT_PKG" >> $LLIST.4_package_owners
+ echo_v -n -e "\n $FILE -> $PKG"
+ fi
+ done
+ echo_v
+ echo -e " done.\n ($LLIST.4_packages_raw, $LLIST.4_package_owners)"
+ fi
+
+ echo
+ echo -n -e "${GR}Cleaning list of packages to rebuild...${NO}"
+ if [ -f $LLIST.4_packages ] ; then
+ echo " using existing $LLIST.4_packages."
+ else
+ sort -u $LLIST.4_packages_raw >$LLIST.4_packages
+ echo -e " done.\n ($LLIST.4_packages)"
+ fi
+
+ echo
+ echo -n -e "${GR}Assigning packages to ebuilds...${NO}"
+ if [ -f $LLIST.4_ebuilds ] ; then
+ echo " using existing $LLIST.4_ebuilds."
+ else
+ if [ -s "$LLIST.4_packages" ]
+ then
+ set_trap "$LLIST.4_ebuilds"
+ cat $LLIST.4_packages | while read EXACT_PKG
+ do
+ PKG="$(echo $EXACT_PKG | sed 's/-r[0-9].*$//;s/\(^.*\/*\)-.*$/\1/')"
+ SLOT=$(cat /var/db/pkg/${EXACT_PKG}/SLOT)
+ best_visible=$(portageq best_visible $PORTAGE_ROOT ${PKG}:${SLOT})
+ [ "x" != "x$best_visible" ] && echo $best_visible
+ done > $LLIST.4_ebuilds
+ echo -e " done.\n ($LLIST.4_ebuilds)"
+ else
+ echo " Nothing to rebuild"
+ echo -n > $LLIST.4_ebuilds
+ fi
+ fi
+else
+ EXACT_EBUILDS=true
+
+ echo
+ echo -n -e "${GR}Assigning files to ebuilds...${NO}"
+ if [ -f $LLIST.4_ebuilds ] ; then
+ echo " using existing $LLIST.4_ebuilds."
+ else
+ if [ -s "$LLIST.3_rebuild" ] ; then
+ set_trap "$LLIST.4_ebuilds"
+ find /var/db/pkg -name CONTENTS | xargs fgrep -l -f $LLIST.3_rebuild |
+ sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:' > $LLIST.4_ebuilds
+ echo -e " done.\n ($LLIST.4_ebuilds)"
+ else
+ echo " Nothing to rebuild"
+ echo -n > $LLIST.4_ebuilds
+ fi
+ fi
+
+fi
+
+echo
+echo -n -e "${GR}Evaluating package order...${NO}"
+if [ -f $LLIST.5_order ] ; then
+ echo " using existing $LLIST.5_order."
+else
+ set_trap "$LLIST.5_order"
+ RAW_REBUILD_LIST="$(cat $LLIST.4_ebuilds | sed s/^/=/ | tr '\n' ' ')"
+ if [ ! -z "$RAW_REBUILD_LIST" ] ; then
+ REBUILD_GREP="^\\($( (EMERGE_DEFAULT_OPTS="" emerge --nospinner --pretend --oneshot --nodeps --quiet $RAW_REBUILD_LIST ; echo $? >$LLIST.5a_status ) | sed -n 's/\./\\&/g;s/ //g;s/$/\\/;s/\[[^]]*\]//gp' | tr '\n' '|' | sed 's/|$//'))\$"
+ if [ $(cat $LLIST.5a_status) -gt 0 ] ; then
+ echo ""
+ echo -e "${RD}Warning: Failed to resolve package order."
+ echo -e "Will merge in \"random\" order!${NO}"
+ echo "Possible reasons:"
+ echo "- An ebuild is no longer in the portage tree."
+ echo "- An ebuild is masked, use /etc/portage/packages.keyword"
+ echo " and/or /etc/portage/package.unmask to unmask it"
+ for i in . . . . . ; do
+ echo -n -e '\a.'
+ sleep 1
+ done
+ ln -f $LLIST.4_ebuilds $LLIST.5_order
+ else
+ (EMERGE_DEFAULT_OPTS="" emerge --nospinner --pretend --oneshot --deep --quiet $RAW_REBUILD_LIST ; echo $? >$LLIST.5b_status ) | sed -n 's/ *$//;s/^\[.*\] //p' | awk '{print $1}' | grep "$REBUILD_GREP" >$LLIST.5_order
+ if [ $(cat $LLIST.5b_status) -gt 0 ] ; then
+ echo ""
+ echo -e "${RD}Warning: Failed to resolve package order."
+ echo -e "Will merge in \"random\" order!${NO}"
+ echo "Possible reasons:"
+ echo "- An ebuild is no longer in the portage tree."
+ echo "- An ebuild is masked, use /etc/portage/packages.keyword"
+ echo " and/or /etc/portage/package.unmask to unmask it"
+ for i in . . . . . ; do
+ echo -n -e '\a.'
+ sleep 1
+ done
+ rm -f $LLIST.5_order
+ ln -f $LLIST.4_ebuilds $LLIST.5_order
+ fi
+ fi
+ else
+ echo -n "" >$LLIST.5_order
+ fi
+ echo -e " done.\n ($LLIST.5_order)"
+fi
+
+# Clean up no longer needed environment variables
+unset COMPLETE_LD_LIBRARY_PATH SEARCH_DIRS SEARCH_DIRS_MASK LD_LIBRARY_MASK PORTAGE_ROOT CALLED_OPTIONS
+
+REBUILD_LIST="$(cat $LLIST.5_order | sed s/^/=/ | tr '\n' ' ')"
+
+trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+
+if [ -z "$REBUILD_LIST" ] ; then
+ echo -e "\n${GR}$OK_TEXT... All done.${NO} "
+ if [ ! $KEEPTEMP ]
+ then
+ rm $LIST.[0-2]_*
+ rm $LLIST.[3-9]_*
+ fi
+ exit 0
+fi
+
+IS_REAL_MERGE=true
+echo " $EMERGE_OPTIONS " | grep -q '\( -p \| --pretend \| -f \| --fetchonly \)' && IS_REAL_MERGE=false
+
+echo
+echo -e "${GR}All prepared. Starting rebuild...${NO}"
+
+echo "emerge --oneshot $EMERGE_OPTIONS $REBUILD_LIST"
+
+if $IS_REAL_MERGE ; then
+ for i in . . . . . . . . . . ; do
+ echo -n -e '\a.'
+ sleep 1
+ done
+ echo
+fi
+
+# Link file descriptor #6 with stdin
+exec 6<&0
+
+# Run in background to correctly handle Ctrl-C
+(
+ EMERGE_DEFAULT_OPTS="" emerge --oneshot $EMERGE_OPTIONS $REBUILD_LIST <&6
+ echo $? >$LLIST.6_status
+) &
+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<&-
+
+#if $EXACT_EBUILDS ; then
+# mv -i /usr/portage/profiles/package.mask.hidden /usr/portage/profiles/package.mask
+# trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+#fi
+
+if [ "$(cat $LLIST.6_status)" -gt 0 ] ; then
+ echo
+ echo -e "${RD}revdep-rebuild failed to emerge all packages${NO}"
+ echo -e "${RD}you have the following choices:${NO}"
+ echo
+ echo "- if emerge failed during the build, fix the problems and re-run revdep-rebuild"
+ echo " or"
+ echo "- use -X or --package-names as first argument (trys to rebuild package, not exact"
+ echo " ebuild)"
+ echo " or"
+ echo "- set ACCEPT_KEYWORDS=\"~<your platform>\" and/or /etc/portage/package.unmask"
+ echo " (and remove $LLIST.5_order to be evaluated again)"
+ echo " or"
+ echo "- modify the above emerge command and run it manually"
+ echo " or"
+ echo "- compile or unmerge unsatisfied packages manually, remove temporary files and"
+ echo " try again (you can edit package/ebuild list first)"
+ echo
+ echo -e "${GR}To remove temporary files, please run:${NO}"
+ echo "rm $LIST*.?_*"
+ exit $(cat $LLIST.6_status)
+else
+ if $IS_REAL_MERGE ; then
+ trap "echo -e \" terminated. Please remove them manually:\nrm $LIST*.?_*\" ; exit 1" \
+ SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+ echo -n -e "${GR}Build finished correctly. Removing temporary files...${NO} "
+ echo
+ rm $LIST.[0-2]_*
+ rm $LLIST.[3-9]_*
+ echo "You can re-run revdep-rebuild to verify that all libraries and binaries"
+ echo "are fixed. If some inconsistency remains, it can be orphaned file, deep"
+ echo "dependency, binary package or specially evaluated library."
+ else
+ echo -e "${GR}Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.${NO}"
+ fi
+fi
+exit 0
diff --git a/src/revdep-rebuild/revdep-rebuild-sh b/src/revdep-rebuild/revdep-rebuild-sh
new file mode 100755
index 0000000..c7acdc6
--- /dev/null
+++ b/src/revdep-rebuild/revdep-rebuild-sh
@@ -0,0 +1,332 @@
+#!/bin/sh
+# Copyright 1999-2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+appname=${0##*/}
+
+# If baselayout is broken, define our own functions
+[ -r /etc/init.d/functions.sh ] && . /etc/init.d/functions.sh
+if ! type eend >/dev/null 2>&1 || ! eend 0 >/dev/null 2>&1; then
+ einfo() { echo " * $*"; }
+ eerror() { echo " * $*" >&2; return 1; }
+ eindent() { :; }
+ eoutdent() { :; }
+fi
+
+# No temporary files used, so nothing to clean up :)
+trap "export RC_EINDENT=; echo; eerror 'Caught interrupt'; exit 1" \
+ SIGINT SIGQUIT
+
+print_usage() {
+ cat << EOF
+Usage: ${appname} [OPTIONS] [--] [EMERGE_OPTIONS]
+
+Broken reverse dependency rebuilder.
+
+ -h, --help Print this usage
+ -e, --exact Emerge based on exact package version
+ -C, --nocolor Turn off colored output
+ -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)
+
+Calls emerge, all other options are used for it (e. g. -p, --pretend).
+
+Report bugs to <http://bugs.gentoo.org>
+EOF
+}
+
+# Have we linked to this library?
+elf_linked() {
+ local f=$1
+ shift
+ while [ -n "$1" ]; do
+ ldd "${f}" 2>/dev/null | grep -q "=> $1 " && return 0
+ shift
+ done
+ return 1
+}
+
+# Work out of we really need this library or not
+elf_needed() {
+ local f=$1
+ shift
+ while [ -n "$1" ]; do
+ objdump -p "${f}" 2>/dev/null | \
+ grep -vF "${ld_mask:=$'\a'}" | \
+ grep -q "^ NEEDED ${1##*/}" && return 0
+ shift
+ done
+ return 1
+}
+
+elf_broken() {
+ local lib=
+
+ for lib in $(ldd "$1" 2>/dev/null | \
+ sed -n -e 's/[[:space:]]*\(.*\) => not found.*/\1/p'); do
+ if elf_needed "$1" "${lib}"; then
+ echo "(missing ${lib})"
+ return 0
+ fi
+ done
+ return 1
+}
+
+# Check that all direct files exist in .la files
+la_broken() {
+ local x=
+ for x in $(sed -n -e "s/^dependency_libs=\'\(.*\)'\$/\1/p" "$1"); do
+ case "${x}" in
+ /*)
+ if [ ! -e "${x}" ]; then
+ echo "(missing ${x})"
+ return 0
+ fi
+ ;;
+ esac
+ done
+
+ return 1
+}
+
+# Return a $PATH style variable based on ld.so.conf
+read_so_conf() {
+ local line=
+ while read line; do
+ case "${line}" in
+ "#"*) ;;
+ *) printf ":%s" "${line}";;
+ esac
+ done < /etc/ld.so.conf
+}
+
+# Check to see if we have already scanned a dir or not
+scanned() {
+ local dir=$1 IFS=:
+ set -- ${scanned}
+
+ while [ -n "$1" ]; do
+ [ "$1" = "$dir" ] && return 0
+ shift
+ done
+
+ scanned="${scanned}${scanned:+:}${dir}"
+ return 1
+}
+
+# Hit the portage vdb to work out our ebuilds
+# If everything is 100% then this happens in one very fast pass
+# Otherwise we have to take the slow approach to inform the user which files
+# are orphans
+get_exact_ebuilds() {
+ local regex= ebuilds= x= IFS=:
+ set -- $@
+ IFS=" "
+
+ # Hit the vdb in one go - this is fast!
+ regex=$(printf "%s|" "$@")
+ regex=${regex%*|}
+ find /var/db/pkg -name CONTENTS | \
+ xargs egrep "^obj (${regex}) " | \
+ sed -e 's,/var/db/pkg/\(.*\/.*\)/CONTENTS:.*,=\1,g' | \
+ tr '\n' ' '
+}
+
+# Get our args
+libs=
+exact=false
+order=true
+while [ -n "$1" ]; do
+ case "$1" in
+ --*=*)
+ arg1=${1%%=*}
+ arg2=${1#*=}
+ shift
+ set -- ${arg1} ${arg2} $@
+ continue
+ ;;
+ -h|--help) print_usage; exit 0;;
+ -L|--library|--soname|--soname-regexp)
+ if [ -z "$2" ]; then
+ eerror "Missing expected argument to $1"
+ exit 1
+ fi
+ libs="${libs}${libs:+ }$2"
+ shift
+ ;;
+ -e|--exact) exact=true;;
+ -X|--package-names) ;; #compat
+ --) shift; emerge_opts="$@"; break;;
+ *) eerror "$0: unknown option $1"; exit 1;;
+ esac
+ shift
+done
+
+einfo "Configuring search environment for ${appname}"
+# OK, this truely sucks. Paths can have spaces in, but our config format
+# is space separated?
+sdirs=$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
+sdirs_mask=$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
+ld_mask=$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
+
+if [ -d /etc/revdep-rebuild ]; then
+ for x in /etc/revdep-rebuild/*; do
+ sdirs="${sdirs}${sdirs:+ }$(unset SEARCH_DIRS; . "${x}"; echo "${SEARCH_DIRS}")"
+ sdirs_mask="${sdirs_mask}${sdirs_mask:+ }$(unset SEARCH_DIRS_MASK; . "${x}" ; echo "${SEARCH_DIRS_MASK}")"
+ ld_mask="${ld_mask}${ld_mask:+ }$(unset LD_LIBRARY_MASK; . "${x}"; echo "${LD_LIBRARY_MASK}")"
+ done
+else
+ sdirs="${sdirs}${sdirs:+ }/bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
+ sdirs_mask="${sdirs_mask}${sdirs_mask:+ }/opt/OpenOffice /usr/lib/openoffice"
+ ld_mask="${ld_mask}${ld_mask:+ }libodbcinst.so libodbc.so libjava.so libjvm.so"
+fi
+
+sdirs=$(find ${sdirs} -type d)
+
+einfo "Starting scan"
+eindent
+# Mark our masked dirs already scanned
+scanned=
+for dir in ${sdirs_mask}; do
+ scanned "${dir}"
+done
+
+# Now scan our dirs
+for dir in ${sdirs}; do
+ scanned "${dir}" && continue
+
+ einfo "in ${dir}"
+ eindent
+ for x in "${dir}"/*; do
+ [ -d "${x}" ] && continue
+ [ -L "${x}" ] && continue
+
+ scan=true
+ process=false
+ reason=
+ case "${x}" in
+ *.so|*.so.*) process=true;;
+ *.la)
+ scan=false
+ if [ -z "${libs}" ]; then
+ reason=$(la_broken "${x}")
+ [ $? = 0 ] && process=true
+ fi
+ ;;
+ esac
+ [ -x "${x}" ] && ${scan} && process=true
+ ${process} || continue
+
+ if ${scan}; then
+ process=false
+ if [ -n "${libs}" ]; then
+ for lib in ${libs}; do
+ if [ "${lib#/}" != "${lib}" ]; then
+ # If lib starts with / then check if the exact
+ # lib is linked
+ elf_linked "${x}" "${lib}" || continue
+ fi
+ if elf_needed "${x}" ${lib}; then
+ process=true
+ break
+ fi
+ done
+ else
+ reason=$(elf_broken "${x}")
+ [ $? = 0 ] && process=true
+ fi
+ fi
+
+ ${process} || continue
+ einfo "found ${x} ${reason}"
+ files="${files}${files:+:}${x}"
+ done
+ eoutdent
+done
+eoutdent
+
+if [ -z "${files}" ]; then
+ if [ -z "${libs}" ]; then
+ einfo "Nothing found that needs rebuilding"
+ else
+ einfo "No dynamic binaries found with these libraries"
+ fi
+ exit 0
+fi
+
+einfo "Assigning files to packages"
+eindent
+ebuilds=$(get_exact_ebuilds "${files}")
+
+if [ -z "${ebuilds}" ]; then
+ eerror "No packages own these files"
+ exit 1
+fi
+
+# Work out the best visible package for the slot
+if ! ${exact}; then
+ root=$(portageq envvar ROOT)
+ root=${root:-/}
+
+ set -- ${ebuilds}
+ ebuilds=
+ for x in "$@"; do
+ x=${x#=*}
+ pkg=${x%-r[0-9]*}
+ pkg=${pkg%-*}
+ slot=$(cat "/var/db/pkg/${x}/SLOT")
+ ebd=$(portageq best_visible "${root}" "${pkg}:${slot}")
+ if [ -z "${ebd}" ]; then
+ eerror "Cannot find an ebuild visible for ${x}"
+ else
+ ebuilds="${ebuilds}${ebuilds:+ }=${ebd}"
+ fi
+ done
+fi
+eoutdent
+
+# Work out the build order
+if ${order}; then
+ einfo "Ordering packages"
+ order="$(EMERGE_DEFAULT_OPTS="" \
+ emerge --nospinner --pretend --deep --quiet ${ebuilds})"
+ if [ $? = 0 ]; then
+ ebuilds=$(echo "${order}" | \
+ sed -e 's:^\[.*\] \([^ ]*\)[ ].*$:=\1:' | \
+ grep -F "$(printf "%s\n" ${ebuilds})" | \
+ tr '\n' ' ')
+ else
+ eerror "Unable to order packages!"
+ fi
+fi
+
+if [ -z "${ebuilds}" ]; then
+ eerror "Don't know how to find which package owns what file :/"
+ exit 1
+fi
+
+echo
+einfo "About to execute"
+echo "emerge --oneshot ${emerge_opts} ${ebuilds}"
+echo
+
+i=5
+printf "in"
+while [ ${i} -gt 0 ]; do
+ printf " ${i}"
+ sleep 1
+ i=$((${i} - 1))
+done
+printf "\n\n"
+
+EMERGE_DEFAULT_OPTS="" emerge --oneshot ${emerge_opts} ${ebuilds}
+retval=$?
+
+if [ "${retval}" = 0 ]; then
+ einfo "All done"
+ exit 0
+fi
+
+eerror "There was an error trying to emerge the broken packages"
+exit "${retval}"
diff --git a/src/revdep-rebuild/revdep-rebuild.1 b/src/revdep-rebuild/revdep-rebuild.1
new file mode 100644
index 0000000..267f7f1
--- /dev/null
+++ b/src/revdep-rebuild/revdep-rebuild.1
@@ -0,0 +1,101 @@
+.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 \-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 \-u UTIL | \-\-no-util UTIL
+Do not use features provided by UTIL.
+UTIL can be one of portage-utils or pkgcore, or it can be a \fBquoted\fR space-delimited list.
+.TP
+.B \-v | \-\-verbose
+More output. (Prints the revdep\-rebuild search environment.)
+.TP
+.B All other options (including unrecognized ones) are passed to the emerge command. Single\-letter options may not be combined, so for example, \-pv is not valid. Please use \-p \-v.
+.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 "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 .revdep\-rebuild* files, your emerge \-\-info, and patches. ;)
+
+.SH "SEE ALSO"
+emerge(1), portage(5), regex(7)
diff --git a/src/useflag/AUTHORS b/src/useflag/AUTHORS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/useflag/AUTHORS
diff --git a/src/useflag/ChangeLog b/src/useflag/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/useflag/ChangeLog
diff --git a/src/useflag/README b/src/useflag/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/useflag/README
diff --git a/src/useflag/useflag b/src/useflag/useflag
new file mode 100644
index 0000000..fd4cc08
--- /dev/null
+++ b/src/useflag/useflag
@@ -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/src/useflag/useflag.1 b/src/useflag/useflag.1
new file mode 100644
index 0000000..d321861
--- /dev/null
+++ b/src/useflag/useflag.1
@@ -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.
+