# Licensed under the GPL v2
PROGRAM_NAME=euse
- PROGRAM_VERSION=$(cat /usr/share/gentoolkit/VERSION)
+ VERSION="svn"
- MAKE_CONF_PATH=/etc/make.conf
- MAKE_GLOBALS_PATH=/etc/make.globals
- MAKE_PROFILE_PATH=/etc/make.profile
- MAKE_CONF_BACKUP_PATH=/etc/make.conf.euse_backup
- PACKAGE_USE_PATH=/etc/portage/package.use
+ EPREFIX=${EPREFIX:-$(portageq envvar EPREFIX)}
+ ETC="${EPREFIX}/etc"
+ USR_SHARE_PORTAGE="${EPREFIX}/usr/share/portage"
+
-# define error function so it can be used immediately
-error() {
- echo "ERROR: ${1}"
++# define error functions so they can be used immediately
++fatal() {
++ echo -e "ERROR: ${*}"
+ set +f
+ exit 1
+ }
+
++error() {
++ echo -e "ERROR: ${*}"
++}
++
++warn() {
++ echo -e "WARNING: ${*}"
++}
++
+ # /etc/make.conf can now exist in /etc/portage/make.conf, prefer it over /etc/make.conf for changes
+ if [ -e "${ETC}/portage/make.conf" ]; then
+ MAKE_CONF_PATH="${ETC}/portage/make.conf"
+ elif [ -e "${ETC}/make.conf" ]; then
+ MAKE_CONF_PATH="${ETC}/make.conf"
+ else
- error "make.conf does not exist"
++ fatal "make.conf does not exist"
+ fi
+ MAKE_CONF_BACKUP_PATH="${MAKE_CONF_PATH}.euse_backup"
+
+ # /etc/make.globals has been moved to /usr/share/portage/config/make.globals
+ if [ -e "${USR_SHARE_PORTAGE}/config/make.globals" ]; then
+ MAKE_GLOBALS_PATH="${USR_SHARE_PORTAGE}/config/make.globals"
+ else
+ MAKE_GLOBALS_PATH="${ETC}/make.globals"
+ fi
+
+ # /etc/make.profile or /etc/portage/make.profile, if /etc/make.profile exists, it will be used
+ if [ -e "${ETC}/make.profile" ]; then
+ MAKE_PROFILE_PATH="${ETC}/make.profile"
+ elif [ -e "${ETC}/portage/make.profile" ]; then
+ MAKE_PROFILE_PATH="${ETC}/portage/make.profile"
+ else
- error "make.profile does not exist"
++ fatal "make.profile does not exist"
+ fi
++PACKAGE_USE_PATH=${ETC}/portage/package.use
[ -z "${MODE}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
# file permission tests
local descdir
local make_defaults
-
+ local make_conf
+
+ [[ ! -d "${MAKE_PROFILE_PATH}" || ! -r "${MAKE_PROFILE_PATH}" ]] && error "${MAKE_PROFILE_PATH} is not readable"
+ #
+ for make_conf in $(get_all_make_conf); do
- [ ! -r "${make_conf}" ] && error "${make_conf} is not readable"
++ [ ! -r "${make_conf}" ] && fatal "${make_conf} is not readable"
+ done
+
descdir="$(get_portdir)/profiles"
- [ ! -r "${MAKE_CONF_PATH}" ] && fatal "${MAKE_CONF_PATH} is not readable"
+
- [ ! -h "${MAKE_PROFILE_PATH}" ] && fatal "${MAKE_PROFILE_PATH} is not a symlink"
+ [ ! -r "${MAKE_GLOBALS_PATH}" ] && fatal "${MAKE_GLOBALS_PATH} is not readable"
- [ ! -r "${MAKE_GLOBALS_PATH}" ] && error "${MAKE_GLOBALS_PATH} is not readable"
- [ -z "$(get_portdir)" ] && error "\$PORTDIR couldn't be determined"
- [ ! -d "${descdir}" ] && error "${descdir} does not exist or is not a directory"
- [ ! -r "${descdir}/use.desc" ] && error "${descdir}/use.desc is not readable"
- [ ! -r "${descdir}/use.local.desc" ] && error "${descdir}/use.local.desc is not readable"
+ [ -z "$(get_portdir)" ] && fatal "\$PORTDIR couldn't be determined"
+ [ ! -d "${descdir}" ] && fatal "${descdir} does not exist or is not a directory"
+ [ ! -r "${descdir}/use.desc" ] && fatal "${descdir}/use.desc is not readable"
+ [ ! -r "${descdir}/use.local.desc" ] && fatal "${descdir}/use.local.desc is not readable"
+
for make_defaults in $(get_all_make_defaults); do
- [ ! -r "$make_defaults" ] && error "$_make_defaults is not readable"
+ [ ! -r "$make_defaults" ] && fatal "$_make_defaults is not readable"
done
-# [ ! -r "$(get_make_defaults)" ] && error "$(get_make_defaults) is not readable"
- [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && error ""${MAKE_CONF_PATH}" is not writable"
-}
+ [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && fatal ""${MAKE_CONF_PATH}" is not writable"
+ [ "${MODE}" == "modify" -a -s "${PACKAGE_USE_PATH}" -a ! -w "${PACKAGE_USE_PATH}" ] && fatal ""${PACKAGE_USE_PATH}" is not writable"
+} # }}}
showhelp() {
cat << HELP
# backup portdir so get_portdir() doesn't give false results later
portdir_backup="${PORTDIR}"
-
+
ACTIVE_FLAGS[0]="$(reduce_incrementals ${USE})"
USE=""
- source "${MAKE_CONF_PATH}"
- ACTIVE_FLAGS[1]="$(reduce_incrementals ${USE})"
+ for x in $(get_all_make_conf); do
+ source "${x}"
+ ACTIVE_FLAGS[1]="$(reduce_incrementals ${ACTIVE_FLAGS[1]} ${USE})"
+ done
USE=""
for x in $(get_all_make_defaults); do
source "${x}"
USE="${ACTIVE_FLAGS[0]}"
PORTDIR="${portdir_backup}"
+ #
+ # Traverse through use.mask and use.force (0.5s)
+ # Flip signs of use.mask (it's interpreted oppositely),
+ ACTIVE_FLAGS[6]=$(reduce_incrementals_trump \
+ $(cat $(traverse_profile "use.mask") | sed -re "/^#.*$/{d}") \
+ | sed -re "s/(^| )-[^ ]*//g" -e "s/(^| )([a-z0-9])/ -\2/g")
+ ACTIVE_FLAGS[7]=$(reduce_incrementals \
+ $(cat $(traverse_profile "use.force") \
+ | sed -re "/^#.*$/ {d}"))
+
+ USE_FLAGS_CALCULATED=1
+} # }}}
+
+# Function: get_portageuseflags # {{{
+# Fetch USE flags reported active by Portage
+get_portageuseflags() {
+ # only calculate once as calling emerge is painfully slow
+ [ -n "${_PORTAGE_USE_FLAGS_CALCULATED}" ] && return
# get the currently active USE flags as seen by portage, this has to be after
# restoring USE or portage won't see the original environment
- ACTIVE_FLAGS[9]="$(emerge --ignore-default-opts --info | grep 'USE=' | cut -b 5- | sed -e 's:"::g')" #'
+ # Bug 181309, emerge may complain if EMERGE_DEFAULT_OPTS="--ask" is set
- USE_FLAGS_CALCULATED=1
-}
-
-# get the list of all known USE flags by reading use.desc and/or use.local.desc
-# (depending on the value of $SCOPE)
+ ACTIVE_FLAGS[9]="$(portageq envvar USE)" #'
+ _PORTAGE_USE_FLAGS_CALCULATED=1
+} # }}}
+
+# Function: get_useflaglist {{{
+# Get the list of all known USE flags by reading use.desc and/or
+# use.local.desc (depending on the value of $SCOPE). Also searches any
+# registered overlays after searching the main portage tree first.
+# Use flags visible in both the main tree and overlays are trumped by
+# the main tree. Overlays are indicated by brackets [xxx] at the
+# beginning of the description.
+#
+# Returns:
+# (written to stdout) Sorted, unique list of system-wide USE flags and
+# descriptions. Flags defined in overlays have the overlay in brackets
+# prepended to the descriptions.
+#
+# Environment:
+# SCOPE - [local|global] constrain search to local (use.local.desc) or
+# global (use.desc)
get_useflaglist() {
local descdir
-
- descdir="$(get_portdir)/profiles"
-
- if [ -z "${SCOPE}" -o "${SCOPE}" == "global" ]; then
- egrep "^[^# ]+ +-" "${descdir}/use.desc" | cut -d\ -f 1
- fi
- if [ -z "${SCOPE}" -o "${SCOPE}" == "local" ]; then
- egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" | cut -d: -f 2 | cut -d\ -f 1
+ local overlay
+ for profiledir in ${ALL_PORTDIRS[@]}; do
+ descdir="${profiledir}/profiles"
+ if [[ -z ${SCOPE} || ${SCOPE} == "global" ]]; then
+ [[ ! -s "${descdir}/use.desc" ]] && continue
+ egrep "^[^# ]+ +-" "${descdir}/use.desc"
+ fi
+ if [[ -z ${SCOPE} || ${SCOPE} == "local" ]]; then
+ [[ ! -s "${descdir}/use.local.desc" ]] && continue
+ egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" \
+ | cut -d: -f 2
+ fi
+ done | cut -d " " -f1 | sort --field=":" --key=1,1 --unique
+} # }}}
+
+# Function: get_useflaglist_ebuild {{{
+# Builds USE flag information for specified package atom into
+# ACTIVE_FLAGS[5]. For the atom, the versions available are found, and
+# for each, the corresponding SLOT, IUSE are stored along with which
+# overlay the ebuild lives in. Considering that the pieces of information
+# may be required in any order or any subsets, it is intended for the
+# function to cache the information and it be retrieved from
+# ACTIVE_FLAGS[5]. So the format of ACTIVE_FLAGS[5] is newline-separated
+# list of:
+#
+# category/packge;version;SLOT;IUSE;overlay
+#
+# Arguments:
+# $1 - Package atom to lookup (app-editor/vim)
+#
+# Returns:
+# Nothing significant
+#
+# Environment:
+# PORTDIR - Root of portage tree
+# ACTIVE_FLAGS - Array of current use flag info
+#
+get_useflaglist_ebuild() {
+ local known=$(echo "${ACTIVE_FLAGS[5]}" | egrep "^${1}")
+ if [[ -n $known ]]; then
+ # No need to recache
+ return
fi
-}
+ local pkg=$(echo ${1} | cut -d/ -f2)
+ declare append
+ for portdir in ${ALL_PORTDIRS[@]}; do
+ # Open the ebuild file and retrieve defined USE flags
+ [[ ! -d "$portdir/${1}" ]] && continue
+ append=$(echo -n $portdir/${1}/*.ebuild "" \
+ | perl -pne "s:$portdir/${1}/${pkg}-(([^.]|\.(?!e))+)\.ebuild:\1:g" \
+ | while read -d " " version; do
+ IFS=$'\n'
+ if [[ $portdir == $PORTDIR ]]; then
+ overlay=""
+ else
+ if [[ -s $(dirname ${portdir}/repo_name) ]]; then
+ overlay="$(cat "${portdir}/profiles/repo_name")"
+ else
+ # XXX: May be better to use full path
+ overlay="$(basename "${portdir}")"
+ fi
+ fi
+ if [[ ! -d "$portdir/metadata/cache" ]]; then
+ echo "!!! Metadata cache not found. You need to run " >&2
+ echo "!!! 'egencache --repo=$overlay --update'" >&2
+ echo "!!! to generate metadata for your overlays" >&2
+ return 1
+ elif [[ ! -e "$portdir/metadata/cache/${1}-$version" ]]; then
+ # Repo does not have this particular package
+ continue
+ fi
+ iuse=$(head -11 "$portdir/metadata/cache/${1}-$version"|tail -1)
+ slot=$(head -3 "$portdir/metadata/cache/${1}-$version"|tail -1)
+ echo "${1};${version};${slot};${iuse};${overlay}"
+ done
+ )
+ if [[ -z ${ACTIVE_FLAGS[5]} ]]; then ACTIVE_FLAGS[5]="$append"
+ else ACTIVE_FLAGS[5]="${ACTIVE_FLAGS[5]}"$'\n'"$append"
+ fi
+ done
+} # }}}
-# get all make.defaults by traversing the cascaded profile directories
-get_all_make_defaults() {
+ # get all make.conf files that exist on the system
+ get_all_make_conf() {
+ # At least one of the files exists or we would not have made it this far
+ for x in ${ETC}/make.conf ${ETC}/portage/make.conf; do
+ [ -e "${x}" ] && echo "${x}"
+ done
+ }
+# Function: traverse_profile {{{
+# General method of collecting the contents of a profile
+# component by traversing through the cascading profile
+#
+# Arguments:
+# $1 - Filename (make.profile)
+# [$2] - Current directory (unspecified means to start at the top)
+traverse_profile() {
local curdir
local parent
local rvalue
local current_desc
local found_one
local args
-
+
+ set -f
args="${*:-*}"
-
+
if [ -z "${SCOPE}" ]; then
SCOPE="global" showdesc ${args}
echo
[ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
[ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
echo "************************************************************"
-
+ set +f
if [ "${args}" == "*" ]; then
- args="$(get_useflaglist | sort -u)"
+ args="${useflags[*]}"
fi
-
+
set ${args}
foundone=0
- while [ -n "${1}" ]; do
- if [ "${SCOPE}" == "global" ]; then
- if grep "^${1} *-" "${descdir}/use.desc" > /dev/null; then
+ while [[ -n "${1}" ]]; do
+ if [[ "${SCOPE}" == "global" ]]; then
+ if array_contains "${useflags[*]}" "$1"; then
get_flagstatus "${1}"
+ # XXX: Handle overlay
+ grep "^${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.desc} 2> /dev/null \
+ | sed -re "s/^([^:]+)://"
foundone=1
fi
- grep "^${1} *-" "${descdir}/use.desc"
fi
- # local flags are a bit more complicated as there can be multiple
+ # local flags are a bit more complicated as there can be multiple
# entries per flag and we can't pipe into printf
- if [ "${SCOPE}" == "local" ]; then
- if grep ":${1} *-" "${descdir}/use.local.desc" > /dev/null; then
+ if [[ "${SCOPE}" == "local" ]]; then
+ if array_contains "${useflags[*]}" "$1"; then
foundone=1
fi
- grep ":${1} *-" "${descdir}/use.local.desc" \
- | sed -e "s/^\([^:]\+\):\(${1}\) *- *\(.\+\)/\1|\2|\3/g" \
- | while read line; do
- pkg="$(echo $line | cut -d\| -f 1)"
- flag="$(echo $line | cut -d\| -f 2)"
- desc="$(echo $line | cut -d\| -f 3)"
- get_flagstatus "${flag}"
- printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc}"
- done
+ # Fetch all the packages data using this flag
+ infos=$( grep ":${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.local.desc} 2> /dev/null \
+ | sed -re "s/^([^:]+):([^:]+):(${1}) *- *(.+)/\1|\2|\3|\4/g")
+ OIFS=$IFS; IFS=$'\n'; infos=($infos); IFS=$OIFS;
+ for line in "${infos[@]}"; do
+ OIFS=$IFS; IFS="|"; line=($line); IFS=$OIFS
+ pkg=${line[1]}
+ flag=${line[2]}
+ desc=${line[3]}
+ if get_flagstatus "${flag}"; then
+ ACTIVE="+"
+ else
+ ACTIVE="-"
+ fi
+ printf "%s\n" "${flag}"
+ printf "%s: %s\n" "${pkg}" "${desc}" \
+ | fold --width=$((${COLUMNS:-80}-10)) -s | sed -e "s/^/ /g"
+ get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}"
+ done
fi
shift
done
if [ "${args}" == "*" ]; then
args="$(get_useflaglist | sort -u)"
fi
-
+
set ${args}
-
+ get_portageuseflags
+
while [ -n "${1}" ]; do
if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
printf "%-20s" ${1}
echo -ne "${NEW_MAKE_CONF_USE_2%% }"
echo '"'
fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
-
+
echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
-}
+} # }}}
##### main program comes now #####