commit:     81c73cae27a5602fbe4134e47de2663af85f1e99
Author:     Brian Dolbec <dolsen <AT> gentoo <DOT> org>
AuthorDate: Wed Mar 22 11:29:12 2017 +0000
Commit:     Brian Dolbec <dolsen <AT> gentoo <DOT> org>
CommitDate: Wed Mar 22 11:35:11 2017 +0000
URL:        https://gitweb.gentoo.org/proj/gentoolkit.git/commit/?id=81c73cae

Initial import of remaining gentoolkit-dev packages

Unable to merge due to confilts in history, removed gnetoolkit paths in 
gentoolkit-dev...
So do a basic new files commit instead.

For the previous history of the different packages, refer to the gentoolkit-dev 
branch.

 AUTHORS.gentoolkit-dev                             |   6 +
 ebump/AUTHORS                                      |   4 +
 ebump/ChangeLog                                    |   8 +
 ebump/Makefile                                     |  20 +
 ebump/README                                       |  18 +
 ebump/ebump                                        | 389 +++++++++++++++
 ebump/ebump.1                                      | 126 +++++
 pym/gentoolkit/ekeyword/.pylintrc                  |  36 ++
 pym/gentoolkit/ekeyword/AUTHORS                    |  10 +
 pym/gentoolkit/ekeyword/Makefile                   |  20 +
 pym/gentoolkit/ekeyword/README                     |  20 +
 pym/gentoolkit/ekeyword/ekeyword                   |   1 +
 pym/gentoolkit/ekeyword/ekeyword.py                | 538 +++++++++++++++++++++
 pym/gentoolkit/ekeyword/ekeyword_unittest.py       | 424 ++++++++++++++++
 pym/gentoolkit/ekeyword/pylint                     |  49 ++
 pym/gentoolkit/ekeyword/pytest.ini                 |   3 +
 pym/gentoolkit/ekeyword/tests/process-1.ebuild     |   5 +
 .../tests/profiles/arch-only/profiles/arch.list    |   1 +
 .../tests/profiles/both/profiles/arch.list         |  45 ++
 .../tests/profiles/both/profiles/profiles.desc     | 295 +++++++++++
 .../ekeyword/tests/profiles/none/profiles/.keep    |   0
 .../profiles/profiles-only/profiles/profiles.desc  |   1 +
 pym/gentoolkit/imlate/Makefile                     |  18 +
 pym/gentoolkit/imlate/imlate                       | 480 ++++++++++++++++++
 pym/gentoolkit/imlate/imlate.1                     |  48 ++
 25 files changed, 2565 insertions(+)

diff --git a/AUTHORS.gentoolkit-dev b/AUTHORS.gentoolkit-dev
new file mode 100644
index 0000000..ca985ba
--- /dev/null
+++ b/AUTHORS.gentoolkit-dev
@@ -0,0 +1,6 @@
+Christian Ruppert <id...@gentoo.org>
+Paul Varner <fuzzy...@gentoo.org>
+Karl Trygve Kalleberg <kar...@gentoo.org>
+
+See the AUTHOR file in the various src/<foo> subdirectories for a full
+log of who's done what with whome and when.

diff --git a/ebump/AUTHORS b/ebump/AUTHORS
new file mode 100644
index 0000000..0cf8ad8
--- /dev/null
+++ b/ebump/AUTHORS
@@ -0,0 +1,4 @@
+Christian Ruppert <id...@gentoo.org>
+
+Original author:
+Karl Trygve Kalleberg <kar...@gentoo.org>

diff --git a/ebump/ChangeLog b/ebump/ChangeLog
new file mode 100644
index 0000000..4434b94
--- /dev/null
+++ b/ebump/ChangeLog
@@ -0,0 +1,8 @@
+2004-06-21 Karl Trygve Kalleberg <kar...@gentoo.org>
+       * Fixed handling of deletion.
+
+2004-03-11 Karl Trygve Kalleberg <kar...@gentoo.org>
+       * Fixed incorrect cut'ing of wc -l output when updating ChangeLog
+       
+2004-02-08 Karl Trygve Kalleberg <kar...@gentoo.org>
+       * Initial import

diff --git a/ebump/Makefile b/ebump/Makefile
new file mode 100644
index 0000000..61afab3
--- /dev/null
+++ b/ebump/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2004 Karl Trygve Kalleberg <kar...@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 ChangeLog ebump ebump.1 
../../$(DISTDIR)/src/ebump/
+
+install: all
+       install -m 0755 ebump $(BINDIR)/
+       install -d $(DOCDIR)/ebump
+       install -m 0644 AUTHORS README ChangeLog $(DOCDIR)/ebump/
+       install -m 0644 ebump.1 $(MAN1DIR)/

diff --git a/ebump/README b/ebump/README
new file mode 100644
index 0000000..f13592e
--- /dev/null
+++ b/ebump/README
@@ -0,0 +1,18 @@
+
+Package : ebump
+Version : 0.1.1
+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/ebump/ebump b/ebump/ebump
new file mode 100755
index 0000000..47ffd86
--- /dev/null
+++ b/ebump/ebump
@@ -0,0 +1,389 @@
+#! /bin/sh
+# Copyright (c) 2004 Karl Trygve Kalleberg <kar...@gentoo.org>
+# Copyright 1999-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+__version__="0.1.1"
+__author__="Karl Trygve Kalleberg"
+__email__="<kar...@gentoo.org>"
+__description__="Ebuild version bumping tool"
+
+
+
+die() {
+       echo $1 >&2
+       exit -1
+}
+
+einfo() {
+       if [ ${opt_verbosity:-0} -eq 1 ] ; then
+               echo $*
+       fi
+}
+
+print_version() {
+       echo "${__description__}, v${__version__}"
+       echo "Copyright (c) 2004 ${__author__} ${__email__}"
+       echo "Copyright 1999-2010 Gentoo Foundation"
+       echo "Distributed under the terms of the GNU General Public License v2"
+}
+
+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 "  -a|--no-auxfiles       don't bump auxfiles (files/*)"
+       echo "  -c|--no-changelog      do not update ChangeLog (via echangelog)"
+       echo "  -C|--no-vcs            do not add to VCS"
+       echo "  -m|--message           append message to ChangeLog"
+       echo "  -d|--delete-old        delete previous revision from VCS 
(DANGEROUS!)"
+}
+
+#
+# Load options from /etc/gentoolkit/ebump.conf and ${HOME}/.gentoo/ebump.conf
+# Home directory file takes precedence.
+#
+load_options() {
+       # FIXME: Sourcing config files like this is really a bad idea; users may
+       # easily override any function in this program inside his config files.
+       if [ -f "/etc/gentoolkit/ebump.conf" ] ; then
+               . /etc/gentoolkit/ebump.conf
+       fi
+       if [ -f "${HOME}/.gentoo/gentool-env" ] ; then
+               . ${HOME}/.gentoo/gentool-env
+       fi
+       if [ -f "${HOME}/.gentoo/ebump.conf" ] ; then
+               . ${HOME}/.gentoo/ebump.conf
+       fi
+
+       # FIXME: remove this warning in 2-3 releases.
+       if [ -n "${opt_add_cvs}" ]; then
+               echo "Warning: opt_add_cvs is deprecated, please use 
opt_add_vcs from now on!" >&2
+       fi
+}
+
+#
+# Find closes ebuild to ${1}, if any
+#
+find_ebuild() {
+       local f=${1}
+
+       if [ -f "${f}" ] ; then
+               echo ${f}
+       fi
+
+       if [ -f "${f}.ebuild" ] ; then
+               echo ${f}
+       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() {
+       local vcs=$1
+       shift
+       local ebuild_arg="${*}"
+       shift $#
+
+       # Files to add to VCS
+       local addfiles=""
+       # Files to remove from VCS
+       local delfiles=""
+
+       if [ -z "${ebuild_arg}" ] ; then
+               print_usage
+               exit
+       fi
+
+       for ebuild in $ebuild_arg; do
+               #
+               # Try to find a matching ebuild
+               #
+               local ebuild_name=$(find_ebuild ${ebuild})
+               if [ -z "${ebuild_name}" ] ; then
+                       die "Could not find ${ebuild}"
+               fi
+
+               einfo "Processing ebuild ${ebuild_name}"
+
+               #
+               # Bump revision suffix (or add one)
+               #
+               local PF=$(basename ${ebuild_name} .ebuild)
+               local PN=$(splitname name ${PF})
+               local PV=$(splitname version ${PF})
+               local rev=$(splitname revision ${PF})
+               local PV_norev=$(splitname vernorev ${PF})
+               local newPF=${PN}-${PV_norev}-r$((rev+1))
+
+#              echo $PF / $PN / $PV / $rev / $PV_norev / $newPF
+
+               einfo "Bumped ${PF}.ebuild to ${newPF}.ebuild"
+
+               if [ "${vcs}" = "svn" ]; then
+                       svn cp ${PF}.ebuild ${newPF}.ebuild
+               else
+                       cp ${PF}.ebuild ${newPF}.ebuild
+               fi
+
+               einfo "Reset keywords to ~arch"
+
+               ekeyword '~all' "${newPF}.ebuild"
+
+               addfiles="${addfiles} ${newPF}.ebuild"
+               delfiles="${delfiles} ${PF}.ebuild"
+
+               #
+               # (Optional) Bump relevant files in files/
+               #
+               if [ "${opt_bump_auxfiles}" = "y" ] ; then
+                       # Gather list of auxiliary files in files/ that has a 
versioned
+                       # filename, where the version matches our current 
version.
+                       local 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
+                               local bn=$(basename ${x})
+                               local dn=$(dirname ${x})
+                               local newbn
+
+                               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" >&2
+                                       else
+                                               cp -a ${dn}/${bn} ${dn}/${newbn}
+                                               # uhm, is that necessary?
+#                                              find ${dn}/${newbn} -name CVS | 
xargs rm -rf
+                                       fi
+                               else
+                                       cp ${dn}/${bn} ${dn}/${newbn}
+                               fi
+
+                               addfiles="${addfiles} ${dn}/${newbn}"
+                               delfiles="${delfiles} ${dn}/${bn}"
+
+                               einfo "Bumped ${dn}/${bn} to ${dn}/${newbn}"
+                       done
+               fi
+       done
+
+#      echo "addfiles ${addfiles}"
+#      echo "delfiles ${delfiles}"
+
+       #
+       # (Optional) Add VCS entry for all new files
+       #
+       if [ "${opt_add_vcs}" = "y" ] ; then
+#              for x in ${addfiles} ; do
+#                      if [ -d ${x} ] ; then
+#                              find ${x} -exec ${vcs} add {} ';'
+#                      else
+#                              ${vcs} add ${x}
+#                      fi
+#              done
+               $vcs add $addfiles
+               einfo "Added ${addfiles} to VCS"
+       fi
+
+
+       #
+       # (Optional) Delete previous entry
+       #
+       # Could we use 'rm' instead of remove for all vcs?
+       if [ "${opt_delete_old}" = "y" ] ; then
+#              for x in ${delfiles} ; do
+#                      if [ "${vcs}" = "cvs" ]; then
+#                              ${vcs} remove -f ${x}
+#                      elif [ "${vcs}" = "git" ]; then
+#                              ${vcs} rm ${x}
+#                      else
+#                              ${vcs} remove ${x}
+#                      fi
+#              done
+               if [ "${vcs}" = "cvs" ]; then
+                       $vcs remove -f $delfiles
+               elif [ "${vcs}" = "git" ]; then
+                       $vcs rm $delfiles
+               else
+                       $vcs remove $delfiles
+               fi
+               einfo "Removed ${delfiles} from VCS"
+       fi
+
+       #
+       # (Optional) Add ChangeLog entry
+       #
+       if [ "${opt_add_changelog}" = "y" ] && [ "${opt_add_vcs}" = "y" ]; then
+               # FIXME: remove this warning in 2-3 releases
+               if [ -n "${AUTHORNAME}" ] || [ -n "${AUTHOREMAIL}" ]; then
+                       echo "Warning: AUTHORNAME and AUTHOREMAIL is 
deprecated!" >&2
+                       echo "Please take a look at echangelog(1)." >&2
+                       echo "To avoid this warning unset AUTHORNAME and 
AUTHOREMAIL." >&2
+               fi
+
+               echangelog "${opt_commitmessage}" || set $?
+
+               if [ ${1:-0} -ne 0 ]; then
+                       einfo "Modifying ChangeLog failed!"
+               else
+                       einfo "Added ChangeLog entry"
+               fi
+       fi
+}
+
+get_vcs() {
+       if [ -d "CVS" ]; then
+               echo "cvs"
+               return 0
+       elif [ -d ".svn" ]; then
+               echo "svn"
+               return 0
+       else
+               if [ -x "$(which git)" ]; then
+                       if [ -n "$(git rev-parse --git-dir 2>/dev/null)" ]; then
+                               echo "git"
+                               return 0
+                       fi
+               fi
+
+               echo
+               return 1
+       fi
+}
+
+#
+# Global options
+#
+opt_verbosity=0
+opt_add_changelog=y
+opt_add_vcs=y
+opt_bump_auxfiles=y
+opt_delete_old=n
+opt_commitmessage=""
+
+load_options
+
+while [ ${#} -gt 0 ] ; do
+       arg=${1}
+       shift
+
+       case ${arg} in
+               -h|--help)
+                       print_usage
+                       exit 0
+               ;;
+               -m|--message)
+                       opt_commitmessage="${1}"
+                       shift
+                       continue
+               ;;
+               -a|--no-auxfiles)
+                       opt_bump_auxfiles=n
+                       continue
+               ;;
+               -c|--no-changelog)
+                       opt_add_changelog=n
+                       continue
+               ;;
+               -C|--no-vcs)
+                       opt_add_vcs=n
+                       continue
+               ;;
+               -V|--version)
+                       print_version
+                       exit
+               ;;
+               -v|--verbose)
+                       opt_verbosity=1
+                       continue
+               ;;
+               -q|--quiet)
+                       opt_verbosity=0
+                       continue
+               ;;
+               -d|--delete-old)
+                       opt_delete_old=y
+                       continue
+               ;;
+               *)
+                       ebuild_arg="${ebuild_arg:+${ebuild_arg} }${arg}"
+                       continue
+               ;;
+       esac
+done
+
+vcs=$(get_vcs)
+if [ -z "${vcs}" ]; then
+       echo "Warning: no cvs, git or svn repository found!" >&2
+       echo "Changes can't be added to the VCS" >&2
+       opt_add_vcs=n
+       opt_delete_old=n
+fi
+
+process_ebuild "${vcs}" $ebuild_arg
+
+# TODO:
+# - put cli parser into separate functions

diff --git a/ebump/ebump.1 b/ebump/ebump.1
new file mode 100644
index 0000000..b1e473d
--- /dev/null
+++ b/ebump/ebump.1
@@ -0,0 +1,126 @@
+.TH "ebump" "1" "0.1.1" "Gentoolkit" "Gentoo Administration"
+.SH "NAME"
+.LP 
+ebump \- Gentoo: Ebuild revision bumper
+.SH "SYNTAX"
+.LP 
+ebump [\fIoption\fP] <\fIpackage-name[-version]\fP>
+
+.SH "DESCRIPTION" 
+
+.LP
+\fIebump\fR bumps the revision of a particular ebuild, and all auxiliary
+files in the files/ directory that have a matching version suffix.
+
+.LP
+By default, the all new revision files will be added to the VCS.
+
+.LP 
+You must stand in the directory of the ebuild to be bumped.
+
+.SH "OPTIONS"
+.LP 
+\fB\-C\fR
+.br
+\fB--no-vcs\fB
+.IP 
+Do not add new files to VCS.
+
+.LP 
+\fB\-V\fR
+.br
+\fB--version\fB
+.IP 
+Display version information and exit.
+
+.LP 
+\fB\-v\fR
+.br
+\fB--verbose\fB
+.IP 
+Increase verbosity level. May be used more than once.
+
+.LP 
+\fB\-q\fR
+.br
+\fB--quiet\fB
+.IP 
+Do not output any non-essential information.
+
+.LP
+\fB\-a\fR
+.br
+\fB--no-auxfiles\fB
+.IP
+don't bump auxfiles (files/*)
+
+.LP
+\fB\-c\fR
+.br
+\fB--no-changelog\fB
+.IP
+do not update ChangeLog (via echangelog)
+
+.LP 
+\fB\-m\fR <\fIChangeLog text\fR>
+.br 
+\fB\--message\fR <\fIChangeLog text\fR>
+.IP 
+Specifies the message to add to the ChangeLog, instead of the standard
+placeholder.
+
+.LP 
+\fB\-d\fR
+.br
+\fB\--delete-old\fR
+.IP 
+Delete old revision and old auxiliary files from VCS. This is
+\fIdangerous\fR and should only be used if you know exactly what you are
+doing, because
+.br
+1) the old revision may be stable on a different architecture than the one you
+are working on.
+.br
+2) the auxiliary files may be required by other versions of the ebuild.
+.br
+3) the new revision should usually undergo a period of testing before being 
marked stable.
+
+.SH "CONFIGURATION"
+
+.LP
+\fB/etc/gentoolkit/ebump.conf\fR
+.br
+\fB~/.gentoo/ebump.conf\fR
+.IP
+From these files, \fIebump\fR will load the settings
+.br
+\fBopt_verbosity\fR (default \fI1\fR) - verbosity level 0-10
+.br
+\fBopt_add_changelog\fR (default \fIy\fR) - add entry in ChangeLog
+.br
+\fBopt_add_vcs\fR (default \fIy\fR) - add new files to VCS
+.br
+\fBopt_bump_auxfiles\fR (default \fIy\fR) - bump auxiliary files in files/
+.br
+\fBopt_delete_old\fR (default \fIn\fR) - delete old revision (DANGEROUS!)
+.br
+\fBopt_commitmessage\fR (default \fI""\fR) - default ChangeLog message
+
+.LP
+\fB(DEPRECATED)\fR
+.br
+\fB~/.gentoo/gentool-env\fR
+.IR
+From this file, \fIebump\fR will load the env vars \fBAUTHORNAME\fR and 
+\fBAUTHOREMAIL\fR, which are used to generate proper ChangeLog entries.
+
+.SH "SEE ALSO"
+.LP 
+The rest of the utilities in \fIapp-portage/gentoolkit-dev\fR, such as
+\fIechangelog(1)\fR and \fIekeyword(1)\fR.
+
+.SH "AUTHORS"
+.LP 
+Karl Trygve Kalleberg <kar...@gentoo.org>
+.br
+Christian Ruppert <id...@gentoo.org>

diff --git a/pym/gentoolkit/ekeyword/.pylintrc 
b/pym/gentoolkit/ekeyword/.pylintrc
new file mode 100644
index 0000000..cd5b31e
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/.pylintrc
@@ -0,0 +1,36 @@
+[MESSAGES CONTROL]
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifier separated by comma (,) or put this option
+# multiple times (only on the command line, not in the configuration file where
+# it should appear only once).
+disable=
+       missing-docstring,
+       too-many-lines,
+       too-many-branches,
+       too-many-statements,
+       too-few-public-methods,
+       too-many-instance-attributes,
+       too-many-public-methods,
+       too-many-locals,
+       too-many-arguments,
+       locally-enabled,
+       locally-disabled,
+       fixme,
+       bad-continuation,
+       invalid-name,
+
+[REPORTS]
+reports=no
+
+[FORMAT]
+max-line-length=80
+indent-string='\t'
+
+[SIMILARITIES]
+min-similarity-lines=20
+
+[VARIABLES]
+dummy-variables-rgx=_
+
+[DESIGN]
+max-parents=10

diff --git a/pym/gentoolkit/ekeyword/AUTHORS b/pym/gentoolkit/ekeyword/AUTHORS
new file mode 100644
index 0000000..353a45e
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/AUTHORS
@@ -0,0 +1,10 @@
+Current python version:
+       Mike Frysinger <vap...@gentoo.org>
+
+Previous perl version:
+       Christian Ruppert <id...@gentoo.org>
+       Paul Varner <fuzzy...@gentoo.org>
+       Mike Frysinger <vap...@gentoo.org>
+
+Original author:
+       Aron Griffis <agrif...@gentoo.org>

diff --git a/pym/gentoolkit/ekeyword/Makefile b/pym/gentoolkit/ekeyword/Makefile
new file mode 100644
index 0000000..cfa1a06
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2004 Karl Trygve Kalleberg <kar...@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+.PHONY: all clean dist
+all:
+
+dist:
+       mkdir -p ../../$(DISTDIR)/src/ekeyword
+       cp Makefile AUTHORS README ekeyword.py ekeyword_unittest.py \
+               ../../$(DISTDIR)/src/ekeyword/
+
+install: all
+       install -m 0755 ekeyword.py $(BINDIR)/ekeyword
+       install -d $(DOCDIR)/ekeyword
+       install -m 0644 AUTHORS README $(DOCDIR)/ekeyword/

diff --git a/pym/gentoolkit/ekeyword/README b/pym/gentoolkit/ekeyword/README
new file mode 100644
index 0000000..b147e4a
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/README
@@ -0,0 +1,20 @@
+Package : ekeyword
+Version : 1.0
+Author  : See AUTHORS
+
+MOTIVATION
+
+Update the KEYWORDS in an ebuild.
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+- Should we allow users to pass in */-*/~*?
+- Should we collapse multiple globs into one.
+- Should we support multiline KEYWORDS values?  No...
+- Support autodetection of ~user homedir expansions.
+  e.g. If "arm" is a user, then "~arm" will be passed in as "/home/arm".
+       We should catch that and normalize it back to "~arm".

diff --git a/pym/gentoolkit/ekeyword/ekeyword b/pym/gentoolkit/ekeyword/ekeyword
new file mode 120000
index 0000000..8374306
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/ekeyword
@@ -0,0 +1 @@
+ekeyword.py
\ No newline at end of file

diff --git a/pym/gentoolkit/ekeyword/ekeyword.py 
b/pym/gentoolkit/ekeyword/ekeyword.py
new file mode 100755
index 0000000..31225b0
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/ekeyword.py
@@ -0,0 +1,538 @@
+#!/usr/bin/python
+# Copyright 2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# Written by Mike Frysinger <vap...@gentoo.org>
+
+"""Manage KEYWORDS in ebuilds easily.
+
+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.
+
+Examples:
+
+  # Mark all existing arches in the ebuild as stable.
+  $ %(prog)s all foo-1.ebuild
+
+  # Mark arm as stable and x86 as unstable.
+  $ %(prog)s arm ~x86 foo-1.ebuild
+
+  # Mark hppa as unsupported (explicitly adds -hppa).
+  $ %(prog)s -hppa foo-1.ebuild
+
+  # Delete alpha keywords from all ebuilds.
+  $ %(prog)s ^alpha *.ebuild
+
+  # Mark sparc as stable for foo-1 and m68k as unstable for foo-2.
+  $ %(prog)s sparc foo-1.ebuild ~m68k foo-2.ebuild
+
+  # Mark s390 as the same state as amd64.
+  $ %(prog)s s390=amd64 foo-1.ebuild
+"""
+
+from __future__ import print_function
+
+import argparse
+import collections
+import difflib
+import io
+import os
+import re
+import subprocess
+import sys
+
+import portage
+from portage.output import colorize, nocolor
+
+
+VERSION = '1.0 awesome'
+
+# Operation object that describes how to perform a change.
+# Args:
+#  op: The operation to perform when |ref_arch| is not set:
+#      None: Mark |arch| stable
+#      '-': Mark |arch| as not applicable (e.g. -foo)
+#      '~': Mark |arch| as unstable (e.g. ~foo)
+#      '^': Delete |arch| so it isn't listed at all
+#  arch: The required arch to update
+#  ref_arch: Set |arch| status to this arch (ignoring |op|)
+Op = collections.namedtuple('Op', ('op', 'arch', 'ref_arch'))
+
+
+def warning(msg):
+       """Write |msg| as a warning to stderr"""
+       print('warning: %s' % msg, file=sys.stderr)
+
+
+def keyword_to_arch(keyword):
+       """Given a keyword, strip it down to its arch value
+
+       When an ARCH shows up in KEYWORDS, it may have prefixes like ~ or -.
+       Strip all that cruft off to get back to the ARCH.
+       """
+       return keyword.lstrip('-~')
+
+
+def sort_keywords(arches):
+       """Sort |arches| list in the order developers expect
+
+       This is vaguely defined because it is kind of vaguely defined once you 
get
+       past the basic (Linux-only) keywords.
+
+       Args:
+         arches: An iterable of ARCH values.
+
+       Returns:
+         A sorted list of |arches|
+       """
+       keywords = []
+
+       # Globs always come first.
+       for g in ('-*', '*', '~*'):
+               if g in arches:
+                       arches.remove(g)
+                       keywords.append(g)
+
+       def arch_key(keyword):
+               """Callback for python sorting functions
+
+               Used to turn a Gentoo keyword into a sortable form.
+               """
+               # Sort independent of leading marker (~ or -).
+               arch = keyword_to_arch(keyword)
+
+               # A keyword may have a "-" in it.  We split on that and sort
+               # by the two resulting items.  The part after the hyphen is
+               # the primary key.
+               if '-' in arch:
+                       arch, plat = arch.split('-', 1)
+               else:
+                       arch, plat = arch, ''
+
+               return (plat, arch)
+
+       keywords += sorted(arches, key=arch_key)
+
+       return keywords
+
+
+def diff_keywords(old_keywords, new_keywords, style='color-inline'):
+       """Show pretty diff between list of keywords
+
+       Args:
+         old_keywords: The old set of KEYWORDS
+         new_keywords: The new set of KEYWORDS
+         style: The diff style
+
+       Returns:
+         A string containing the diff output ready to shown to the user
+       """
+       def show_diff(s):
+               output = ''
+
+               for tag, i0, i1, j0, j1 in s.get_opcodes():
+
+                       if tag == 'equal':
+                               output += s.a[i0:i1]
+
+                       if tag in ('delete', 'replace'):
+                               o = s.a[i0:i1]
+                               if style == 'color-inline':
+                                       o = colorize('bg_darkred', o)
+                               else:
+                                       o = '-{%s}' % o
+                               output += o
+
+                       if tag in ('insert', 'replace'):
+                               o = s.b[j0:j1]
+                               if style == 'color-inline':
+                                       o = colorize('bg_darkgreen', o)
+                               else:
+                                       o = '+{%s}' % o
+                               output += o
+
+               return output
+
+       sold = str(' '.join(old_keywords))
+       snew = str(' '.join(new_keywords))
+       s = difflib.SequenceMatcher(str.isspace, sold, snew, autojunk=False)
+       return show_diff(s)
+
+
+def process_keywords(keywords, ops, arch_status=None):
+       """Process |ops| for |keywords|"""
+       new_keywords = set(keywords).copy()
+
+       # Process each op one at a time.
+       for op, oarch, refarch in ops:
+               # Figure out which keywords we need to modify.
+               if oarch == 'all':
+                       if arch_status is None:
+                               raise ValueError('unable to process "all" w/out 
profiles.desc')
+                       old_arches = set([keyword_to_arch(a) for a in 
new_keywords])
+                       if op is None:
+                               # Process just stable keywords.
+                               arches = [k for k, v in arch_status.items()
+                                         if v == 'stable' and k in old_arches]
+                       else:
+                               # Process all possible keywords.  We use the 
arch_status as a
+                               # master list.  If it lacks some keywords, then 
we might miss
+                               # somethings here, but not much we can do.
+                               arches = list(old_arches)
+
+                       # We ignore the glob arch as we never want to tweak it.
+                       if '*' in arches:
+                               arches.remove('*')
+
+                       # For keywords that are explicitly disabled, do not 
update.  When
+                       # people use `ekeyword ~all ...` or `ekeyword all ...`, 
they rarely
+                       # (if ever) want to change a '-sparc' to 'sparc' or 
'-sparc' to
+                       # '~sparc'.  We force people to explicitly do `ekeyword 
sparc ...`
+                       # in these cases.
+                       arches = [x for x in arches if '-' + x not in 
new_keywords]
+               else:
+                       arches = [oarch]
+
+               if refarch:
+                       # Figure out the state for this arch based on the 
reference arch.
+                       # TODO: Add support for "all" keywords.
+                       # XXX: Should this ignore the '-' state ?  Does it make 
sense to
+                       #      sync e.g. "s390" to "-ppc" ?
+                       refkeyword = [x for x in new_keywords if refarch == 
keyword_to_arch(x)]
+                       if not refkeyword:
+                               op = '^'
+                       elif refkeyword[0].startswith('~'):
+                               op = '~'
+                       elif refkeyword[0].startswith('-'):
+                               op = '-'
+                       else:
+                               op = None
+
+               # Finally do the actual update of the keywords list.
+               for arch in arches:
+                       new_keywords -= set(['%s%s' % (x, arch) for x in ('', 
'~', '-')])
+
+                       if op is None:
+                               new_keywords.add(arch)
+                       elif op in ('~', '-'):
+                               new_keywords.add('%s%s' % (op, arch))
+                       elif op == '^':
+                               # Already deleted.  Whee.
+                               pass
+                       else:
+                               raise ValueError('unknown operation %s' % op)
+
+       return new_keywords
+
+
+def process_content(ebuild, data, ops, arch_status=None, verbose=0,
+                    quiet=0, style='color-inline'):
+       """Process |ops| for |data|"""
+       # Set up the user display style based on verbose/quiet settings.
+       if verbose > 1:
+               disp_name = ebuild
+               def logit(msg):
+                       print('%s: %s' % (disp_name, msg))
+       elif quiet > 1:
+               def logit(_msg):
+                       pass
+       else:
+               # Chop the full path and the .ebuild suffix.
+               disp_name = os.path.basename(ebuild)[:-7]
+               def logit(msg):
+                       print('%s: %s' % (disp_name, msg))
+
+       # Match any KEYWORDS= entry that isn't commented out.
+       keywords_re = re.compile(r'^([^#]*\bKEYWORDS=)([\'"])(.*)(\2)(.*)')
+       updated = False
+       content = []
+
+       # Walk each line of the ebuild looking for KEYWORDS to process.
+       for line in data:
+               m = keywords_re.match(line)
+               if not m:
+                       content.append(line)
+                       continue
+
+               # Ok, we've got it, now let's process things.
+               old_keywords = set(m.group(3).split())
+               new_keywords = process_keywords(
+                       old_keywords, ops, arch_status=arch_status)
+
+               # Finally let's present the results to the user.
+               if (new_keywords != old_keywords) or verbose:
+                       # Only do the diff work if something actually changed.
+                       updated = True
+                       old_keywords = sort_keywords(old_keywords)
+                       new_keywords = sort_keywords(new_keywords)
+                       line = '%s"%s"%s\n' % (m.group(1), ' 
'.join(new_keywords),
+                                              m.group(5))
+                       if style in ('color-inline', 'inline'):
+                               logit(diff_keywords(old_keywords, new_keywords, 
style=style))
+                       else:
+                               if style == 'long-multi':
+                                       logit(' '.join(['%*s' % 
(len(keyword_to_arch(x)) + 1, x)
+                                                       for x in old_keywords]))
+                                       logit(' '.join(['%*s' % 
(len(keyword_to_arch(x)) + 1, x)
+                                                       for x in new_keywords]))
+                               else:
+                                       deleted_keywords = [x for x in 
old_keywords
+                                                           if x not in 
new_keywords]
+                                       logit('--- %s' % ' 
'.join(deleted_keywords))
+                                       added_keywords = [x for x in 
new_keywords
+                                                         if x not in 
old_keywords]
+                                       logit('+++ %s' % ' 
'.join(added_keywords))
+
+               content.append(line)
+
+       if not updated:
+               logit('no updates')
+
+       return updated, content
+
+
+def process_ebuild(ebuild, ops, arch_status=None, verbose=0, quiet=0,
+                   dry_run=False, style='color-inline', manifest=False):
+       """Process |ops| for |ebuild|
+
+       Args:
+         ebuild: The ebuild file to operate on & update in place
+         ops: An iterable of operations (Op objects) to perform on |ebuild|
+         arch_status: A dict mapping default arches to their stability; see the
+                      load_profile_data function for more details
+         verbose: Be verbose; show various status messages
+         quiet: Be quiet; only show errors
+         dry_run: Do not make any changes to |ebuild|; show what would be done
+         style: The diff style
+
+       Returns:
+         Whether any updates were processed
+       """
+       with io.open(ebuild, encoding='utf8') as f:
+               updated, content = process_content(
+                       ebuild, f, ops, arch_status=arch_status,
+                       verbose=verbose, quiet=quiet, style=style)
+               if updated and not dry_run:
+                       with io.open(ebuild, 'w', encoding='utf8') as f:
+                               f.writelines(content)
+                       if manifest:
+                               subprocess.check_call(['ebuild', ebuild, 
'manifest'])
+       return updated
+
+
+def portage_settings():
+       """Return the portage settings we care about."""
+       # Portage creates the db member on the fly which confuses the linter.
+       # pylint: disable=no-member
+       return portage.db['/']['vartree'].settings
+
+
+def load_profile_data(portdir=None, repo='gentoo'):
+       """Load the list of known arches from the tree
+
+       Args:
+         portdir: The repository to load all data from (and ignore |repo|)
+         repo: Look up this repository by name to locate profile data
+
+       Returns:
+         A dict mapping the keyword to its preferred state:
+         {'x86': 'stable', 'mips': 'dev', ...}
+       """
+       if portdir is None:
+               portdir = portage_settings().repositories[repo].location
+
+       arch_status = {}
+
+       try:
+               arch_list = os.path.join(portdir, 'profiles', 'arch.list')
+               with open(arch_list) as f:
+                       for line in f:
+                               line = line.split('#', 1)[0].strip()
+                               if line:
+                                       arch_status[line] = None
+       except IOError:
+               pass
+
+       try:
+               profile_status = {
+                       'stable': 0,
+                       'dev': 1,
+                       'exp': 2,
+                       None: 3,
+               }
+               profiles_list = os.path.join(portdir, 'profiles', 
'profiles.desc')
+               with open(profiles_list) as f:
+                       for line in f:
+                               line = line.split('#', 1)[0].split()
+                               if line:
+                                       arch, _profile, status = line
+                                       arch_status.setdefault(arch, status)
+                                       curr_status = 
profile_status[arch_status[arch]]
+                                       new_status = profile_status[status]
+                                       if new_status < curr_status:
+                                               arch_status[arch] = status
+       except IOError:
+               pass
+
+       if arch_status:
+               arch_status['all'] = None
+       else:
+               warning('could not read profile files: %s' % arch_list)
+               warning('will not be able to verify args are correct')
+
+       return arch_status
+
+
+def arg_to_op(arg):
+       """Convert a command line |arg| to an Op"""
+       arch_prefixes = ('-', '~', '^')
+
+       op = None
+       arch = arg
+       refarch = None
+
+       if arg and arg[0] in arch_prefixes:
+               op, arch = arg[0], arg[1:]
+
+       if '=' in arch:
+               if not op is None:
+                       raise ValueError('Cannot use an op and a refarch')
+               arch, refarch = arch.split('=', 1)
+
+       return Op(op, arch, refarch)
+
+
+def ignorable_arg(arg, quiet=0):
+       """Whether it's ok to ignore this argument"""
+       if os.path.isdir(arg):
+               if not quiet:
+                       warning('ignoring directory %s' % arg)
+               return True
+
+       WHITELIST = (
+               'Manifest',
+               'metadata.xml',
+       )
+       base = os.path.basename(arg)
+       if (base.startswith('ChangeLog') or
+           base in WHITELIST or
+           base.startswith('.') or
+           base.endswith('~')):
+               if not quiet:
+                       warning('ignoring file: %s' % arg)
+               return True
+
+       return False
+
+
+def args_to_work(args, arch_status=None, _repo='gentoo', quiet=0):
+       """Process |args| into a list of work itmes (ebuild/arches to update)"""
+       work = []
+       todo_arches = []
+       last_todo_arches = None
+
+       for arg in args:
+               if arg.endswith('.ebuild'):
+                       if not todo_arches:
+                               todo_arches = last_todo_arches
+                       if not todo_arches:
+                               raise ValueError('missing arches to process for 
%s' % arg)
+                       work.append([arg, todo_arches])
+                       last_todo_arches = todo_arches
+                       todo_arches = []
+               else:
+                       op = arg_to_op(arg)
+                       if not arch_status or op.arch in arch_status:
+                               todo_arches.append(op)
+                       elif not ignorable_arg(arg, quiet=quiet):
+                               raise ValueError('unknown arch/argument: %s' % 
arg)
+
+       if todo_arches:
+               raise ValueError('missing ebuilds to process!')
+
+       return work
+
+
+def get_parser():
+       """Return an argument parser for ekeyword"""
+       parser = argparse.ArgumentParser(
+               description=__doc__,
+               formatter_class=argparse.RawDescriptionHelpFormatter)
+       parser.add_argument('-m', '--manifest', default=False, 
action='store_true',
+               help='Run `ebuild manifest` on the ebuild after modifying it')
+       parser.add_argument('-n', '--dry-run', default=False, 
action='store_true',
+               help='Show what would be changed, but do not commit')
+       parser.add_argument('-v', '--verbose', action='count', default=0,
+               help='Be verbose while processing things')
+       parser.add_argument('-q', '--quiet', action='count', default=0,
+               help='Be quiet while processing things (only show errors)')
+       parser.add_argument('--format', default='auto', dest='style',
+               choices=('auto', 'color-inline', 'inline', 'short-multi', 
'long-multi'),
+               help='Selet output format for showing differences')
+       parser.add_argument('-V', '--version', action='version', 
version=VERSION,
+               help='Show version information')
+       return parser
+
+
+def main(argv):
+       if argv is None:
+               argv = sys.argv[1:]
+
+       # Extract the args ourselves.  This is to allow things like -hppa
+       # without tripping over the -h/--help flags.  We can't use the
+       # parse_known_args function either.
+       # This sucks and really wish we didn't need to do this ...
+       parse_args = []
+       work_args = []
+       while argv:
+               arg = argv.pop(0)
+               if arg.startswith('--'):
+                       if arg == '--':
+                               work_args += argv
+                               break
+                       else:
+                               parse_args.append(arg)
+                       # Handle flags that take arguments.
+                       if arg in ('--format',):
+                               if argv:
+                                       parse_args.append(argv.pop(0))
+               elif len(arg) == 2 and arg[0] == '-':
+                       parse_args.append(arg)
+               else:
+                       work_args.append(arg)
+
+       parser = get_parser()
+       opts = parser.parse_args(parse_args)
+       if not work_args:
+               parser.error('need arches/ebuilds to process')
+
+       if opts.style == 'auto':
+               if not portage_settings().get('NOCOLOR', 'false').lower() in 
('no', 'false'):
+                       nocolor()
+                       opts.style = 'short'
+               else:
+                       opts.style = 'color-inline'
+
+       arch_status = load_profile_data()
+       try:
+               work = args_to_work(work_args, arch_status=arch_status, 
quiet=opts.quiet)
+       except ValueError as e:
+               parser.error(e)
+
+       for ebuild, ops in work:
+               process_ebuild(ebuild, ops, arch_status=arch_status,
+                              verbose=opts.verbose, quiet=opts.quiet,
+                              dry_run=opts.dry_run, style=opts.style,
+                              manifest=opts.manifest)
+
+       return os.EX_OK
+
+
+if __name__ == '__main__':
+       sys.exit(main(sys.argv[1:]))

diff --git a/pym/gentoolkit/ekeyword/ekeyword_unittest.py 
b/pym/gentoolkit/ekeyword/ekeyword_unittest.py
new file mode 100755
index 0000000..de40e7a
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/ekeyword_unittest.py
@@ -0,0 +1,424 @@
+#!/usr/bin/python
+# Copyright 2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# Written by Mike Frysinger <vap...@gentoo.org>
+
+# pylint: disable=no-self-use
+
+"""Unittests for ekeyword"""
+
+from __future__ import print_function
+
+import os
+import subprocess
+import tempfile
+import unittest
+
+import mock
+
+import ekeyword
+
+
+TESTDIR = os.path.join(os.path.dirname(__file__), 'tests')
+
+
+class TestSortKeywords(unittest.TestCase):
+       """Tests for sort_keywords"""
+
+       def _test(self, input_data, exp_data):
+               """Sort |input_data| and make sure it matches |exp_data|"""
+               output_data = ekeyword.sort_keywords(input_data.split())
+               self.assertEqual(exp_data.split(), output_data)
+
+       def testNull(self):
+               """Verify whitespace is collapsed"""
+               self._test('', '')
+               self._test('             ', '')
+
+       def testGlob(self):
+               """Verify globs get sorted before all others"""
+               self._test('* arm', '* arm')
+               self._test('arm -* x86', '-* arm x86')
+               self._test('hppa ~* amd64', '~* amd64 hppa')
+
+       def testMixedPlatform(self):
+               """Verify core arches get sorted before all w/suffix"""
+               self._test('arm-linux alpha amd64-fbsd hppa',
+                          'alpha hppa amd64-fbsd arm-linux')
+
+       def testPrefixes(self):
+               """Verify -/~ and such get ignored for sorting"""
+               self._test('-hppa arm ~alpha -* ~arm-linux',
+                          '-* ~alpha arm -hppa ~arm-linux')
+
+       def testPlatform(self):
+               """Verify we sort based on platform first"""
+               self._test('x86-linux ppc-macos x86-fbsd amd64-linux 
amd64-fbsd',
+                          'amd64-fbsd x86-fbsd amd64-linux x86-linux 
ppc-macos')
+
+
+class TestDiffKeywords(unittest.TestCase):
+       """Tests for diff_keywords"""
+
+       def testEmpty(self):
+               """Test when there is no content to diff"""
+               ret = ekeyword.diff_keywords([], [])
+               self.assertEqual(ret, '')
+
+       def testSame(self):
+               """Test when there is no difference"""
+               ret = ekeyword.diff_keywords(['a b c'], ['a b c'])
+               self.assertEqual(ret, 'a b c')
+
+       def testInsert(self):
+               """Test when content is simply added"""
+               ret = ekeyword.diff_keywords(['a'], ['~a'])
+               self.assertNotEqual(ret, '')
+
+       def testDelete(self):
+               """Test when content is simply deleted"""
+               ret = ekeyword.diff_keywords(['~a'], ['a'])
+               self.assertNotEqual(ret, '')
+
+       def testReplace(self):
+               """Test when some content replaces another"""
+               ret = ekeyword.diff_keywords(['~a'], ['-a'])
+               self.assertNotEqual(ret, '')
+
+       def _testSmokeStyle(self, style):
+               return ekeyword.diff_keywords(
+                       ['~a', 'b', '-abcde'],
+                       ['a', '-b', '-abxde'], style=style)
+
+       def testSmokeStyleColor(self):
+               """Run a full smoke test for color-inline style"""
+               ret = self._testSmokeStyle('color-inline')
+               self.assertNotEqual(ret, '')
+
+       def testSmokeStyleNoColor(self):
+               """Run a full smoke test for non-color-inline style"""
+               self._testSmokeStyle('nocolor')
+
+
+class TestProcessKeywords(unittest.TestCase):
+       """Tests for process_keywords"""
+
+       def _test(self, keywords, ops, exp, arch_status=None):
+               # This func doesn't return sorted results (which is fine),
+               # so do so ourselves to get stable tests.
+               ret = ekeyword.process_keywords(
+                       keywords.split(), ops, arch_status=arch_status)
+               self.assertEqual(sorted(ret), sorted(exp.split()))
+
+       def testAdd(self):
+               ops = (
+                       ekeyword.Op(None, 'arm', None),
+                       ekeyword.Op('~', 's390', None),
+                       ekeyword.Op('-', 'sh', None),
+               )
+               self._test('moo', ops, 'arm ~s390 -sh moo')
+
+       def testModify(self):
+               ops = (
+                       ekeyword.Op(None, 'arm', None),
+                       ekeyword.Op('~', 's390', None),
+                       ekeyword.Op('-', 'sh', None),
+               )
+               self._test('~arm s390 ~sh moo', ops, 'arm ~s390 -sh moo')
+
+       def testDelete(self):
+               ops = (
+                       ekeyword.Op('^', 'arm', None),
+                       ekeyword.Op('^', 's390', None),
+                       ekeyword.Op('^', 'x86', None),
+               )
+               self._test('arm -s390 ~x86 bar', ops, 'bar')
+
+       def testSync(self):
+               ops = (
+                       ekeyword.Op('=', 'arm64', 'arm'),
+                       ekeyword.Op('=', 'ppc64', 'ppc'),
+                       ekeyword.Op('=', 'amd64', 'x86'),
+                       ekeyword.Op('=', 'm68k', 'mips'),
+                       ekeyword.Op('=', 'ia64', 'alpha'),
+                       ekeyword.Op('=', 'sh', 'sparc'),
+                       ekeyword.Op('=', 's390', 's390x'),
+                       ekeyword.Op('=', 'boo', 'moo'),
+               )
+               self._test(
+                       'arm64 arm '
+                       '~ppc64 ~ppc '
+                       '~amd64 x86 '
+                       'm68k ~mips '
+                       '-ia64 alpha '
+                       'sh -sparc '
+                       's390 '
+                       'moo ',
+                       ops,
+                       'arm64 arm ~ppc64 ~ppc amd64 x86 ~m68k ~mips ia64 alpha 
'
+                       '-sh -sparc boo moo')
+
+       def testAllNoStatus(self):
+               ops = (
+                       ekeyword.Op(None, 'all', None),
+               )
+               self.assertRaises(ValueError, self._test, '', ops, '')
+
+       def testAllStable(self):
+               ops = (
+                       ekeyword.Op(None, 'all', None),
+               )
+               arch_status = {
+                       'alpha': None,
+                       'arm': 'stable',
+                       'arm64': 'exp',
+                       'm68k': 'dev',
+               }
+               self._test('* ~alpha ~arm ~arm64 ~m68k ~mips ~arm-linux', ops,
+                          '* ~alpha arm ~arm64 ~m68k ~mips ~arm-linux', 
arch_status)
+
+       def testAllUnstable(self):
+               ops = (
+                       ekeyword.Op('~', 'all', None),
+               )
+               arch_status = {
+                       'alpha': None,
+                       'arm': 'stable',
+                       'arm64': 'exp',
+                       'm68k': 'dev',
+                       's390': 'dev',
+                       'sh': 'dev',
+               }
+               self._test('-* ~* * alpha arm arm64 m68k arm-linux', ops,
+                          '-* ~* * ~alpha ~arm ~arm64 ~m68k ~arm-linux', 
arch_status)
+
+       def testAllMultiUnstableStable(self):
+               ops = (
+                       ekeyword.Op('~', 'all', None),
+                       ekeyword.Op(None, 'all', None),
+               )
+               arch_status = {
+                       'alpha': None,
+                       'arm': 'stable',
+                       'arm64': 'exp',
+                       'm68k': 'dev',
+               }
+               self._test('-* ~* * alpha arm arm64 m68k', ops,
+                          '-* ~* * ~alpha arm ~arm64 ~m68k', arch_status)
+
+       def testAllDisabled(self):
+               """Make sure ~all does not change -arch to ~arch"""
+               ops = (
+                       ekeyword.Op('~', 'all', None),
+               )
+               self._test('alpha -sparc ~x86', ops,
+                          '~alpha -sparc ~x86', {})
+
+
+class TestProcessContent(unittest.TestCase):
+       """Tests for process_content"""
+
+       def _testKeywords(self, line):
+               ops = (
+                       ekeyword.Op(None, 'arm', None),
+                       ekeyword.Op('~', 'sparc', None),
+               )
+               return ekeyword.process_content(
+                       'file', ['%s\n' % line], ops, quiet=True)
+
+       def testKeywords(self):
+               """Basic KEYWORDS mod"""
+               updated, ret = self._testKeywords('KEYWORDS=""')
+               self.assertTrue(updated)
+               self.assertEqual(ret, ['KEYWORDS="arm ~sparc"\n'])
+
+       def testKeywordsIndented(self):
+               """Test KEYWORDS indented by space"""
+               updated, ret = self._testKeywords('             KEYWORDS=""')
+               self.assertTrue(updated)
+               self.assertEqual(ret, ['                KEYWORDS="arm 
~sparc"\n'])
+
+       def testKeywordsSingleQuote(self):
+               """Test single quoted KEYWORDS"""
+               updated, ret = self._testKeywords("KEYWORDS=' '")
+               self.assertTrue(updated)
+               self.assertEqual(ret, ['KEYWORDS="arm ~sparc"\n'])
+
+       def testKeywordsComment(self):
+               """Test commented out KEYWORDS"""
+               updated, ret = self._testKeywords('# KEYWORDS=""')
+               self.assertFalse(updated)
+               self.assertEqual(ret, ['# KEYWORDS=""\n'])
+
+       def testKeywordsCode(self):
+               """Test code leading KEYWORDS"""
+               updated, ret = self._testKeywords('[[ ${PV} ]] && KEYWORDS=""')
+               self.assertTrue(updated)
+               self.assertEqual(ret, ['[[ ${PV} ]] && KEYWORDS="arm 
~sparc"\n'])
+
+       def testKeywordsEmpty(self):
+               """Test KEYWORDS not set at all"""
+               updated, ret = self._testKeywords(' KEYWORDS=')
+               self.assertFalse(updated)
+               self.assertEqual(ret, [' KEYWORDS=\n'])
+
+       def _testSmoke(self, style='color-inline', verbose=0, quiet=0):
+               ops = (
+                       ekeyword.Op(None, 'arm', None),
+                       ekeyword.Op('~', 'sparc', None),
+               )
+               ekeyword.process_content(
+                       'asdf', ['KEYWORDS="arm"'], ops, verbose=verbose,
+                       quiet=quiet, style=style)
+
+       def testSmokeQuiet(self):
+               """Smoke test for quiet mode"""
+               self._testSmoke(quiet=10)
+
+       def testSmokeVerbose(self):
+               """Smoke test for verbose mode"""
+               self._testSmoke(verbose=10)
+
+       def testSmokeStyleColor(self):
+               """Smoke test for color-inline style"""
+               self._testSmoke('color-inline')
+
+       def testSmokeStyleInline(self):
+               """Smoke test for inline style"""
+               self._testSmoke('inline')
+
+       def testSmokeStyleShortMulti(self):
+               """Smoke test for short-multi style"""
+               self._testSmoke('short-multi')
+
+       def testSmokeStyleLongMulti(self):
+               """Smoke test for long-multi style"""
+               self._testSmoke('long-multi')
+
+
+class TestProcessEbuild(unittest.TestCase):
+       """Tests for process_ebuild
+
+       This is fairly light as most code is in process_content.
+       """
+
+       def _process_ebuild(self, *args, **kwargs):
+               """Set up a writable copy of an ebuild for process_ebuild()"""
+               with tempfile.NamedTemporaryFile() as tmp:
+                       with open(tmp.name, 'wb') as fw:
+                               with open(os.path.join(TESTDIR, 
'process-1.ebuild'), 'rb') as f:
+                                       orig_content = f.read()
+                                       fw.write(orig_content)
+                       ekeyword.process_ebuild(tmp.name, *args, **kwargs)
+                       with open(tmp.name, 'rb') as f:
+                               return (orig_content, f.read())
+
+       def _testSmoke(self, dry_run):
+               ops = (
+                       ekeyword.Op(None, 'arm', None),
+                       ekeyword.Op('~', 'sparc', None),
+               )
+               orig_content, new_content = self._process_ebuild(ops, 
dry_run=dry_run)
+               if dry_run:
+                       self.assertEqual(orig_content, new_content)
+               else:
+                       self.assertNotEqual(orig_content, new_content)
+
+       def testSmokeNotDry(self):
+               self._testSmoke(False)
+
+       def testSmokeDry(self):
+               self._testSmoke(True)
+
+       def testManifestUpdated(self):
+               """Verify `ebuild ... manifest` runs on updated files"""
+               with mock.patch.object(subprocess, 'check_call') as m:
+                       self._process_ebuild((ekeyword.Op('~', 'arm', None),),
+                                            manifest=True)
+               m.assert_called_once_with(['ebuild', mock.ANY, 'manifest'])
+
+       def testManifestNotUpdated(self):
+               """Verify we don't run `ebuild ... manifest` on unmodified 
files"""
+               with mock.patch.object(subprocess, 'check_call') as m:
+                       self._process_ebuild((ekeyword.Op(None, 'arm', None),),
+                                            manifest=True)
+               self.assertEqual(m.call_count, 0)
+
+
+class TestLoadProfileData(unittest.TestCase):
+       """Tests for load_profile_data"""
+
+       def _test(self, subdir):
+               portdir = os.path.join(TESTDIR, 'profiles', subdir)
+               return ekeyword.load_profile_data(portdir=portdir)
+
+       def testLoadBoth(self):
+               """Test loading both arch.list and profiles.desc"""
+               ret = self._test('both')
+               self.assertIn('arm', ret)
+               self.assertEqual(ret['arm'], 'stable')
+               self.assertIn('arm64', ret)
+               self.assertEqual(ret['arm64'], 'exp')
+
+       def testLoadArchOnly(self):
+               """Test loading only arch.list"""
+               ret = self._test('arch-only')
+               self.assertIn('arm', ret)
+               self.assertEqual(ret['arm'], None)
+               self.assertIn('x86-solaris', ret)
+
+       def testLoadProfilesOnly(self):
+               """Test loading only profiles.desc"""
+               ret = self._test('profiles-only')
+               self.assertIn('arm', ret)
+               self.assertEqual(ret['arm'], 'stable')
+               self.assertIn('arm64', ret)
+               self.assertEqual(ret['arm64'], 'exp')
+
+       def testLoadNone(self):
+               """Test running when neither files exists"""
+               ret = self._test('none')
+               self.assertEqual(ret, {})
+
+
+class TestArgToOps(unittest.TestCase):
+       """Tests for arg_to_op()"""
+
+       def _test(self, arg, op):
+               self.assertEqual(ekeyword.arg_to_op(arg), ekeyword.Op(*op))
+
+       def testStable(self):
+               self._test('arm', (None, 'arm', None))
+
+       def testUnstable(self):
+               self._test('~ppc64', ('~', 'ppc64', None))
+
+       def testDisabled(self):
+               self._test('-sparc', ('-', 'sparc', None))
+
+       def testDeleted(self):
+               self._test('^x86-fbsd', ('^', 'x86-fbsd', None))
+
+       def testSync(self):
+               self._test('s390=x86', (None, 's390', 'x86'))
+
+
+class TestMain(unittest.TestCase):
+       """Tests for the main entry point"""
+
+       def testSmoke(self):
+               ekeyword.main(['arm', '--dry-run', os.path.join(TESTDIR, 
'process-1.ebuild')])
+
+       def testVersion(self):
+               with self.assertRaises(SystemExit) as e:
+                       ekeyword.main(['--version', '--dry-run'])
+               self.assertEqual(e.exception.code, os.EX_OK)
+
+       def testEmptyString(self):
+               with self.assertRaises(SystemExit) as e:
+                       ekeyword.main(['', os.path.join(TESTDIR, 
'process-1.ebuild')])
+               self.assertNotEqual(e.exception.code, os.EX_OK)
+
+
+if __name__ == '__main__':
+       unittest.main()

diff --git a/pym/gentoolkit/ekeyword/pylint b/pym/gentoolkit/ekeyword/pylint
new file mode 100755
index 0000000..3a9a368
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/pylint
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 1999-2017 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+"""Run pylint with the right settings."""
+
+from __future__ import print_function
+
+import os
+import sys
+
+
+def find_all_modules(source_root):
+       """Locate all python modules in the tree for scanning"""
+       ret = []
+
+       for root, _dirs, files in os.walk(source_root, topdown=False):
+               # Add all of the .py modules in the tree.
+               ret += [os.path.join(root, x) for x in files if 
x.endswith('.py')]
+
+       # Add the main scripts that don't end in .py.
+       ret += [os.path.join(source_root, x) for x in ('pylint',)]
+
+       return ret
+
+
+def main(argv):
+       """The main entry point"""
+       source_root = os.path.dirname(os.path.realpath(__file__))
+
+       if not argv:
+               argv = find_all_modules(source_root)
+
+       pympath = source_root
+       pythonpath = os.environ.get('PYTHONPATH')
+       if pythonpath is None:
+               pythonpath = pympath
+       else:
+               pythonpath = pympath + ':' + pythonpath
+       os.environ['PYTHONPATH'] = pythonpath
+
+       pylintrc = os.path.join(source_root, '.pylintrc')
+       cmd = ['pylint', '--rcfile', pylintrc]
+       os.execvp(cmd[0], cmd + argv)
+
+
+if __name__ == '__main__':
+       sys.exit(main(sys.argv[1:]))

diff --git a/pym/gentoolkit/ekeyword/pytest.ini 
b/pym/gentoolkit/ekeyword/pytest.ini
new file mode 100644
index 0000000..622c9d8
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+addopts = --cov
+python_files = *_unittest.py

diff --git a/pym/gentoolkit/ekeyword/tests/process-1.ebuild 
b/pym/gentoolkit/ekeyword/tests/process-1.ebuild
new file mode 100644
index 0000000..75168c6
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/process-1.ebuild
@@ -0,0 +1,5 @@
+# asdf
+
+KEYWORDS="arm ~hppa x86"
+
+# blah

diff --git 
a/pym/gentoolkit/ekeyword/tests/profiles/arch-only/profiles/arch.list 
b/pym/gentoolkit/ekeyword/tests/profiles/arch-only/profiles/arch.list
new file mode 120000
index 0000000..361ad76
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/profiles/arch-only/profiles/arch.list
@@ -0,0 +1 @@
+../../both/profiles/arch.list
\ No newline at end of file

diff --git a/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/arch.list 
b/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/arch.list
new file mode 100644
index 0000000..e4787c0
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/arch.list
@@ -0,0 +1,45 @@
+alpha
+amd64
+amd64-fbsd
+arm
+arm64
+hppa
+ia64
+m68k
+mips
+ppc
+ppc64
+s390
+sh
+sparc
+sparc-fbsd
+x86
+x86-fbsd
+
+# Prefix keywords
+ppc-aix
+x86-freebsd
+x64-freebsd
+sparc64-freebsd
+hppa-hpux
+ia64-hpux
+x86-interix
+amd64-linux
+arm-linux
+ia64-linux
+ppc64-linux
+x86-linux
+ppc-macos
+x86-macos
+x64-macos
+m68k-mint
+x86-netbsd
+ppc-openbsd
+x86-openbsd
+x64-openbsd
+sparc-solaris
+sparc64-solaris
+x64-solaris
+x86-solaris
+x86-winnt
+x86-cygwin

diff --git a/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/profiles.desc 
b/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/profiles.desc
new file mode 100644
index 0000000..be751a8
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/profiles.desc
@@ -0,0 +1,295 @@
+#############################################
+# This is a list of valid profiles for each architecture.  This file is used by
+# repoman when doing a repoman scan or repoman full.
+# DO NOT ADD PROFILES WITH A "die" or "exit" IN THEM OR IT KILLS REPOMAN
+#
+#layout:
+#arch          profile_directory                               status
+
+# Alpha Profiles
+alpha           default/linux/alpha/13.0                        stable
+alpha           default/linux/alpha/13.0/desktop                stable
+alpha           default/linux/alpha/13.0/desktop/gnome          stable
+alpha           default/linux/alpha/13.0/desktop/gnome/systemd  stable
+alpha           default/linux/alpha/13.0/desktop/kde            stable
+alpha           default/linux/alpha/13.0/desktop/kde/systemd    stable
+alpha           default/linux/alpha/13.0/developer              stable
+
+# AMD64 Profiles
+amd64           default/linux/amd64/13.0                        stable
+amd64           default/linux/amd64/13.0/selinux                dev
+amd64           default/linux/amd64/13.0/desktop                stable
+amd64           default/linux/amd64/13.0/desktop/gnome          stable
+amd64           default/linux/amd64/13.0/desktop/gnome/systemd  stable
+amd64           default/linux/amd64/13.0/desktop/kde            stable
+amd64           default/linux/amd64/13.0/desktop/kde/systemd    stable
+amd64           default/linux/amd64/13.0/developer              stable
+amd64           default/linux/amd64/13.0/no-multilib            dev
+amd64           default/linux/amd64/13.0/x32                    dev
+
+# ARM Profiles
+arm             default/linux/arm/13.0                          stable
+arm             default/linux/arm/13.0/desktop                  dev
+arm             default/linux/arm/13.0/desktop/gnome            dev
+arm             default/linux/arm/13.0/desktop/gnome/systemd    dev
+arm             default/linux/arm/13.0/desktop/kde              dev
+arm             default/linux/arm/13.0/desktop/kde/systemd      dev
+arm             default/linux/arm/13.0/developer                dev
+arm             default/linux/arm/13.0/armv4                    dev
+arm             default/linux/arm/13.0/armv4/desktop            dev
+arm             default/linux/arm/13.0/armv4/desktop/gnome      dev
+arm             default/linux/arm/13.0/armv4/desktop/kde        dev
+arm             default/linux/arm/13.0/armv4/developer          dev
+arm             default/linux/arm/13.0/armv4t                   dev
+arm             default/linux/arm/13.0/armv4t/desktop           dev
+arm             default/linux/arm/13.0/armv4t/desktop/gnome     dev
+arm             default/linux/arm/13.0/armv4t/desktop/kde       dev
+arm             default/linux/arm/13.0/armv4t/developer         dev
+arm             default/linux/arm/13.0/armv5te                  dev
+arm             default/linux/arm/13.0/armv5te/desktop          dev
+arm             default/linux/arm/13.0/armv5te/desktop/gnome    dev
+arm             default/linux/arm/13.0/armv5te/desktop/kde      dev
+arm             default/linux/arm/13.0/armv5te/developer        dev
+arm             default/linux/arm/13.0/armv6j                   dev
+arm             default/linux/arm/13.0/armv6j/desktop           dev
+arm             default/linux/arm/13.0/armv6j/desktop/gnome     dev
+arm             default/linux/arm/13.0/armv6j/desktop/kde       dev
+arm             default/linux/arm/13.0/armv6j/developer         dev
+arm             default/linux/arm/13.0/armv7a                   dev
+arm             default/linux/arm/13.0/armv7a/desktop           dev
+arm             default/linux/arm/13.0/armv7a/desktop/gnome     dev
+arm             default/linux/arm/13.0/armv7a/desktop/kde       dev
+arm             default/linux/arm/13.0/armv7a/developer         dev
+
+# ARM64 Profiles
+arm64           default/linux/arm64/13.0                        exp
+arm64           default/linux/arm64/13.0/desktop                exp
+arm64           default/linux/arm64/13.0/developer              exp
+
+# HPPA Profiles
+hppa            default/linux/hppa/13.0                         stable
+hppa            default/linux/hppa/13.0/desktop                 dev
+hppa            default/linux/hppa/13.0/developer               dev
+
+# IA64 Profiles
+ia64            default/linux/ia64/13.0                         stable
+ia64            default/linux/ia64/13.0/desktop                 stable
+ia64            default/linux/ia64/13.0/desktop/gnome           stable
+ia64            default/linux/ia64/13.0/desktop/gnome/systemd   stable
+ia64            default/linux/ia64/13.0/desktop/kde             stable
+ia64            default/linux/ia64/13.0/desktop/kde/systemd     stable
+ia64            default/linux/ia64/13.0/developer               stable
+
+# M68K Profiles
+m68k            default/linux/m68k/13.0                         dev
+m68k            default/linux/m68k/13.0/desktop                 dev
+m68k            default/linux/m68k/13.0/desktop/gnome           dev
+m68k            default/linux/m68k/13.0/desktop/kde             dev
+m68k            default/linux/m68k/13.0/developer               dev
+
+# MIPS Profiles
+mips            default/linux/mips/13.0                         dev
+mips            default/linux/mips/13.0/n32                     dev
+mips            default/linux/mips/13.0/n64                     exp
+mips            default/linux/mips/13.0/multilib                dev
+mips            default/linux/mips/13.0/multilib/n32            dev
+mips            default/linux/mips/13.0/multilib/n64            exp
+mips            default/linux/mips/13.0/mipsel                  dev
+mips            default/linux/mips/13.0/mipsel/n32              dev
+mips            default/linux/mips/13.0/mipsel/n64              exp
+mips            default/linux/mips/13.0/mipsel/multilib         dev
+mips            default/linux/mips/13.0/mipsel/multilib/n32     dev
+mips            default/linux/mips/13.0/mipsel/multilib/n64     exp
+
+# PPC32 Profiles
+ppc             default/linux/powerpc/ppc32/13.0                       stable
+ppc             default/linux/powerpc/ppc32/13.0/desktop               stable
+ppc             default/linux/powerpc/ppc32/13.0/desktop/gnome         stable
+ppc             default/linux/powerpc/ppc32/13.0/desktop/gnome/systemd stable
+ppc             default/linux/powerpc/ppc32/13.0/desktop/kde           stable
+ppc             default/linux/powerpc/ppc32/13.0/desktop/kde/systemd   stable
+ppc             default/linux/powerpc/ppc32/13.0/developer             stable
+
+# PPC64 Profiles
+ppc             default/linux/powerpc/ppc64/13.0/32bit-userland                
         stable
+ppc             default/linux/powerpc/ppc64/13.0/32bit-userland/desktop        
         stable
+ppc             default/linux/powerpc/ppc64/13.0/32bit-userland/desktop/gnome  
         stable
+ppc             
default/linux/powerpc/ppc64/13.0/32bit-userland/desktop/gnome/systemd   stable
+ppc             default/linux/powerpc/ppc64/13.0/32bit-userland/desktop/kde    
         stable
+ppc             
default/linux/powerpc/ppc64/13.0/32bit-userland/desktop/kde/systemd     stable
+ppc             default/linux/powerpc/ppc64/13.0/32bit-userland/developer      
         stable
+ppc64           default/linux/powerpc/ppc64/13.0/64bit-userland                
         stable
+ppc64           default/linux/powerpc/ppc64/13.0/64bit-userland/desktop        
         stable
+ppc64           default/linux/powerpc/ppc64/13.0/64bit-userland/desktop/gnome  
         stable
+ppc64           
default/linux/powerpc/ppc64/13.0/64bit-userland/desktop/gnome/systemd   stable
+ppc64           default/linux/powerpc/ppc64/13.0/64bit-userland/desktop/kde    
         stable
+ppc64           
default/linux/powerpc/ppc64/13.0/64bit-userland/desktop/kde/systemd     stable
+ppc64           default/linux/powerpc/ppc64/13.0/64bit-userland/developer      
         stable
+
+# S390 Profiles
+s390            default/linux/s390/13.0                         dev
+s390            default/linux/s390/13.0/s390x                   dev
+
+# SH Profiles
+sh              default/linux/sh/13.0                           dev
+sh              default/linux/sh/13.0/desktop                   dev
+sh              default/linux/sh/13.0/desktop/gnome             dev
+sh              default/linux/sh/13.0/desktop/kde               dev
+sh              default/linux/sh/13.0/developer                 dev
+
+# SPARC Profiles
+sparc           default/linux/sparc/13.0                        stable
+sparc           default/linux/sparc/13.0/desktop                stable
+sparc           default/linux/sparc/13.0/desktop/gnome          stable
+sparc           default/linux/sparc/13.0/desktop/kde            stable
+sparc           default/linux/sparc/13.0/developer              stable
+
+# x86 Profiles
+x86             default/linux/x86/13.0                          stable
+x86             default/linux/x86/13.0/selinux                  dev
+x86             default/linux/x86/13.0/desktop                  stable
+x86             default/linux/x86/13.0/desktop/gnome            stable
+x86             default/linux/x86/13.0/desktop/gnome/systemd    stable
+x86             default/linux/x86/13.0/desktop/kde              stable
+x86             default/linux/x86/13.0/desktop/kde/systemd      stable
+x86             default/linux/x86/13.0/developer                stable
+
+# Gentoo/FreeBSD Profiles
+amd64-fbsd     default/bsd/fbsd/amd64/9.1                      stable
+amd64-fbsd     default/bsd/fbsd/amd64/9.2                      dev
+amd64-fbsd     default/bsd/fbsd/amd64/9.1/clang                exp
+amd64-fbsd     default/bsd/fbsd/amd64/9.2/clang                exp
+sparc-fbsd      default/bsd/fbsd/sparc/8.2                      exp
+x86-fbsd       default/bsd/fbsd/x86/9.1                        dev
+x86-fbsd       default/bsd/fbsd/x86/9.2                        dev
+
+# Hardened Profiles
+amd64          hardened/linux/amd64                                    stable
+amd64          hardened/linux/amd64/selinux                            stable
+amd64          hardened/linux/amd64/no-multilib                        stable
+amd64          hardened/linux/amd64/no-multilib/selinux                stable
+amd64          hardened/linux/amd64/x32                                dev
+amd64          hardened/linux/uclibc/amd64                             dev
+arm            hardened/linux/arm/armv7a                               dev
+arm            hardened/linux/arm/armv6j                               dev
+arm            hardened/linux/uclibc/arm/armv7a                        dev
+ia64           hardened/linux/ia64                                     dev
+mips           hardened/linux/uclibc/mips                              exp
+mips           hardened/linux/uclibc/mips/mipsel                       exp
+ppc            hardened/linux/powerpc/ppc32                            dev
+ppc            hardened/linux/powerpc/ppc64/32bit-userland             dev
+ppc64          hardened/linux/powerpc/ppc64/64bit-userland             dev
+x86            hardened/linux/x86                                      stable
+x86            hardened/linux/x86/selinux                              stable
+x86            hardened/linux/uclibc/x86                               dev
+
+# uclibc/embedded multiarch profiles
+#amd64         uclibc/amd64                                    dev
+#arm           uclibc/arm                                      dev
+#arm           uclibc/arm/2.4                                  dev
+#mips          uclibc/mips                                     dev
+#mips          uclibc/mips/hardened                            dev
+#ppc           uclibc/ppc                                      dev
+#ppc           uclibc/ppc/2.4                                  dev
+#ppc           uclibc/ppc/hardened                             dev
+#ppc           uclibc/ppc/hardened/2.4                         dev
+#sh            uclibc/sh                                       dev
+#sh            uclibc/sh/2.4                                   dev
+#x86           uclibc/x86                                      dev
+#x86           uclibc/x86/2.4                                  dev
+#x86           uclibc/x86/2005.1                               dev
+#x86           uclibc/x86/2005.1/2.4                           dev
+#x86           uclibc/x86/hardened                             dev
+#x86           uclibc/x86/hardened/2.4                         dev
+
+
+# These are Gentoo Prefix profiles, maintained by the Prefix team
+
+# Linux Profiles
+amd64-linux            prefix/linux/amd64                              exp
+arm-linux              prefix/linux/arm                                exp
+ia64-linux             prefix/linux/ia64                               exp
+ppc64-linux            prefix/linux/ppc64                              exp
+x86-linux              prefix/linux/x86                                exp
+
+# Mac OS X Profiles
+ppc-macos              prefix/darwin/macos/10.4/ppc                    exp
+x86-macos              prefix/darwin/macos/10.4/x86                    exp
+ppc-macos              prefix/darwin/macos/10.5/ppc                    exp
+x86-macos              prefix/darwin/macos/10.5/x86                    exp
+x64-macos              prefix/darwin/macos/10.5/x64                    exp
+x86-macos              prefix/darwin/macos/10.6/x86                    exp
+x64-macos              prefix/darwin/macos/10.6/x64                    exp
+x86-macos              prefix/darwin/macos/10.7/x86                    exp
+x64-macos              prefix/darwin/macos/10.7/x64                    exp
+x86-macos              prefix/darwin/macos/10.8/x86                    exp
+x64-macos              prefix/darwin/macos/10.8/x64                    exp
+x86-macos              prefix/darwin/macos/10.9/x86                    exp
+x64-macos              prefix/darwin/macos/10.9/x64                    exp
+
+# Solaris Profiles
+sparc-solaris          prefix/sunos/solaris/5.9/sparc                  exp
+sparc-solaris          prefix/sunos/solaris/5.10/sparc                 exp
+sparc64-solaris                prefix/sunos/solaris/5.10/sparc64               
exp
+x86-solaris            prefix/sunos/solaris/5.10/x86                   exp
+x64-solaris            prefix/sunos/solaris/5.10/x64                   exp
+sparc-solaris          prefix/sunos/solaris/5.11/sparc                 exp
+sparc64-solaris                prefix/sunos/solaris/5.11/sparc64               
exp
+x86-solaris            prefix/sunos/solaris/5.11/x86                   exp
+x64-solaris            prefix/sunos/solaris/5.11/x64                   exp
+
+# AIX Profiles
+ppc-aix                        prefix/aix/5.2.0.0/ppc                          
exp
+ppc-aix                        prefix/aix/5.3.0.0/ppc                          
exp
+ppc-aix                        prefix/aix/6.1.0.0/ppc                          
exp
+
+# Interix Profiles
+x86-interix            prefix/windows/interix/3.5/x86                  exp
+x86-interix            prefix/windows/interix/5.2/x86                  exp
+x86-interix            prefix/windows/interix/6.0/x86                  exp
+x86-interix            prefix/windows/interix/6.1/x86                  exp
+
+# Windows Profiles
+x86-winnt              prefix/windows/winnt/3.5/x86                    exp
+x86-winnt              prefix/windows/winnt/5.2/x86                    exp
+x86-winnt              prefix/windows/winnt/6.0/x86                    exp
+x86-winnt              prefix/windows/winnt/6.1/x86                    exp
+
+# Cygwin Profiles
+x86-cygwin             prefix/windows/cygwin/1.7/x86                   exp
+
+# HP-UX Profiles
+ia64-hpux              prefix/hpux/B.11.23/ia64                        exp
+hppa-hpux              prefix/hpux/B.11.31/hppa2.0                     exp
+ia64-hpux              prefix/hpux/B.11.31/ia64                        exp
+
+# FreeBSD Profiles
+x86-freebsd            prefix/bsd/freebsd/7.1/x86                      exp
+x64-freebsd            prefix/bsd/freebsd/7.1/x64                      exp
+x86-freebsd            prefix/bsd/freebsd/7.2/x86                      exp
+x64-freebsd            prefix/bsd/freebsd/7.2/x64                      exp
+x86-freebsd            prefix/bsd/freebsd/8.0/x86                      exp
+x64-freebsd            prefix/bsd/freebsd/8.0/x64                      exp
+x86-freebsd            prefix/bsd/freebsd/8.1/x86                      exp
+x64-freebsd            prefix/bsd/freebsd/8.1/x64                      exp
+sparc64-freebsd                prefix/bsd/freebsd/8.1/sparc64                  
exp
+x86-freebsd            prefix/bsd/freebsd/8.2/x86                      exp
+x64-freebsd            prefix/bsd/freebsd/8.2/x64                      exp
+x86-freebsd            prefix/bsd/freebsd/9.0/x86                      exp
+x64-freebsd            prefix/bsd/freebsd/9.0/x64                      exp
+x86-freebsd            prefix/bsd/freebsd/9.1/x86                      exp
+x64-freebsd            prefix/bsd/freebsd/9.1/x64                      exp
+
+
+# OpenBSD Profiles
+ppc-openbsd            prefix/bsd/openbsd/4.2/ppc                      exp
+x86-openbsd            prefix/bsd/openbsd/4.2/x86                      exp
+x64-openbsd            prefix/bsd/openbsd/4.2/x64                      exp
+
+# NetBSD Profiles
+x86-netbsd             prefix/bsd/netbsd/4.0/x86                       exp
+
+# FreeMiNT
+m68k-mint              prefix/mint/m68k                                exp
+
+# vim: set ts=8:

diff --git a/pym/gentoolkit/ekeyword/tests/profiles/none/profiles/.keep 
b/pym/gentoolkit/ekeyword/tests/profiles/none/profiles/.keep
new file mode 100644
index 0000000..e69de29

diff --git 
a/pym/gentoolkit/ekeyword/tests/profiles/profiles-only/profiles/profiles.desc 
b/pym/gentoolkit/ekeyword/tests/profiles/profiles-only/profiles/profiles.desc
new file mode 120000
index 0000000..04f8005
--- /dev/null
+++ 
b/pym/gentoolkit/ekeyword/tests/profiles/profiles-only/profiles/profiles.desc
@@ -0,0 +1 @@
+../../both/profiles/profiles.desc
\ No newline at end of file

diff --git a/pym/gentoolkit/imlate/Makefile b/pym/gentoolkit/imlate/Makefile
new file mode 100644
index 0000000..6735696
--- /dev/null
+++ b/pym/gentoolkit/imlate/Makefile
@@ -0,0 +1,18 @@
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+include ../../makedefs.mak
+
+.PHONY: all
+
+all:
+
+dist:
+       mkdir -p ../../$(DISTDIR)/src/imlate/
+       cp Makefile imlate imlate.1 ../../$(DISTDIR)/src/imlate/
+
+install: all
+       install -m 0755 imlate $(BINDIR)/
+       install -m 0644 imlate.1 $(MAN1DIR)/
+

diff --git a/pym/gentoolkit/imlate/imlate b/pym/gentoolkit/imlate/imlate
new file mode 100755
index 0000000..0be72e4
--- /dev/null
+++ b/pym/gentoolkit/imlate/imlate
@@ -0,0 +1,480 @@
+#!/usr/bin/python
+# Copyright 1999-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+# Author: Christian Ruppert <id...@gentoo.org>
+
+# Python 2.6 compatibility
+from __future__ import print_function
+
+VERSION = "1.0.1"
+
+# works just with stable keywords!
+MAIN_ARCH = "auto" # can be overridden by -m ARCH
+TARGET_ARCH = "auto" # can be overridden by -t ARCH
+# auto means e.g.:
+# MAIN_ARCH = amd64
+# TARGET_ARCH = ~amd64
+# That will show you general stable candidates for amd64.
+# The arch will be taken from your portage settings (e.g. make.conf).
+
+################################
+# do not change anything below #
+################################
+
+from os.path import join, basename
+from sys import stderr, stdout
+from os import stat
+from time import time
+from xml.dom import minidom, NotFoundErr
+from xml.parsers.expat import ExpatError
+# TODO: just import needed stuff to safe memory/time and maybe use "as foo"
+import portage
+import portage.versions
+
+if __name__ == "__main__":
+       from optparse import OptionParser
+       from time import gmtime, strftime
+
+# override/change portage module settings
+def _portage_settings( var, value, settings = None ):
+       if not settings:
+               settings = portage.settings
+
+       settings.unlock()
+       settings[var] = value
+       # backup_changes is very important since it can cause trouble,
+       # if we do not backup our changes!
+       settings.backup_changes( var )
+       settings.lock()
+
+# add stuff to our imlate dict
+def _add_ent( imlate, cat, pkg, ver, our_ver ):
+       if not cat in list(imlate.keys()):
+               imlate[cat] = {}
+       if not pkg in list(imlate[cat].keys()):
+               imlate[cat][pkg] = []
+
+       imlate[cat][pkg].append( ver )
+       imlate[cat][pkg].append( our_ver )
+
+       return imlate
+
+def _fill( width, line, fill = " " ):
+       while len( line ) < width:
+               line = "%s%s" % ( str( line ), str( fill ) )
+       return line
+
+# create a hopefully pretty result
+def show_result( conf, pkgs ):
+       # X - len(colX) = space to fill
+       col1 = 40
+       col2 = 20
+
+       _header = "%s candidates for 'gentoo' on '%s'"
+       _helper = "category/package[:SLOT]                 our version         
best version"
+       _cand = ""
+       header = ""
+
+       if conf["FILE"] == "stdout":
+               out = stdout
+       elif conf["FILE"] == "stderr":
+               out = stderr
+       else:
+               out = open( conf["FILE"], "w" )
+
+       if conf["STABLE"] and conf["KEYWORD"]:
+               _cand = "%i Stable and %i Keyword(~)" % ( conf["STABLE_SUM"],
+                                                                               
                conf["KEYWORD_SUM"] )
+       elif conf["STABLE"]:
+               _cand = "%i Stable" % conf["STABLE_SUM"]
+       elif conf["KEYWORD"]:
+               _cand = "%i Keyword(~)" % conf["KEYWORD_SUM"]
+
+       header = _header % ( _cand, conf["MAIN_ARCH"] )
+
+       print("Generated on: %s" % conf["TIME"], file=out)
+       print(_fill( len( header ), "", "=" ), file=out)
+       print(header, file=out)
+       print(_fill( len( header ), "", "=" ), file=out)
+       print(file=out)
+
+       print(_helper, file=out)
+       print(_fill( len( _helper ), "", "-" ), file=out)
+
+       for cat in sorted( pkgs.keys() ):
+               print("%s/" % cat, file=out)
+               for pkg in sorted( pkgs[cat].keys() ):
+                       print("%s%s%s" % ( _fill( col1, ( "  %s" % pkg ) ),
+                                                                       _fill( 
col2, pkgs[cat][pkg][1] ),
+                                                                       
pkgs[cat][pkg][0] ), file=out)
+
+       if conf["FILE"] != "stdout":
+               out.close()
+
+def _get_metadata(metadata, element, tag):
+       values = []
+
+       try:
+               metadatadom = minidom.parse(metadata)
+       except ExpatError as e:
+               raise ExpatError("%s: %s" % (metadata, e,))
+
+       try:
+               elements = metadatadom.getElementsByTagName(element)
+               if not elements:
+                       return values
+       except NotFoundErr:
+               return values
+
+       try:
+               for _element in elements:
+                       node = _element.getElementsByTagName(tag)
+
+                       if tag == "herd" and (not node or not 
node[0].childNodes):
+#                              print >> stderr, "'%s' is missing a <herd> tag 
or it is empty," % metadata
+#                              print >> stderr, "please file a bug at 
https://bugs.gentoo.org and refer to 
http://www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=4";
+                               values.append("no-herd")
+                               continue
+
+                       try:
+                               values.append(node[0].childNodes[0].data)
+                       except IndexError:
+                               pass
+       except NotFoundErr:
+               raise NotFoundErr("%s: Malformed input: missing 'flag' tag(s)" 
% (metadata))
+
+       metadatadom.unlink()
+       return values
+
+def is_maintainer(maintainer, metadata):
+       data = []
+
+       if maintainer == None:
+               return True
+
+       mtainer = maintainer.split(",")
+
+       data = _get_metadata(metadata, "maintainer", "email")
+
+       if not data and len(maintainer) == 0:
+               return True
+       elif not data and len(maintainer) > 0:
+               return False
+       else:
+               for addy in data:
+                       for contact in mtainer:
+                               if addy == contact:
+                                       return True
+                               if addy.startswith(contact):
+                                       return True
+       return False
+
+def is_herd(herd, metadata):
+       data = []
+
+       if herd == None:
+               return True
+
+       hrd = herd.split(",")
+       data = _get_metadata(metadata, "pkgmetadata", "herd")
+
+       if not data and len(herd) == 0:
+               return True
+       elif not data and len(herd) > 0:
+               return False
+       else:
+               for hd in data:
+                       for hd2 in hrd:
+                               if hd == hd2:
+                                       return True
+                               if hd.startswith(hd2):
+                                       return True
+       
+       return False
+
+
+# fetch a list of arch (just stable) packages
+# -* is important to be sure that just arch is used
+def get_packages( conf ):
+       _pkgs = {}
+
+       _portage_settings( "ACCEPT_KEYWORDS", ( "-* %s" % str( 
conf["TARGET_ARCH"] ) ),
+                                       conf["portdb"].settings )
+
+       for cp in conf["portdb"].dbapi.cp_all():
+               cpvrs = []
+               slots = {}
+
+               if conf["USER_PKGS"]:
+                       if not cp in conf["USER_PKGS"] and not basename(cp) in 
conf["USER_PKGS"]:
+                               continue
+
+               # None is important to match also on empty string
+               if conf["MAINTAINER"] != None:
+                       if not is_maintainer(conf["MAINTAINER"], 
join(conf["PORTDIR"], cp, "metadata.xml")):
+                               continue
+               if conf["HERD"] != None:
+                       if not is_herd(conf["HERD"], join(conf["PORTDIR"], cp, 
"metadata.xml")):
+                               continue
+
+               cpvrs = conf["portdb"].dbapi.match( cp )
+
+               for cpvr in cpvrs:
+                       slot = conf["portdb"].dbapi.aux_get( cpvr, ["SLOT"] )[0]
+                       if not slot in slots:
+                               slots[slot] = []
+                       slots[slot].append(cpvr)
+               
+               for slot in sorted(slots):
+                       cpvr = portage.versions.best( slots[slot] )
+
+                       if cpvr:
+                               ( cat, pkg, ver, rev ) = 
portage.versions.catpkgsplit( cpvr )
+
+                               if not cat in list(_pkgs.keys()):
+                                       _pkgs[cat] = {}
+                               if not pkg in list(_pkgs[cat].keys()):
+                                       _pkgs[cat][pkg] = []
+
+                               if rev != "r0":
+                                       ver = "%s-%s" % ( ver, rev )
+
+                               _pkgs[cat][pkg].append( ver )
+
+       return _pkgs
+
+# compare get_packages() against MAIN_ARCH
+def get_imlate( conf, pkgs ):
+       _portage_settings( "ACCEPT_KEYWORDS", ( "-* %s" % str( 
conf["MAIN_ARCH"] ) ),
+                                       conf["portdb"].settings )
+
+       stable = str( conf["MAIN_ARCH"].lstrip("~") )
+       testing = "~%s" % stable
+       exclude = "-%s" % stable
+       exclude_all = "-*"
+
+       imlate = {}
+
+       for cat in sorted( pkgs.keys() ):
+               for pkg in sorted( pkgs[cat].keys() ):
+                       for vr in pkgs[cat][pkg]:
+                               cpvr = ""
+                               abs_pkg = ""
+                               kwds = ""
+                               our = ""
+                               our_ver = ""
+                               mtime = 0
+                               slot = 0
+       
+                               # 0 = none(default), 1 = testing(~arch), 2 = 
stable(arch),
+                               # 3 = exclude(-arch), 4 = exclude_all(-*)
+                               # -* would be overridden by ~arch or arch
+                               kwd_type = 0
+
+                               cpvr = "%s/%s-%s" % ( cat, pkg, vr )
+       
+                               # absolute ebuild path for mtime check
+                               abs_pkg = join( conf["PORTDIR"], cat, pkg, 
basename( cpvr ) )
+                               abs_pkg = "%s.ebuild" % str( abs_pkg )
+       
+                               kwds = conf["portdb"].dbapi.aux_get( cpvr, 
["KEYWORDS"] )[0]
+       
+                               # FIXME: %s is bad.. maybe even cast it, else 
there are issues because its unicode
+                               slot = ":%s" % conf["portdb"].dbapi.aux_get( 
cpvr, ["SLOT"] )[0]
+                               if slot == ":0":
+                                       slot = ""
+       
+                               # sorted() to keep the right order
+                               # e.g. -* first, -arch second, arch third and 
~arch fourth
+                               # -* -foo ~arch
+                               # example: -* would be overridden by ~arch
+                               for kwd in sorted( kwds.split() ):
+                                       if kwd == stable:
+                                               kwd_type = 2
+                                               break
+                                       elif kwd == exclude:
+                                               kwd_type = 3
+                                               break
+                                       elif kwd == exclude_all:
+                                               kwd_type = 4
+                                       elif kwd == testing:
+                                               kwd_type = 1
+                                               break
+       
+                               # ignore -arch and already stabilized packages
+                               if kwd_type == 3 or kwd_type == 2:
+                                       continue
+                               # drop packages with -* and without ~arch or 
arch
+                               # even if there is another version which 
includes arch or ~arch
+                               if kwd_type == 4:
+                                       continue
+                               # drop "stable candidates" with mtime < 30 days
+                               # Shall we use gmtime/UTC here?
+                               if kwd_type == 1:
+                                       mtime = int( ( time() - stat( abs_pkg 
).st_mtime ) / 60 / 60 / 24 )
+                                       if mtime < conf["MTIME"]:
+                                               continue
+       
+                               # look for an existing stable version
+                               our = portage.versions.best( 
conf["portdb"].dbapi.match( "%s/%s%s" % ( cat, pkg, slot ) ) )
+                               if our:
+                                       _foo = portage.versions.pkgsplit( our )
+                                       our_ver = _foo[1]
+                                       if _foo[2] != "r0":
+                                               our_ver = "%s-%s" % ( our_ver, 
_foo[2] )
+                               else:
+                                       our_ver = ""
+       
+                               # we just need the version if > our_ver
+                               if our_ver:
+                                       if portage.versions.vercmp( our_ver, vr 
) >= 0:
+                                               continue
+       
+                               if kwd_type == 1 and conf["STABLE"]:
+                                       imlate = _add_ent( imlate, cat, ("%s%s" 
% (pkg, slot)), vr, our_ver )
+                                       conf["STABLE_SUM"] += 1
+                               elif kwd_type == 0 and conf["KEYWORD"]:
+                                       conf["KEYWORD_SUM"] += 1
+                                       imlate = _add_ent( imlate, cat, ( 
"~%s%s" % (pkg, slot) ),
+                                                                       vr, 
our_ver )
+
+       return imlate
+
+# fetch portage related settings
+def get_settings( conf = None ):
+       if not isinstance( conf, dict ) and conf:
+               raise TypeError("conf must be dict() or None")
+       if not conf:
+               conf = {}
+
+       # TODO: maybe we should improve it a bit ;)
+       mysettings = portage.config( config_incrementals = 
portage.const.INCREMENTALS, local_config = False )
+
+       if conf["MAIN_ARCH"] == "auto":
+               conf["MAIN_ARCH"] = "%s" % 
mysettings["ACCEPT_KEYWORDS"].split(" ")[0].lstrip("~")
+       if conf["TARGET_ARCH"] == "auto":
+               conf["TARGET_ARCH"] = "~%s" % 
mysettings["ACCEPT_KEYWORDS"].split(" ")[0].lstrip("~")
+
+       # TODO: exclude overlay categories from check
+       if conf["CATEGORIES"]:
+               _mycats = []
+               for _cat in conf["CATEGORIES"].split(","):
+                       _cat = _cat.strip()
+                       _mycats.append(_cat )
+                       if _cat not in mysettings.categories:
+                               raise ValueError("invalid category for -C 
switch '%s'" % _cat)
+               mysettings.categories = _mycats
+
+       # maybe thats not necessary because we override porttrees below..
+       _portage_settings( "PORTDIR_OVERLAY", "", mysettings )
+       trees = portage.create_trees()
+       trees["/"]["porttree"].settings = mysettings
+       portdb = trees["/"]["porttree"]
+       portdb.dbapi.settings = mysettings
+       portdb.dbapi.porttrees = [portage.portdb.porttree_root]
+       # does it make sense to remove _all_ useless stuff or just leave it as 
it is?
+       #portdb.dbapi._aux_cache_keys.clear()
+       #portdb.dbapi._aux_cache_keys.update(["EAPI", "KEYWORDS", "SLOT"])
+
+       conf["PORTDIR"] = portage.settings["PORTDIR"]
+       conf["portdb"] = portdb
+
+       return conf
+
+
+# just for standalone
+def main():
+       conf = {}
+       pkgs = {}
+
+       parser = OptionParser( version = "%prog " + VERSION )
+       parser.usage = "%prog [options] [category/package] ..."
+       parser.disable_interspersed_args()
+
+       parser.add_option( "-f", "--file", dest = "filename", action = "store", 
type = "string",
+                       help = "write result into FILE [default: %default]", 
metavar = "FILE", default = "stdout" )
+       parser.add_option( "-m", "--main", dest = "main_arch", action = 
"store", type = "string",
+                       help = "set main ARCH (e.g. your arch) [default: 
%default]", metavar = "ARCH", default = MAIN_ARCH )
+       parser.add_option( "-t", "--target", dest = "target_arch", action = 
"store", type = "string",
+                       help = "set target ARCH (e.g. x86) [default: 
%default]", metavar = "ARCH", default = TARGET_ARCH )
+       parser.add_option( "--mtime", dest = "mtime", action = "store", type = 
"int",
+                       help = "set minimum MTIME in days [default: %default]", 
metavar = "MTIME", default = 30 )
+
+       # TODO: leave a good comment here (about True/False) :)
+       parser.add_option( "-s", "--stable", dest = "stable", action = 
"store_true", default = False,
+                       help = "just show stable candidates (e.g. -s and -k is 
the default result) [default: True]" )
+       parser.add_option( "-k", "--keyword", dest = "keyword", action = 
"store_true", default = False,
+                       help = "just show keyword candidates (e.g. -s and -k is 
the default result) [default: True]" )
+
+       parser.add_option( "-M", "--maintainer", dest = "maintainer", action = 
"store", type = "string",
+                       help = "Show only packages from the specified 
maintainer", metavar = "MAINTAINER", default = None)
+
+       parser.add_option( "-H", "--herd", dest = "herd", action = "store", 
type = "string",
+                       help = "Show only packages from the specified herd", 
metavar = "HERD", default = None)
+
+#      # EXPERIMENTAL
+#      parser.add_option( "-e", "--experimental", dest = "experimental", 
action = "store_true", default = False,
+#                      help = "enables experimental functions/features (have a 
look for # EXPERIMENTAL comments in the source) [default: %default]" )
+
+       parser.add_option( "-C", "--category", "--categories", dest = 
"categories", action = "store", default = None,
+                       metavar = "CATEGORIES",
+                       help = "just check in the specified category/categories 
(comma separated) [default: %default]")
+
+       ( options, args ) = parser.parse_args()
+
+       if len( args ) > 0:
+               conf["USER_PKGS"] = args
+       else:
+               conf["USER_PKGS"] = []
+
+       # cleanup optparse
+       try:
+               parser.destroy()
+       except AttributeError:
+               # to be at least python 2.4 compatible
+               del parser._short_opt
+               del parser._long_opt
+               del parser.defaults
+
+       # generated timestamp (UTC)
+       conf["TIME"] = strftime( "%a %b %d %H:%M:%S %Z %Y", gmtime() )
+
+       # package counter
+       conf["KEYWORD_SUM"] = 0
+       conf["STABLE_SUM"] = 0
+
+       if not options.main_arch in portage.archlist and options.main_arch != 
"auto":
+               raise ValueError("invalid MAIN ARCH defined!")
+       if not options.target_arch in portage.archlist and options.target_arch 
!= "auto":
+               raise ValueError("invalid TARGET ARCH defined!")
+
+       conf["MAIN_ARCH"] = options.main_arch
+       conf["TARGET_ARCH"] = options.target_arch
+
+       conf["FILE"] = options.filename
+       conf["MTIME"] = options.mtime
+
+       if not options.stable and not options.keyword:
+               conf["STABLE"] = True
+               conf["KEYWORD"] = True
+       else:
+               conf["STABLE"] = options.stable
+               conf["KEYWORD"] = options.keyword
+
+#      conf["EXPERIMENTAL"] = options.experimental
+       conf["CATEGORIES"] = options.categories
+
+       conf["MAINTAINER"] = options.maintainer
+       conf["HERD"] = options.herd
+
+       # append to our existing
+       conf = get_settings( conf )
+       pkgs = get_packages( conf )
+       pkgs = get_imlate( conf, pkgs )
+
+       show_result( conf, pkgs )
+
+if __name__ == "__main__":
+       main()
+

diff --git a/pym/gentoolkit/imlate/imlate.1 b/pym/gentoolkit/imlate/imlate.1
new file mode 100644
index 0000000..b9163a4
--- /dev/null
+++ b/pym/gentoolkit/imlate/imlate.1
@@ -0,0 +1,48 @@
+.TH "imlate" "1" "1.0.0" "Christian Ruppert" "gentoolkit-dev"
+.SH "NAME"
+.LP 
+imlate \- Displays candidates for keywords for an architecture based upon a 
target architecture.
+.SH "SYNTAX"
+.LP 
+imlate [options]
+
+
+.SH "OPTIONS"
+.TP 
+.B \-\-version
+show program's version number and exit
+.TP 
+.B \-h, \-\-help
+show this help message and exit
+.TP 
+.B \-f FILE, \-\-file=FILE
+write result into FILE [default: stdout]
+.TP 
+.B \-m ARCH, \-\-main=ARCH  
+set main ARCH (e.g. your arch) [default: amd64]
+.TP 
+.B \-t ARCH, \-\-target=ARCH
+set target ARCH (e.g. x86) [default: x86]
+.TP
+.B \-\-mtime=MTIME
+set minimum MTIME in days [default: 30]
+.TP 
+.B \-s, \-\-stable
+just show stable candidates (e.g. \-s and \-k is the default result) [default: 
True]
+.TP 
+.B \-k, \-\-keyword
+just show keyword candidates (e.g. \-s and \-k is the default result) 
[default: True]
+.TP
+.B \-M MAINTAINER, \-\-maintainer=MAINTAINER
+Show only packages from the specified maintainer
+.TP
+.B \-H HERD, \-\-herd=HERD
+Show only packages from the specified herd
+.TP 
+.B \-C CATEGORIES, \-\-category=CATEGORIES, \-\-categories=CATEGORIES
+just check in the specified category/categories (comma separated) [default: 
none]
+.SH "AUTHORS"
+.LP 
+Christian Ruppert <id...@gentoo.org>
+.SH "BUGS"
+Please report any bugs to http://bugs.gentoo.org

Reply via email to