2 # Disable globbing because "-*" and such is valid as a use flag.
7 # bash replacement for the original euse by Arun Bhanu
8 # Author: Marius Mauch <genone@gentoo.org>
9 # Jared Hancock (Signigicant rewrite for package.use support)
10 # Licensed under the GPL v2
15 EPREFIX=${EPREFIX:-$(portageq envvar EPREFIX)}
17 USR_SHARE_PORTAGE="${EPREFIX}/usr/share/portage"
19 # Arrays containing the known repository names and repository profile paths
20 PORTAGE_REPOS=( $(portageq get_repos ${EPREFIX}/) )
21 PORTAGE_REPO_PATHS=( $(portageq get_repo_path ${EPREFIX}/ ${PORTAGE_REPOS[@]}) )
23 # define error functions so they can be used immediately
35 echo -e "WARNING: ${*}"
38 # /etc/make.conf now exists as /etc/portage/make.conf by default, prefer
39 # it over /etc/make.conf for changes.
40 if [[ -e "${ETC}/portage/make.conf" ]]; then
41 MAKE_CONF_PATH="${ETC}/portage/make.conf"
42 elif [[ -e "${ETC}/make.conf" ]]; then
43 MAKE_CONF_PATH="${ETC}/make.conf"
45 fatal "make.conf does not exist"
47 MAKE_CONF_BACKUP_PATH="${MAKE_CONF_PATH}.euse_backup"
49 # /etc/make.globals has been moved to /usr/share/portage/config/make.globals
50 if [ -e "${USR_SHARE_PORTAGE}/config/make.globals" ]; then
51 MAKE_GLOBALS_PATH="${USR_SHARE_PORTAGE}/config/make.globals"
53 MAKE_GLOBALS_PATH="${ETC}/make.globals"
56 # /etc/make.profile or /etc/portage/make.profile, if /etc/make.profile exists, it will be used
57 if [ -e "${ETC}/make.profile" ]; then
58 MAKE_PROFILE_PATH="${ETC}/make.profile"
59 elif [ -e "${ETC}/portage/make.profile" ]; then
60 MAKE_PROFILE_PATH="${ETC}/portage/make.profile"
62 fatal "make.profile does not exist"
64 PACKAGE_USE_PATH=${ETC}/portage/package.use
66 [ -z "${MODE:-}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
69 if [ -z "${1}" ]; then
72 while [ -n "${1}" ]; do
74 -h | --help) MODE="showhelp";;
75 -V | -v | --version) MODE="showversion";;
76 -i | --info) MODE="showdesc";;
77 -I | --info-installed) MODE="showinstdesc";;
78 -l | --local) SCOPE="local";;
79 -g | --global) SCOPE="global";;
80 -a | --active) MODE="showflags";;
81 -E | --enable) MODE="modify"; ACTION="add";;
82 -D | --disable) MODE="modify"; ACTION="remove";;
83 -P | --prune | -R | --remove)
84 MODE="modify"; ACTION="prune";;
85 -p | --package) MODE="modify"; shift; PACKAGE=${1}; SCOPE="local";;
87 echo "ERROR: unknown option ${1} specified."
93 ARGUMENTS="${ARGUMENTS:-} ${ACTIVE_FLAGS[9]}"
96 ARGUMENTS="${ARGUMENTS:-} ${1}"
111 # Function: check_sanity {{{
112 # Performs some basic system sanity checks
114 # file permission tests
119 [[ ! -d "${MAKE_PROFILE_PATH}" || ! -r "${MAKE_PROFILE_PATH}" ]] && error "${MAKE_PROFILE_PATH} is not readable"
121 for make_conf in $(get_all_make_conf); do
122 [ ! -r "${make_conf}" ] && fatal "${make_conf} is not readable"
125 descdir="$(get_portdir)/profiles"
127 [ ! -r "${MAKE_GLOBALS_PATH}" ] && fatal "${MAKE_GLOBALS_PATH} is not readable"
128 [ -z "$(get_portdir)" ] && fatal "\$PORTDIR couldn't be determined"
129 [ ! -d "${descdir}" ] && fatal "${descdir} does not exist or is not a directory"
130 [ ! -r "${descdir}/use.desc" ] && fatal "${descdir}/use.desc is not readable"
131 [ ! -r "${descdir}/use.local.desc" ] && fatal "${descdir}/use.local.desc is not readable"
133 for make_defaults in $(get_all_make_defaults); do
134 [ ! -r "$make_defaults" ] && fatal "$_make_defaults is not readable"
136 [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && fatal ""${MAKE_CONF_PATH}" is not writable"
137 [ "${MODE}" == "modify" -a -s "${PACKAGE_USE_PATH}" -a ! -w "${PACKAGE_USE_PATH}" ] && fatal ""${PACKAGE_USE_PATH}" is not writable"
143 ${PROGRAM_NAME} (${VERSION})
145 Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]
147 Options: -h, --help - show this message
148 -V, --version - show version information
149 -i, --info - show descriptions for the given useflags
150 -I, --info-installed - show descriptions for the given useflags and
151 their current impact on the installed system
152 -g, --global - show only global use flags (suboption)
153 -l, --local - show only local use flags (suboption)
154 -a, --active - show currently active useflags and their origin
155 -E, --enable - enable the given useflags
156 -D, --disable - disable the given useflags
157 -R, --remove - remove all references to the given flags from
158 make.conf and package.use to revert to default
160 -P, --prune - alias for --remove
161 -p, --package - used with -E, -D, and -R to apply to a
162 specific package only
164 Notes: ${PROGRAM_NAME} currently works for global flags defined
165 in make.globals, make.defaults, make.conf, use.force, and use.mask
166 and local flags defined in package.use and individual package ebuilds.
167 It might have issues with cascaded profiles. If multiple options are
168 specified only the last one will be used.
174 ${PROGRAM_NAME} (${VERSION})
175 Written by Marius Mauch
177 Copyright (C) 2004-2009 Gentoo Foundation, Inc.
178 This is free software; see the source for copying conditions.
182 # Function: reduce_incrementals {{{
183 # remove duplicate flags from the given list in both positive and negative forms
184 # (but unlike portage always keep the last value even if it's negative)
185 # Otherwise the status flags could be incorrect if a flag appers multiple times in
186 # one location (like make.conf).
187 # Using python here as bash sucks for list handling.
188 # NOTE: bash isn't actually that bad at handling lists -- sh is. This may be
189 # worth another look to avoid calling python unnecessariy. Or we could
190 # just write the whole thing in python. ;)
191 reduce_incrementals() {
192 echo $@ | python -c "from __future__ import print_function;import sys
194 for x in sys.stdin.read().split():
195 if x[0] == '-' and x[1:] in r:
198 elif x[0] != '-' and '-'+x in r:
201 elif x == '-*': r = ['-*']
202 elif x not in r: r.append(x)
206 # Function: reduce_incrementals_trump {{{
207 # Similar to reduce_incrementals but negative flags trump positive
208 # flags, regardless of which follows which
209 reduce_incrementals_trump() {
210 echo $@ | python -c "from __future__ import print_function;import sys
212 for x in sys.stdin.read().split():
213 if x[0] == '-' and x[1:] in r:
216 elif x == '-*': r = ['-*']
217 elif x not in r and not '-'+x in r: r.append(x)
221 # Function: reduce_package_use {{{
222 # Similar to reduce_incrementals except converts lines from package atoms
223 # in /etc/portage/package.use files to lines of "pkg {[-]flag}*"
226 # * - Lines of package atom followed by flags
227 # (app-editors/vim flag1 flag2 -flag3)
228 reduce_package_use() {
229 echo "${@}" | python -c "from __future__ import print_function;import sys,re
230 h={}; getflags=re.compile(r'(-?[\w*-]+)')
231 for x in sys.stdin.read().split('\n'):
233 parts = x.lstrip().split(' ',1)
234 if len(parts)==1: continue
236 flags = getflags.findall(parts[1])
237 if not pkg in h: h[pkg]=[]
240 if x[0] == '-' and x[1:] in r:
243 elif x[0] != '-' and '-'+x in r:
246 elif x == '-*': r = h[pkg] = ['-*']
249 print('\n'.join(['%s %s' % (pkg,' '.join(flgs)) for pkg,flgs in h.items() if len(flgs)]))"
252 # Function: get_useflags {{{
253 # Creates a bash array ACTIVE_FLAGS that contains the global use flags,
254 # indexed by origin: 0: environment, 1: make.conf, 2: make.defaults,
255 # 3: make.globals, and local use flags, indexed by origin: 4: package.use,
256 # 5: ebuild IUSE, 6: use.mask, 7: use.force,
257 # 9: flags indicated active by emerge --info (get_portageuseflags)
260 if [[ -z ${ACTIVE_FLAGS[4]} && ( $SCOPE == "local" || -z $SCOPE ) ]]; then
261 # Parse through /etc/portage/package.use
262 if [[ -d ${PACKAGE_USE_PATH} ]]; then
263 ACTIVE_FLAGS[4]="$( cat ${PACKAGE_USE_PATH}/* \
264 | sed -re "s/ *#.*$//g" -e "s/^ *$//g" )"
265 elif [[ -e ${PACKAGE_USE_PATH} ]]; then
266 # JWM, 23/12/2009: I edited this following line but I'm not sure if it's 100% correct.
267 ACTIVE_FLAGS[4]="$( sed -re "s/ *#.*$//g" -e "s/^ *$//g" \
268 ${PACKAGE_USE_PATH})"
270 # Simplify ACTIVE_FLAGS[4] to be lines of pkg {[-]flag}*
271 ACTIVE_FLAGS[4]="$(reduce_package_use "${ACTIVE_FLAGS[4]}")"
273 # ACTIVE_FLAGS[5] reserved for USE flags defined in ebuilds and
274 # is generated/maintained in the get_useflaglist_ebuild() function
277 # only calculate once as calling emerge is painfully slow
278 [ -n "${USE_FLAGS_CALCULATED}" ] && return
280 # backup portdir so get_portdir() doesn't give false results later
281 portdir_backup="${PORTDIR}"
283 ACTIVE_FLAGS[0]="$(reduce_incrementals "${USE}")"
285 for x in $(get_all_make_conf); do
287 ACTIVE_FLAGS[1]="$(reduce_incrementals "${ACTIVE_FLAGS[1]}" "${USE}")"
290 for x in $(get_all_make_defaults); do
292 ACTIVE_FLAGS[2]="${ACTIVE_FLAGS[2]} ${USE}"
294 ACTIVE_FLAGS[2]="$(reduce_incrementals "${ACTIVE_FLAGS[2]}")"
296 source "${MAKE_GLOBALS_PATH}"
297 ACTIVE_FLAGS[3]="$(reduce_incrementals "${USE}")"
299 # restore saved env variables
300 USE="${ACTIVE_FLAGS[0]}"
301 PORTDIR="${portdir_backup}"
304 # Traverse through use.mask and use.force (0.5s)
305 # Flip signs of use.mask (it's interpreted oppositely),
306 ACTIVE_FLAGS[6]=$(reduce_incrementals_trump \
307 $(cat $(traverse_profile "use.mask") | sed -re "/^#.*$/{d}") \
308 | sed -re "s/(^| )-[^ ]*//g" -e "s/(^| )([a-z0-9])/ -\2/g")
309 ACTIVE_FLAGS[7]=$(reduce_incrementals \
310 $(cat $(traverse_profile "use.force") \
311 | sed -re "/^#.*$/ {d}"))
313 USE_FLAGS_CALCULATED=1
317 # Function: get_portageuseflags # {{{
318 # Fetch USE flags reported active by Portage
319 get_portageuseflags() {
320 # only calculate once as calling emerge is painfully slow
321 [ -n "${_PORTAGE_USE_FLAGS_CALCULATED}" ] && return
322 # get the currently active USE flags as seen by portage, this has to be after
323 # restoring USE or portage won't see the original environment
324 # Bug 181309, emerge may complain if EMERGE_DEFAULT_OPTS="--ask" is set
325 ACTIVE_FLAGS[9]="$(portageq envvar USE)" #'
326 _PORTAGE_USE_FLAGS_CALCULATED=1
329 # Function: get_useflaglist {{{
330 # Get the list of all known USE flags by reading use.desc and/or
331 # use.local.desc (depending on the value of $SCOPE). Also searches any
332 # registered overlays after searching the main portage tree first.
333 # Use flags visible in both the main tree and overlays are trumped by
334 # the main tree. Overlays are indicated by brackets [xxx] at the
335 # beginning of the description.
338 # (written to stdout) Sorted, unique list of system-wide USE flags and
339 # descriptions. Flags defined in overlays have the overlay in brackets
340 # prepended to the descriptions.
343 # SCOPE - [local|global] constrain search to local (use.local.desc) or
348 for profiledir in ${ALL_PORTDIRS[@]}; do
349 descdir="${profiledir}/profiles"
350 if [[ -z ${SCOPE} || ${SCOPE} == "global" ]]; then
351 [[ ! -s "${descdir}/use.desc" ]] && continue
352 egrep "^[^# ]+ +-" "${descdir}/use.desc"
354 if [[ -z ${SCOPE} || ${SCOPE} == "local" ]]; then
355 [[ ! -s "${descdir}/use.local.desc" ]] && continue
356 egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" \
359 done | cut -d " " -f1 | sort --field=":" --key=1,1 --unique
362 # Function: get_useflaglist_ebuild {{{
363 # Builds USE flag information for specified package atom into
364 # ACTIVE_FLAGS[5]. For the atom, the versions available are found, and
365 # for each, the corresponding SLOT, IUSE are stored along with which
366 # overlay the ebuild lives in. Considering that the pieces of information
367 # may be required in any order or any subsets, it is intended for the
368 # function to cache the information and it be retrieved from
369 # ACTIVE_FLAGS[5]. So the format of ACTIVE_FLAGS[5] is newline-separated
372 # category/packge;version;SLOT;IUSE;overlay
375 # $1 - Package atom to lookup (app-editor/vim)
378 # Nothing significant
381 # PORTDIR - Root of portage tree
382 # ACTIVE_FLAGS - Array of current use flag info
384 get_useflaglist_ebuild() {
385 local known=$(echo "${ACTIVE_FLAGS[5]}" | egrep "^${1}")
387 if [[ -n $known ]]; then
391 local pkg=$(echo ${1} | cut -d/ -f2)
393 for portdir in ${ALL_PORTDIRS[@]}; do
394 if [[ -s "${portdir}/profiles/repo_name" ]]; then
395 overlay="$(cat "${portdir}/profiles/repo_name")"
397 # XXX: Portage uses "x-<basename of the overlay>
398 overlay="x-$(basename "${portdir}")"
400 # Open the ebuild file and retrieve defined USE flags
401 [[ ! -d "$portdir/${1}" ]] && continue
402 cacheformat="unknown"
403 [[ -d "$portdir/metadata/cache" ]] && cacheformat="cache" # format is pms
404 [[ -d "$portdir/metadata/md5-cache" ]] && cacheformat="md5-cache" # format is md5-cache
405 if [[ "$cacheformat" == "unknown" ]]; then
406 echo "!!! Metadata cache not found. You need to run " >&2
407 echo "!!! 'egencache --repo=$overlay --update'" >&2
408 echo "!!! to generate metadata for your overlays" >&2
411 append=$(set +f; ls ${portdir}/metadata/${cacheformat}/${1}-* \
412 | egrep "${1}-[0-9.]+" \
413 | sed -e "s:${portdir}/metadata/${cacheformat}/${1}-::g" \
414 | while read -d $'\n' version; do
416 if [[ ! -e "${portdir}/metadata/${cacheformat}/${1}-$version" ]]; then
417 # Repo does not have this particular package
420 if [[ "${cacheformat}" == "cache" ]]; then
421 iuse=$(head -n 11 "${portdir}/metadata/${cacheformat}/${1}-$version"|tail -n 1)
422 slot=$(head -n 3 "${portdir}/metadata/${cacheformat}/${1}-$version"|tail -n 1)
423 elif [[ "${cacheformat}" == "md5-cache" ]]; then
424 iuse=$(grep "^IUSE=" "${portdir}/metadata/${cacheformat}/${1}-$version" | sed 's/^IUSE=//')
425 slot=$(grep "^SLOT=" "${portdir}/metadata/${cacheformat}/${1}-$version" | sed 's/^SLOT=//')
427 # This is a bug, we should have already returned
430 echo "${1};${version};${slot};${iuse};${overlay}"
433 if [[ -z ${ACTIVE_FLAGS[5]} ]]; then ACTIVE_FLAGS[5]="$append"
434 else ACTIVE_FLAGS[5]="${ACTIVE_FLAGS[5]}"$'\n'"$append"
439 # get all make.conf files that exist on the system
440 get_all_make_conf() {
441 # At least one of the files exists or we would not have made it this far
442 for x in ${ETC}/make.conf ${ETC}/portage/make.conf; do
443 [ -e "${x}" ] && echo "${x}"
446 # Function: traverse_profile {{{
447 # General method of collecting the contents of a profile
448 # component by traversing through the cascading profile
450 # Bug #414961 allows ':' shorthand to resolve to the ${PORTDIR}/profiles
451 # A value before the ':' references the repository, no value means gentoo
453 # Example: local:base would refer to the profiles directory in the repository
454 # path owned by the 'local' repository
457 # $1 - Filename (make.profile)
458 # [$2] - Current directory (unspecified means to start at the top)
464 curdir="${2:-$(get_real_path ${MAKE_PROFILE_PATH})}"
466 if [[ -f "${curdir}/parent" ]]; then
467 for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
468 # Bug 231394, handle parent path being absolute
469 index=$(expr index "${parent}" :)
470 if [[ ${parent:0:1} == "/" ]]; then
471 pdir="$(get_real_path ${parent})"
472 elif [[ $index -eq 0 ]]; then
473 pdir="$(get_real_path ${curdir}/${parent})"
475 # We have a path with a colon shortcut
477 repo="${parent:0:${i}}"
478 [[ -z "${repo}" ]] && repo="gentoo"
479 parent="${parent:$index}"
480 limit=${#PORTAGE_REPOS[@]}
481 for ((i=0; i < limit ; i++)); do
482 if [[ ${repo} == ${PORTAGE_REPOS[i]} ]]; then
483 parent="${PORTAGE_REPO_PATHS[i]}/profiles/${parent}"
487 pdir="$(get_real_path ${parent})"
489 rvalue="${rvalue} $(traverse_profile ${1} ${pdir})"
492 [[ -f "${curdir}/${1}" ]] && rvalue="${rvalue} ${curdir}/${1}"
497 # Function: get_all_make_defaults {{{
498 # Det all make.defaults by traversing the cascaded profile directories
499 get_all_make_defaults() {
500 if [[ -z ${MAKE_DEFAULTS:-} ]]; then
501 MAKE_DEFAULTS=$(traverse_profile "make.defaults")
505 MAKE_DEFAULTS=$(get_all_make_defaults)
507 # Function: get_flagstatus_helper # {{{
508 # Little helper function to get the status of a given flag in one of the
509 # ACTIVE_FLAGS elements.
512 # (Written to STDOUT) Flag active status (+/-) or default string given
513 # in argument 4 or an empty space
517 # 2 - index of ACTIVE_FLAGS
518 # 3 - echo value for positive (and as lowercase for negative) test result
519 # 4 - (optional) echo value for "missing" test result, defaults to blank
520 get_flagstatus_helper() {
521 if [[ -z ${flags} ]]; then
522 local flags=${ACTIVE_FLAGS[${2}]}
524 local flag=$(echo " $flags " | grep -Eo " [+-]?${1} ")
525 if [[ ${flag:1:1} == "-" ]]; then
526 echo -e -n "${3}" | tr [:upper:]+ [:lower:]-
527 elif [[ -n ${flag} ]]; then
534 # Function: get_flagstatus_helper_pkg # {{{
535 # Entry to get_flagstatus_helper for packages which will fetch use
536 # flags set in package.use for the package and pass them on to
537 # get_flagstatus_helper. Also correcly handles lines in package.use
538 # specified for individual package versions
539 get_flagstatus_helper_pkg() {
540 if [[ -z ${2} ]]; then
543 elif [[ -z ${flags} ]]; then
544 # If no atoms are matchers (start with >,<,=, then they all match
546 if [[ -z "${atoms[@]/[<>=]*/}" ]]; then
548 echo "${atoms[@]}" | python -c "
549 from __future__ import print_function;import portage.dep as dep, sys
550 print(' '.join(dep.match_to_list('$5-$6',sys.stdin.read().split())))"))
552 flags=$(for atom in ${atoms[@]}; do
553 [[ -z $atom ]] && continue
554 echo "${ACTIVE_FLAGS[4]}" | \
555 grep "^ *$atom" | cut -d\ -f2-
558 if [[ -z ${5} || -z ${flags} ]]; then
562 get_flagstatus_helper "$@"
565 # Function: get_flagstatus_helper_ebuild {{{
566 # get_flagstatus_helper replacement for packages to fetch ebuild USE flag
570 # (Written to STDOUT) Flag active status (+/-) or default string given
571 # in argument 4 or an empty space. If USE flag is not defined in the list
572 # of flags (2), an '!' is written
576 # 2 - IUSE line from ebuild file
577 # 3 - echo value for positive (and as lowercase for negative) test result
578 # 4 - (optional) echo value for "missing" test result, defaults to blank space
579 get_flagstatus_helper_ebuild() {
580 local flags=$(echo $2 | cut -d\" -f2)
581 local flag=$(echo " $flags " | grep -Eo " [+-]?$1 ")
582 if [[ ${flag:1:1} == "+" ]]; then
584 elif [[ ${flag:1:1} == "-" ]]; then
585 echo -en "${3}" | tr [:upper:]+ [:lower:]-
586 elif [[ -z $flag ]]; then
593 # Function: get_flagstatus {{{
594 # Prints a status string for the given flag, each column indicating the presence
595 # for portage, in the environment, in make.conf, in make.defaults, in
596 # make.globals, and in use.force and flipped in use.mask.
599 # 1 - use flag for which to retrieve status
602 # 0 (True) if flag is active, 1 (False) if not active
605 # Full positive value would be "[+ECDGFm] ", full negative value would be [-ecdgfM],
606 # full missing value would be "[- ] " (portage only sees present or not present)
610 local E=$(get_flagstatus_helper "${1}" 0 "E")
611 local C=$(get_flagstatus_helper "${1}" 1 "C")
612 local D=$(get_flagstatus_helper "${1}" 2 "D")
613 local G=$(get_flagstatus_helper "${1}" 3 "G")
614 local M=$(get_flagstatus_helper "${1}" 6 "M")
615 local F=$(get_flagstatus_helper "${1}" 7 "F")
616 # Use flags are disabled by default
619 # Use flag precedence is defined (at least) at:
620 # http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=2
621 for location in "E" "C" "D" "G" "F" "M"; do
622 if [[ ${!location} == $location ]]; then
625 elif [[ ${!location} != " " ]]; then
630 echo -n "[${ACTIVE:--}$E$C$D$G$F$M] "
631 [[ $ACTIVE == "+" ]]; return $?
634 # Function: get_flagstatus_pkg {{{
635 # Outputs local flag status information for a specific package and perhaps
636 # specific package version.
640 # 2 - package atom (www-client/elinks)
641 # 3 - +/- whether flag is enabled by global configuration files
642 # 4 - (Optional) version of package to evaluate (empty means all versions)
645 # Flag status for package.use and ebuild, slot and version, and overlay
646 # the version lives is if not PORTDIR
648 # Full positive would be "[+PB]", full negative would be "[-pb]", and full
649 # missing would be "[? ]", question because the sign will default to the
650 # sign of the global status of the flag
651 get_flagstatus_pkg() {
653 # Pre-cache the use flags declared in ebuilds first.
654 # This is required as calling it inside a $() seems to
655 # prevent caching of results into $ACTIVE_FLAGS array
656 get_useflaglist_ebuild ${2}
658 # Get list of ebuilds available for this package. The
659 # get_useflaglist_ebuild() function stores them in $ACTIVE_FLAGS[5]
660 # with the package name leading the lines. The other information
661 # is in the same line, semi-colon (;) separated. The fields are
662 # package;version;SLOT;IUSE;overlay
664 # Fetch package atoms and flags from package.use only once
665 local atoms=$(echo "${ACTIVE_FLAGS[4]}" | \
666 grep -Eo "^ *[<>]?=?${2}(-[0-9rp._*-]*)?")
667 local IFS=$'\n'; for pkgline in $(echo "${ACTIVE_FLAGS[5]}" | grep "^$2" \
668 | sort -t \; -k2,2 -V); do
669 OIFS=$IFS; IFS=";"; INFO=($pkgline); IFS=$OIFS;
670 local version=${INFO[1]}
671 [[ -n $4 && $4 != $version ]] && continue
672 local slot=${INFO[2]}
673 local iuse=${INFO[3]}
674 if [[ $slot == '0' || $slot == "" ]]; then
677 slot="($(echo ${slot} | cut -d\" -f2)) "
679 local overlay=${INFO[4]}
680 [[ -n $overlay ]] && overlay="[$overlay]"
682 # Fetch enabled status for this version
683 local P=$(get_flagstatus_helper_pkg "${1}" "${atoms}" "P" "" "${2}" "${version}")
684 local B=$(get_flagstatus_helper_ebuild "${1}" "${iuse}" "B" "" "${2}" "${version}")
687 for location in "P" "B"; do
688 if [[ ${!location} == $location ]]; then
690 elif [[ ${!location} == "!" ]]; then
693 elif [[ ${!location} != " " ]]; then
698 if [[ $UNUSED == 1 ]]; then
699 echo " ${slot}${version} ${overlay}"
701 echo " [${ACTIVE:-${3:- }}$P$B] ${slot}${version} ${overlay}"
707 # Function: get_portdir {{{
708 # faster replacement to `portageq portdir`
711 # Location of portage tree root
713 # Use a subshell so we don't have to protect the variables in
716 if [ -z "${PORTDIR:-}" ]; then
717 source "${MAKE_GLOBALS_PATH}"
718 for x in $(get_all_make_defaults); do
721 for x in $(get_all_make_conf); do
728 # This won't change while the script is running, so cache it
729 PORTDIR="$(get_portdir)"
731 # Function: get_all_overlays {{{
732 # Outputs list of portage overlays as defined in the PORTDIR_OVERLAY
733 # variable defined in make.conf
735 # Use a subshell so we don't have to protect the variables in
738 for x in $(get_all_make_conf); do
739 [[ -r "${x}" ]] && source "${x}"
741 echo ${PORTDIR_OVERLAY}
744 ALL_PORTDIRS=( "$PORTDIR" $(get_all_overlays) )
746 # Function: array_contains {{{
747 # PHP-style array_contains function.
754 # 0 (True) if needle in haystack, null (False) otherwise
756 for i in $1; do [[ $i == $2 ]] && return 0; done
760 # Function: showdesc {{{
761 # This function takes a list of use flags and shows the status and
762 # the description for each one, honoring $SCOPE
765 # * - USE flags for which to display descriptions. Undefined means to
766 # display descriptions for all known USE flags
769 # SCOPE - defines whether to output local or global USE flag descriptions
770 # Empty means to display both
773 # (STDOUT) Flag description(s) for given USE flags
783 if [ -z "${SCOPE}" ]; then
784 SCOPE="global" showdesc ${args}
786 SCOPE="local" showdesc ${args}
790 local useflags=( $(echo "$(get_useflaglist)") )
792 [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
793 [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
794 echo "************************************************************"
795 if [ "${args}" == "*" ]; then
796 args="${useflags[*]}"
802 while [[ -n "${1}" ]]; do
803 if [[ "${SCOPE}" == "global" ]]; then
804 if array_contains "${useflags[*]}" "$1"; then
805 get_flagstatus "${1}"
806 # XXX: Handle overlay
807 grep -h "^${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.desc} 2> /dev/null
811 # local flags are a bit more complicated as there can be multiple
812 # entries per flag and we can't pipe into printf
813 if [[ "${SCOPE}" == "local" ]]; then
814 if array_contains "${useflags[*]}" "$1"; then
817 # Fetch all the packages data using this flag
818 infos=$( grep -h ":${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.local.desc} 2> /dev/null \
819 | sed -re "s/^([^:]+):(.*) *- *(.+)/\1|\2|\3/g")
820 OIFS=$IFS; IFS=$'\n'; infos=($infos); IFS=$OIFS;
821 for line in "${infos[@]}"; do
822 OIFS=$IFS; IFS="|"; line=($line); IFS=$OIFS
826 if get_flagstatus "${flag}"; then
831 printf "%s\n" "${flag}"
832 printf "%s: %s\n" "${pkg}" "${desc}" \
833 | fold --width=$((${COLUMNS:-80}-10)) -s | sed -e "s/^/ /g"
834 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}"
840 if [[ ${foundone} == 0 ]]; then
841 echo "no matching entries found"
845 # Function: showinstdesc {{{
846 # Works like showdesc() but displays only descriptions of which the appropriate
847 # ebuild is installed and prints the name of those packages.
850 # * - USE flags for which to display descriptions. Undefined means to
851 # display descriptions for all known USE flags
854 # SCOPE - defines whether to output local or global USE flag descriptions
855 # Empty means to display both
858 # (STDOUT) Flag description(s) for given USE flags along with installed
871 "global") echo "global use flags (searching: ${args[@]})";;
872 "local") echo "local use flags (searching: ${args[@]})";;
873 *) SCOPE="global" showinstdesc "${args[@]}"
875 SCOPE="local" showinstdesc "${args[@]}"
879 descdir="$(get_portdir)/profiles"
880 echo "************************************************************"
882 if [ "${args}" = "*" ]; then
883 args="$(get_useflaglist | sort -u)"
888 while [ -n "${1}" ]; do
891 if desc=$(grep "^${1} *-" "${descdir}/use.desc"); then
892 get_flagstatus "${1}"
894 # get list of installed packages matching this USE flag.
896 packages=($(equery -q -C hasuse -i "${1}" | awk '{ print $(NF-1) }' | sort))
897 foundone+=${#packages[@]}
898 printf "\nInstalled packages matching this USE flag: "
899 if [ ${foundone} -gt 0 ]; then
900 echo $'\n'"${packages[*]}"
907 # local flags are a bit more complicated as there can be multiple
908 # entries per flag and we can't pipe into printf
909 IFS=': ' # Use a space instead of a dash because dashes occur in cat/pkg
910 while read pkg flag desc; do
911 # print name only if package is installed
912 # NOTE: If we implement bug #114086 's enhancement we can just use the
913 # exit status of equery instead of a subshell and pipe to wc -l
914 # Bug 274472, -e is the default
915 if [ $(equery -q -C list -i "${pkg}" | wc -l) -gt 0 ]; then
918 get_flagstatus "${flag}" "${pkg}"
920 printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc#- }"
922 done < <(grep ":${1} *-" "${descdir}/use.local.desc")
928 if [ ${foundone} -lt 1 ]; then
929 echo "no matching entries found"
934 # Function: showflags {{{
935 # show a list of all currently active flags and where they are activated
943 if [ "${args}" == "*" ]; then
944 args="$(get_useflaglist | sort -u)"
950 while [ -n "${1}" ]; do
951 if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
956 if echo " ${ACTIVE_FLAGS[4]} " | egrep -e " -?${1} " > /dev/null; then
957 for pkg in $( echo "${ACTIVE_FLAGS[4]}" | \
958 egrep " -?${1} " | cut -d " " -f 2); do
960 SCOPE="local" get_flagstatus ${1} "${pkg}"
961 printf "(%s)\n" ${pkg}
968 # two small helpers to add or remove a flag from a USE string
970 # Remove leading '-' from flag if found
972 [[ ${flag:0:1} == "-" ]] && flag=${1:1}
974 if [[ -n $(grep " -${flag} " <<< " ${ACTIVE_FLAGS[6]} ") ]]; then
975 error "Use flag \"${flag}\" is masked and should not be added" \
978 # Bug #104396 -- Only add use flags defined in use.desc and use.local.desc
979 elif [[ -z $(grep "^${flag}$" <<< "$(get_useflaglist)") ]]; then
980 error "Use flag \"${flag}\" is not defined in use.desc and should" \
981 "not be added\nto make.conf."
984 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
985 echo "Adding flag \"${1}\" to make.conf" >&2
990 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
991 echo "Removing flag \"${1}\" from make.conf" >&2
994 # Function: clean_package_use {{{
995 # Simple utility to remove empty files from package.use
996 clean_package_use() {
997 if [[ -d ${PACKAGE_USE_PATH} ]]; then
998 for f in $(find ${PACKAGE_USE_PATH} -size 0); do
999 echo "Removing empty file ""${f}"""
1005 # Function: scrub_use_flag {{{
1006 # Utility to remove a use flag from a file in package.use[/]
1013 # PACKAGE - Package atom for which to remove flag
1015 local atom_re="^[<>]?=?([a-z][\da-z/-]+[a-z])(-[0-9pr._*-]+)?"
1017 # Ignore leading - on flag
1019 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
1020 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
1023 # Skip (preserve) comments on their own lines
1024 if [[ -z $(echo "${line}" | sed -re "s/^ *#.*$//") ]]; then
1026 # Detect if requested package is defined on this line
1027 elif [[ -n "${PACKAGE}" ]]; then
1028 if [[ -n $(echo "${line}" | grep -Ee "${pkg_re}") ]]; then
1029 # If this is the only (remaining) use flag defined
1030 # for this package, then remove the whole line
1031 if [[ -z $(echo "${line}" | grep -Ee "${pkg_re} *-?${flag} *$") ]]; then
1032 # Remove flag from this line
1033 echo "${line}" | sed -re "s/ *-?\b${flag}\b//"
1039 # If line only has this use flag, let it be removed
1040 # (used if PACKAGE is not defined -- from pruning)
1041 elif [[ -n $(echo "${line}" | \
1042 egrep "^[^#]*${atom_re}.*-?${flag}") ]]; then
1043 echo "Removing use flag from ${line}" >&2
1044 if [[ -z $(echo "${line}" | \
1045 grep -Ee "${atom_re} *-?${flag} *$") ]]; then
1046 # Remove flag from this line
1047 echo "${line}" | sed -re "s/-?\b${flag}\b//"
1053 done > "${filename}.new" < "${filename}"
1054 mv "${filename}.new" "${filename}"
1057 # Function: modify_package {{{
1058 # Adds and removes USE flags from individual packages by modifying
1059 # files in package.use. It supports package.use both as a file and
1060 # and as a folder. Also handles "enabling" as removing a disabled flag from
1061 # a file, and "disabling" a globally enabled flag by adding a negative to
1062 # a file. Also warns about unused and unknown flags, and about flags
1063 # already enabled, disabled, or masked.
1066 # * - USE flag(s) to add or remove
1069 # PACKAGE - Package for which to add (-E) or remove (-D) the USE
1074 local atom_re="^[<>]?=?([a-z][0-9a-z/-]+[a-z])(-[0-9pr._*-]+)?"
1075 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
1076 local V=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\2/")
1077 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
1079 local all_flags=$(SCOPE= get_useflaglist)
1080 # Shift at top rather than bottom to support 'continue'
1082 while [[ -n ${2} ]]; do
1088 # Fetch flag ACTIVE status (+,-,null)
1089 get_flagstatus "${flag}" "${pkg}" > /dev/null
1090 GLOBAL_ACTIVE="$ACTIVE"
1091 # XXX: If V is not given, is this necessary? Should it use the version
1092 # that would be installed by emerge?
1093 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}" "${V}" > /dev/null
1096 # (1) make sure ${pkg} exists in portdir or an overlay
1098 for portdir in ${ALL_PORTDIRS[@]}; do
1099 if [[ -d "${portdir}/${pkg}" ]]; then
1104 if [[ $exists == 1 ]]; then
1105 fatal "Package \"${pkg}\" does not exist"
1107 # (2) make sure ${flag} is defined in get_useflaglist
1108 elif ! array_contains "$all_flags" ${flag}; then
1109 error "USE flag \"${flag}\" does not exist"
1111 # Don't bail just because of this, just warn
1112 # (3) make sure use flag is valid for the package
1113 elif [[ -z $(echo "${ACTIVE_FLAGS[5]} " | grep -Ee "^${pkg_re}" \
1114 | grep -Ee "[; ][+-]?${flag}") ]]; then
1115 # XXX: Handle version or version wildcard?
1116 warn "USE flag \"${flag}\" is not used by $PACKAGE"
1117 # Don't necessarily bail for this, just warn
1118 elif [[ -n "${V}" && -z "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1119 error "Invalid package atom. Did you forget the leading '='?"
1121 elif [[ -z "${V}" && -n "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1122 error "Invalid package atom. Did you forget the version?"
1125 # If removing a disabled flag, or adding an enabled one, emit a warning,
1126 # indicating a likely misunderstanding
1127 if [[ "${ACTION}" == "remove" ]]; then
1128 if [[ "${ACTIVE:-${GLOBAL_ACTIVE}}" == "-" ]]; then
1129 warn "USE flag \"$flag\" is already disabled for $PACKAGE"
1132 elif [[ "${ACTION}" == "prune" ]]; then
1133 # Just remove the flag below
1134 [[ "${ACTIVE}" == "-" ]] && flag="-${flag}"
1136 elif [[ "${ACTION}" == "add" ]]; then
1137 if [[ "${ACTIVE:-${GLOBAL_ACTIVE:--}}" == "+" ]]; then
1138 # XXX: Perhaps look at indicating where it is enabled
1139 warn "USE flag \"$flag\" is already enabled for $PACKAGE"
1145 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1146 # Use naming convention of package.use/package
1147 filename="${PACKAGE_USE_PATH}/${pkg#*/}"
1148 if [[ ! -s "${filename}" ]]; then
1149 # Create new file to contain flag
1150 echo "${PACKAGE} ${flag}" > "${filename}"
1151 echo "Adding \"${PACKAGE}[${flag}]\" use flag to new file ""${filename}"""
1155 # Add to package.use file instead
1156 filename="${PACKAGE_USE_PATH}"
1157 # Create as necessary
1160 # Walk through the file and add the flag manually
1161 echo "Adding \"${PACKAGE}[${flag}]\" use flag in \"${filename}\""
1164 if [[ -n $(echo "${line}" | egrep -e "^[^#]*${PACKAGE} ") ]]; then
1165 echo $(reduce_package_use "${line} ${flag}")
1171 done < "${filename}" > "${filename}.new"
1172 mv "${filename}.new" "${filename}"
1173 if [[ ${added} -eq 0 ]]; then
1174 echo "${PACKAGE} ${flag}" >> "${filename}"
1179 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1180 # Scan for file containing named package and use flag
1181 filename=$(egrep -rle "${pkg_re}.*[^-]${flag}( |$)" "${PACKAGE_USE_PATH}")
1182 if [[ -z "${filename}" ]]; then
1183 error ""${flag}" is not defined for package "${PACKAGE}""
1187 # Remove from package.use instead
1188 filename=${PACKAGE_USE_PATH}
1189 # Create as necessary
1192 # Scrub use flag from matched files
1193 for f in ${filename}; do
1194 # Remove current flags in file
1195 echo "Removing \"${PACKAGE}[${flag}]\" use flag in \"${f}\""
1196 scrub_use_flag ${f} ${flag}
1198 # Remove empty files
1205 # Function: modify {{{
1206 # USE flag modification function. Mainly a loop with calls to add_flag and
1207 # remove_flag to create a new USE string which is then inserted into make.conf.
1209 if [[ -n "${PACKAGE}" ]]; then
1210 modify_package "${*}"
1214 local make_conf_modified=0
1216 if [ -z "${*}" ]; then
1217 if [ "${ACTION}" != "prune" ]; then
1218 echo "WARNING: no USE flags listed for modification, do you really"
1219 echo " want to ${ACTION} *all* known USE flags?"
1220 echo " If you don't please press Ctrl-C NOW!!!"
1222 set $(get_useflaglist | sort -u)
1228 NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
1230 while [[ -n "${1}" ]]; do
1231 if [[ "${ACTION}" == "add" ]]; then
1232 if [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1233 warn "Use flag \"${1}\" is already enabled globally"
1235 elif [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1236 remove_flag "-${1}" || exit
1237 make_conf_modified=1
1239 add_flag "${1}" || exit
1240 make_conf_modified=1
1243 elif [[ "${ACTION}" == "remove" ]]; then
1244 if [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1245 warn "Use flag \"${1}\" is already disabled globally"
1247 elif [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1248 remove_flag "${1}" || exit
1249 make_conf_modified=1
1251 add_flag "-${1}" || exit
1252 make_conf_modified=1
1255 elif [[ "${ACTION}" == "prune" ]]; then
1256 if [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1257 remove_flag "${1}" || exit
1258 make_conf_modified=1
1259 elif [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1260 remove_flag "-${1}" || exit
1261 make_conf_modified=1
1263 warn "Use flag \"${1}\" is not set globally"
1269 # a little loop to add linebreaks so we don't end with one ultra-long line
1270 NEW_MAKE_CONF_USE_2=""
1271 for x in ${NEW_MAKE_CONF_USE}; do
1272 if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
1273 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n $x "
1275 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
1279 # Bail if there is no need to modify make.conf
1280 [[ ${make_conf_modified} == 1 ]] || return
1281 # make a backup just in case the user doesn't like the new make.conf
1282 cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
1284 # as sed doesn't really work with multi-line patterns we have to replace USE
1285 # on our own here. Basically just skip everything between USE=" and the
1286 # closing ", printing our new USE line there instead.
1290 (while [ "$x" -eq "0" ]; do
1291 read -r line || break
1293 # Bug 275362 - Handle the case where make.conf includes:
1297 # Consume USE=" when detected so the quote won't be detected
1298 # as the ending quote
1299 if [ "${line:0:4}" == "USE=" ]; then inuse=1; line=${line:5}; fi
1300 [ "${inuse}" == "0" ] && echo -E "${line}"
1301 if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
1303 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1309 if [ ${had_use} -eq 0 ]; then
1311 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1313 fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
1315 echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
1318 ##### main program comes now #####
1320 parse_arguments "$@"
1323 eval ${MODE} ${ARGUMENTS}
1325 # vim: set tabstop=4 shiftwidth=4: