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 can now exist in /etc/portage/make.conf, prefer it over
39 # /etc/make.conf for changes. Since this will only be used for modifying
40 # the USE variable, we need to make sure the one we pick is the one with
41 # the USE variable defined.
42 if [[ -n $(grep '^USE="' "${ETC}/portage/make.conf" 2>/dev/null) ]]; then
43 MAKE_CONF_PATH="${ETC}/portage/make.conf"
44 elif [[ -e "${ETC}/make.conf" ]]; then
45 MAKE_CONF_PATH="${ETC}/make.conf"
47 fatal "make.conf does not exist"
49 MAKE_CONF_BACKUP_PATH="${MAKE_CONF_PATH}.euse_backup"
51 # /etc/make.globals has been moved to /usr/share/portage/config/make.globals
52 if [ -e "${USR_SHARE_PORTAGE}/config/make.globals" ]; then
53 MAKE_GLOBALS_PATH="${USR_SHARE_PORTAGE}/config/make.globals"
55 MAKE_GLOBALS_PATH="${ETC}/make.globals"
58 # /etc/make.profile or /etc/portage/make.profile, if /etc/make.profile exists, it will be used
59 if [ -e "${ETC}/make.profile" ]; then
60 MAKE_PROFILE_PATH="${ETC}/make.profile"
61 elif [ -e "${ETC}/portage/make.profile" ]; then
62 MAKE_PROFILE_PATH="${ETC}/portage/make.profile"
64 fatal "make.profile does not exist"
66 PACKAGE_USE_PATH=${ETC}/portage/package.use
68 [ -z "${MODE:-}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
71 if [ -z "${1}" ]; then
74 while [ -n "${1}" ]; do
76 -h | --help) MODE="showhelp";;
77 -V | -v | --version) MODE="showversion";;
78 -i | --info) MODE="showdesc";;
79 -I | --info-installed) MODE="showinstdesc";;
80 -l | --local) SCOPE="local";;
81 -g | --global) SCOPE="global";;
82 -a | --active) MODE="showflags";;
83 -E | --enable) MODE="modify"; ACTION="add";;
84 -D | --disable) MODE="modify"; ACTION="remove";;
85 -P | --prune | -R | --remove)
86 MODE="modify"; ACTION="prune";;
87 -p | --package) MODE="modify"; shift; PACKAGE=${1}; SCOPE="local";;
89 echo "ERROR: unknown option ${1} specified."
95 ARGUMENTS="${ARGUMENTS:-} ${ACTIVE_FLAGS[9]}"
98 ARGUMENTS="${ARGUMENTS:-} ${1}"
113 # Function: check_sanity {{{
114 # Performs some basic system sanity checks
116 # file permission tests
121 [[ ! -d "${MAKE_PROFILE_PATH}" || ! -r "${MAKE_PROFILE_PATH}" ]] && error "${MAKE_PROFILE_PATH} is not readable"
123 for make_conf in $(get_all_make_conf); do
124 [ ! -r "${make_conf}" ] && fatal "${make_conf} is not readable"
127 descdir="$(get_portdir)/profiles"
129 [ ! -r "${MAKE_GLOBALS_PATH}" ] && fatal "${MAKE_GLOBALS_PATH} is not readable"
130 [ -z "$(get_portdir)" ] && fatal "\$PORTDIR couldn't be determined"
131 [ ! -d "${descdir}" ] && fatal "${descdir} does not exist or is not a directory"
132 [ ! -r "${descdir}/use.desc" ] && fatal "${descdir}/use.desc is not readable"
133 [ ! -r "${descdir}/use.local.desc" ] && fatal "${descdir}/use.local.desc is not readable"
135 for make_defaults in $(get_all_make_defaults); do
136 [ ! -r "$make_defaults" ] && fatal "$_make_defaults is not readable"
138 [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && fatal ""${MAKE_CONF_PATH}" is not writable"
139 [ "${MODE}" == "modify" -a -s "${PACKAGE_USE_PATH}" -a ! -w "${PACKAGE_USE_PATH}" ] && fatal ""${PACKAGE_USE_PATH}" is not writable"
145 ${PROGRAM_NAME} (${VERSION})
147 Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]
149 Options: -h, --help - show this message
150 -V, --version - show version information
151 -i, --info - show descriptions for the given useflags
152 -I, --info-installed - show descriptions for the given useflags and
153 their current impact on the installed system
154 -g, --global - show only global use flags (suboption)
155 -l, --local - show only local use flags (suboption)
156 -a, --active - show currently active useflags and their origin
157 -E, --enable - enable the given useflags
158 -D, --disable - disable the given useflags
159 -R, --remove - remove all references to the given flags from
160 make.conf and package.use to revert to default
162 -P, --prune - alias for --remove
163 -p, --package - used with -E, -D, and -R to apply to a
164 specific package only
166 Notes: ${PROGRAM_NAME} currently works for global flags defined
167 in make.globals, make.defaults, make.conf, use.force, and use.mask
168 and local flags defined in package.use and individual package ebuilds.
169 It might have issues with cascaded profiles. If multiple options are
170 specified only the last one will be used.
176 ${PROGRAM_NAME} (${VERSION})
177 Written by Marius Mauch
179 Copyright (C) 2004-2009 Gentoo Foundation, Inc.
180 This is free software; see the source for copying conditions.
184 # Function: reduce_incrementals {{{
185 # remove duplicate flags from the given list in both positive and negative forms
186 # (but unlike portage always keep the last value even if it's negative)
187 # Otherwise the status flags could be incorrect if a flag appers multiple times in
188 # one location (like make.conf).
189 # Using python here as bash sucks for list handling.
190 # NOTE: bash isn't actually that bad at handling lists -- sh is. This may be
191 # worth another look to avoid calling python unnecessariy. Or we could
192 # just write the whole thing in python. ;)
193 reduce_incrementals() {
194 echo $@ | python -c "from __future__ import print_function;import sys
196 for x in sys.stdin.read().split():
197 if x[0] == '-' and x[1:] in r:
200 elif x[0] != '-' and '-'+x in r:
203 elif x == '-*': r = ['-*']
204 elif x not in r: r.append(x)
208 # Function: reduce_incrementals_trump {{{
209 # Similar to reduce_incrementals but negative flags trump positive
210 # flags, regardless of which follows which
211 reduce_incrementals_trump() {
212 echo $@ | python -c "from __future__ import print_function;import sys
214 for x in sys.stdin.read().split():
215 if x[0] == '-' and x[1:] in r:
218 elif x == '-*': r = ['-*']
219 elif x not in r and not '-'+x in r: r.append(x)
223 # Function: reduce_package_use {{{
224 # Similar to reduce_incrementals except converts lines from package atoms
225 # in /etc/portage/package.use files to lines of "pkg {[-]flag}*"
228 # * - Lines of package atom followed by flags
229 # (app-editors/vim flag1 flag2 -flag3)
230 reduce_package_use() {
231 echo "${@}" | python -c "from __future__ import print_function;import sys,re
232 h={}; getflags=re.compile(r'(-?[\w*-]+)')
233 for x in sys.stdin.read().split('\n'):
235 parts = x.lstrip().split(' ',1)
236 if len(parts)==1: continue
238 flags = getflags.findall(parts[1])
239 if not pkg in h: h[pkg]=[]
242 if x[0] == '-' and x[1:] in r:
245 elif x[0] != '-' and '-'+x in r:
248 elif x == '-*': r = h[pkg] = ['-*']
251 print('\n'.join(['%s %s' % (pkg,' '.join(flgs)) for pkg,flgs in h.items() if len(flgs)]))"
254 # Function: get_useflags {{{
255 # Creates a bash array ACTIVE_FLAGS that contains the global use flags,
256 # indexed by origin: 0: environment, 1: make.conf, 2: make.defaults,
257 # 3: make.globals, and local use flags, indexed by origin: 4: package.use,
258 # 5: ebuild IUSE, 6: use.mask, 7: use.force,
259 # 9: flags indicated active by emerge --info (get_portageuseflags)
262 if [[ -z ${ACTIVE_FLAGS[4]} && ( $SCOPE == "local" || -z $SCOPE ) ]]; then
263 # Parse through /etc/portage/package.use
264 if [[ -d ${PACKAGE_USE_PATH} ]]; then
265 ACTIVE_FLAGS[4]="$( cat ${PACKAGE_USE_PATH}/* \
266 | sed -re "s/ *#.*$//g" -e "s/^ *$//g" )"
267 elif [[ -e ${PACKAGE_USE_PATH} ]]; then
268 # JWM, 23/12/2009: I edited this following line but I'm not sure if it's 100% correct.
269 ACTIVE_FLAGS[4]="$( sed -re "s/ *#.*$//g" -e "s/^ *$//g" \
270 ${PACKAGE_USE_PATH})"
272 # Simplify ACTIVE_FLAGS[4] to be lines of pkg {[-]flag}*
273 ACTIVE_FLAGS[4]="$(reduce_package_use "${ACTIVE_FLAGS[4]}")"
275 # ACTIVE_FLAGS[5] reserved for USE flags defined in ebuilds and
276 # is generated/maintained in the get_useflaglist_ebuild() function
279 # only calculate once as calling emerge is painfully slow
280 [ -n "${USE_FLAGS_CALCULATED}" ] && return
282 # backup portdir so get_portdir() doesn't give false results later
283 portdir_backup="${PORTDIR}"
285 ACTIVE_FLAGS[0]="$(reduce_incrementals "${USE}")"
287 for x in $(get_all_make_conf); do
289 ACTIVE_FLAGS[1]="$(reduce_incrementals "${ACTIVE_FLAGS[1]}" "${USE}")"
292 for x in $(get_all_make_defaults); do
294 ACTIVE_FLAGS[2]="${ACTIVE_FLAGS[2]} ${USE}"
296 ACTIVE_FLAGS[2]="$(reduce_incrementals "${ACTIVE_FLAGS[2]}")"
298 source "${MAKE_GLOBALS_PATH}"
299 ACTIVE_FLAGS[3]="$(reduce_incrementals "${USE}")"
301 # restore saved env variables
302 USE="${ACTIVE_FLAGS[0]}"
303 PORTDIR="${portdir_backup}"
306 # Traverse through use.mask and use.force (0.5s)
307 # Flip signs of use.mask (it's interpreted oppositely),
308 ACTIVE_FLAGS[6]=$(reduce_incrementals_trump \
309 $(cat $(traverse_profile "use.mask") | sed -re "/^#.*$/{d}") \
310 | sed -re "s/(^| )-[^ ]*//g" -e "s/(^| )([a-z0-9])/ -\2/g")
311 ACTIVE_FLAGS[7]=$(reduce_incrementals \
312 $(cat $(traverse_profile "use.force") \
313 | sed -re "/^#.*$/ {d}"))
315 USE_FLAGS_CALCULATED=1
319 # Function: get_portageuseflags # {{{
320 # Fetch USE flags reported active by Portage
321 get_portageuseflags() {
322 # only calculate once as calling emerge is painfully slow
323 [ -n "${_PORTAGE_USE_FLAGS_CALCULATED}" ] && return
324 # get the currently active USE flags as seen by portage, this has to be after
325 # restoring USE or portage won't see the original environment
326 # Bug 181309, emerge may complain if EMERGE_DEFAULT_OPTS="--ask" is set
327 ACTIVE_FLAGS[9]="$(portageq envvar USE)" #'
328 _PORTAGE_USE_FLAGS_CALCULATED=1
331 # Function: get_useflaglist {{{
332 # Get the list of all known USE flags by reading use.desc and/or
333 # use.local.desc (depending on the value of $SCOPE). Also searches any
334 # registered overlays after searching the main portage tree first.
335 # Use flags visible in both the main tree and overlays are trumped by
336 # the main tree. Overlays are indicated by brackets [xxx] at the
337 # beginning of the description.
340 # (written to stdout) Sorted, unique list of system-wide USE flags and
341 # descriptions. Flags defined in overlays have the overlay in brackets
342 # prepended to the descriptions.
345 # SCOPE - [local|global] constrain search to local (use.local.desc) or
350 for profiledir in ${ALL_PORTDIRS[@]}; do
351 descdir="${profiledir}/profiles"
352 if [[ -z ${SCOPE} || ${SCOPE} == "global" ]]; then
353 [[ ! -s "${descdir}/use.desc" ]] && continue
354 egrep "^[^# ]+ +-" "${descdir}/use.desc"
356 if [[ -z ${SCOPE} || ${SCOPE} == "local" ]]; then
357 [[ ! -s "${descdir}/use.local.desc" ]] && continue
358 egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" \
361 done | cut -d " " -f1 | sort --field=":" --key=1,1 --unique
364 # Function: get_useflaglist_ebuild {{{
365 # Builds USE flag information for specified package atom into
366 # ACTIVE_FLAGS[5]. For the atom, the versions available are found, and
367 # for each, the corresponding SLOT, IUSE are stored along with which
368 # overlay the ebuild lives in. Considering that the pieces of information
369 # may be required in any order or any subsets, it is intended for the
370 # function to cache the information and it be retrieved from
371 # ACTIVE_FLAGS[5]. So the format of ACTIVE_FLAGS[5] is newline-separated
374 # category/packge;version;SLOT;IUSE;overlay
377 # $1 - Package atom to lookup (app-editor/vim)
380 # Nothing significant
383 # PORTDIR - Root of portage tree
384 # ACTIVE_FLAGS - Array of current use flag info
386 get_useflaglist_ebuild() {
387 local known=$(echo "${ACTIVE_FLAGS[5]}" | egrep "^${1}")
388 if [[ -n $known ]]; then
392 local pkg=$(echo ${1} | cut -d/ -f2)
394 for portdir in ${ALL_PORTDIRS[@]}; do
395 if [[ -s "${portdir}/profiles/repo_name" ]]; then
396 overlay="$(cat "${portdir}/profiles/repo_name")"
398 # XXX: Portage uses "x-<basename of the overlay>
399 overlay="x-$(basename "${portdir}")"
401 # Open the ebuild file and retrieve defined USE flags
402 [[ ! -d "$portdir/${1}" ]] && continue
403 if [[ ! -d "$portdir/metadata/cache" ]]; then
404 echo "!!! Metadata cache not found. You need to run " >&2
405 echo "!!! 'egencache --repo=$overlay --update'" >&2
406 echo "!!! to generate metadata for your overlays" >&2
409 append=$(set +f; ls $portdir/metadata/cache/${1}-* \
410 | egrep "${1}-[0-9.]+" \
411 | sed -e "s:$portdir/metadata/cache/${1}-::g" \
412 | while read -d $'\n' version; do
414 if [[ ! -e "$portdir/metadata/cache/${1}-$version" ]]; then
415 # Repo does not have this particular package
418 iuse=$(head -11 "$portdir/metadata/cache/${1}-$version"|tail -1)
419 slot=$(head -3 "$portdir/metadata/cache/${1}-$version"|tail -1)
420 echo "${1};${version};${slot};${iuse};${overlay}"
423 if [[ -z ${ACTIVE_FLAGS[5]} ]]; then ACTIVE_FLAGS[5]="$append"
424 else ACTIVE_FLAGS[5]="${ACTIVE_FLAGS[5]}"$'\n'"$append"
429 # get all make.conf files that exist on the system
430 get_all_make_conf() {
431 # At least one of the files exists or we would not have made it this far
432 for x in ${ETC}/make.conf ${ETC}/portage/make.conf; do
433 [ -e "${x}" ] && echo "${x}"
436 # Function: traverse_profile {{{
437 # General method of collecting the contents of a profile
438 # component by traversing through the cascading profile
440 # Bug #414961 allows ':' shorthand to resolve to the ${PORTDIR}/profiles
441 # A value before the ':' references the repository, no value means gentoo
443 # Example: local:base would refer to the profiles directory in the repository
444 # path owned by the 'local' repository
447 # $1 - Filename (make.profile)
448 # [$2] - Current directory (unspecified means to start at the top)
454 curdir="${2:-$(get_real_path ${MAKE_PROFILE_PATH})}"
456 if [[ -f "${curdir}/parent" ]]; then
457 for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
458 # Bug 231394, handle parent path being absolute
459 index=$(expr index "${parent}" :)
460 if [[ ${parent:0:1} == "/" ]]; then
461 pdir="$(get_real_path ${parent})"
462 elif [[ $index -eq 0 ]]; then
463 pdir="$(get_real_path ${curdir}/${parent})"
465 # We have a path with a colon shortcut
467 repo="${parent:0:${i}}"
468 [[ -z "${repo}" ]] && repo="gentoo"
469 parent="${parent:$index}"
470 limit=${#PORTAGE_REPOS[@]}
471 for ((i=0; i < limit ; i++)); do
472 if [[ ${repo} == ${PORTAGE_REPOS[i]} ]]; then
473 parent="${PORTAGE_REPO_PATHS[i]}/profiles/${parent}"
477 pdir="$(get_real_path ${parent})"
479 rvalue="${rvalue} $(traverse_profile ${1} ${pdir})"
482 [[ -f "${curdir}/${1}" ]] && rvalue="${rvalue} ${curdir}/${1}"
487 # Function: get_all_make_defaults {{{
488 # Det all make.defaults by traversing the cascaded profile directories
489 get_all_make_defaults() {
490 if [[ -z ${MAKE_DEFAULTS:-} ]]; then
491 MAKE_DEFAULTS=$(traverse_profile "make.defaults")
495 MAKE_DEFAULTS=$(get_all_make_defaults)
497 # Function: get_flagstatus_helper # {{{
498 # Little helper function to get the status of a given flag in one of the
499 # ACTIVE_FLAGS elements.
502 # (Written to STDOUT) Flag active status (+/-) or default string given
503 # in argument 4 or an empty space
507 # 2 - index of ACTIVE_FLAGS
508 # 3 - echo value for positive (and as lowercase for negative) test result
509 # 4 - (optional) echo value for "missing" test result, defaults to blank
510 get_flagstatus_helper() {
511 if [[ -z ${flags} ]]; then
512 local flags=${ACTIVE_FLAGS[${2}]}
514 local flag=$(echo " $flags " | grep -Eo " [+-]?${1} ")
515 if [[ ${flag:1:1} == "-" ]]; then
516 echo -e -n "${3}" | tr [:upper:]+ [:lower:]-
517 elif [[ -n ${flag} ]]; then
524 # Function: get_flagstatus_helper_pkg # {{{
525 # Entry to get_flagstatus_helper for packages which will fetch use
526 # flags set in package.use for the package and pass them on to
527 # get_flagstatus_helper. Also correcly handles lines in package.use
528 # specified for individual package versions
529 get_flagstatus_helper_pkg() {
530 if [[ -z ${2} ]]; then
533 elif [[ -z ${flags} ]]; then
534 # If no atoms are matchers (start with >,<,=, then they all match
536 if [[ -z "${atoms[@]/[<>=]*/}" ]]; then
538 echo "${atoms[@]}" | python -c "
539 from __future__ import print_function;import portage.dep as dep, sys
540 print(' '.join(dep.match_to_list('$5-$6',sys.stdin.read().split())))"))
542 flags=$(for atom in ${atoms[@]}; do
543 [[ -z $atom ]] && continue
544 echo "${ACTIVE_FLAGS[4]}" | \
545 grep "^ *$atom" | cut -d\ -f2-
548 if [[ -z ${5} || -z ${flags} ]]; then
552 get_flagstatus_helper "$@"
555 # Function: get_flagstatus_helper_ebuild {{{
556 # get_flagstatus_helper replacement for packages to fetch ebuild USE flag
560 # (Written to STDOUT) Flag active status (+/-) or default string given
561 # in argument 4 or an empty space. If USE flag is not defined in the list
562 # of flags (2), an '!' is written
566 # 2 - IUSE line from ebuild file
567 # 3 - echo value for positive (and as lowercase for negative) test result
568 # 4 - (optional) echo value for "missing" test result, defaults to blank space
569 get_flagstatus_helper_ebuild() {
570 local flags=$(echo $2 | cut -d\" -f2)
571 local flag=$(echo " $flags " | grep -Eo " [+-]?$1 ")
572 if [[ ${flag:1:1} == "+" ]]; then
574 elif [[ ${flag:1:1} == "-" ]]; then
575 echo -en "${3}" | tr [:upper:]+ [:lower:]-
576 elif [[ -z $flag ]]; then
583 # Function: get_flagstatus {{{
584 # Prints a status string for the given flag, each column indicating the presence
585 # for portage, in the environment, in make.conf, in make.defaults, in
586 # make.globals, and in use.force and flipped in use.mask.
589 # 1 - use flag for which to retrieve status
592 # 0 (True) if flag is active, 1 (False) if not active
595 # Full positive value would be "[+ECDGFm] ", full negative value would be [-ecdgfM],
596 # full missing value would be "[- ] " (portage only sees present or not present)
600 local E=$(get_flagstatus_helper "${1}" 0 "E")
601 local C=$(get_flagstatus_helper "${1}" 1 "C")
602 local D=$(get_flagstatus_helper "${1}" 2 "D")
603 local G=$(get_flagstatus_helper "${1}" 3 "G")
604 local M=$(get_flagstatus_helper "${1}" 6 "M")
605 local F=$(get_flagstatus_helper "${1}" 7 "F")
606 # Use flags are disabled by default
609 # Use flag precedence is defined (at least) at:
610 # http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=2
611 for location in "E" "C" "D" "G" "F" "M"; do
612 if [[ ${!location} == $location ]]; then
615 elif [[ ${!location} != " " ]]; then
620 echo -n "[${ACTIVE:--}$E$C$D$G$F$M] "
621 [[ $ACTIVE == "+" ]]; return $?
624 # Function: get_flagstatus_pkg {{{
625 # Outputs local flag status information for a specific package and perhaps
626 # specific package version.
630 # 2 - package atom (www-client/elinks)
631 # 3 - +/- whether flag is enabled by global configuration files
632 # 4 - (Optional) version of package to evaluate (empty means all versions)
635 # Flag status for package.use and ebuild, slot and version, and overlay
636 # the version lives is if not PORTDIR
638 # Full positive would be "[+PB]", full negative would be "[-pb]", and full
639 # missing would be "[? ]", question because the sign will default to the
640 # sign of the global status of the flag
641 get_flagstatus_pkg() {
643 # Pre-cache the use flags declared in ebuilds first.
644 # This is required as calling it inside a $() seems to
645 # prevent caching of results into $ACTIVE_FLAGS array
646 get_useflaglist_ebuild ${2}
648 # Get list of ebuilds available for this package. The
649 # get_useflaglist_ebuild() function stores them in $ACTIVE_FLAGS[5]
650 # with the package name leading the lines. The other information
651 # is in the same line, semi-colon (;) separated. The fields are
652 # package;version;SLOT;IUSE;overlay
654 # Fetch package atoms and flags from package.use only once
655 local atoms=$(echo "${ACTIVE_FLAGS[4]}" | \
656 grep -Eo "^ *[<>]?=?${2}(-[0-9rp._*-]*)?")
657 local IFS=$'\n'; for pkgline in $(echo "${ACTIVE_FLAGS[5]}" | grep "^$2" \
658 | sort -t \; -k2,2 -V); do
659 OIFS=$IFS; IFS=";"; INFO=($pkgline); IFS=$OIFS;
660 local version=${INFO[1]}
661 [[ -n $4 && $4 != $version ]] && continue
662 local slot=${INFO[2]}
663 local iuse=${INFO[3]}
664 if [[ $slot == '0' || $slot == "" ]]; then
667 slot="($(echo ${slot} | cut -d\" -f2)) "
669 local overlay=${INFO[4]}
670 [[ -n $overlay ]] && overlay="[$overlay]"
672 # Fetch enabled status for this version
673 local P=$(get_flagstatus_helper_pkg "${1}" "${atoms}" "P" "" "${2}" "${version}")
674 local B=$(get_flagstatus_helper_ebuild "${1}" "${iuse}" "B" "" "${2}" "${version}")
677 for location in "P" "B"; do
678 if [[ ${!location} == $location ]]; then
680 elif [[ ${!location} == "!" ]]; then
683 elif [[ ${!location} != " " ]]; then
688 if [[ $UNUSED == 1 ]]; then
689 echo " ${slot}${version} ${overlay}"
691 echo " [${ACTIVE:-${3:- }}$P$B] ${slot}${version} ${overlay}"
697 # Function: get_portdir {{{
698 # faster replacement to `portageq portdir`
701 # Location of portage tree root
703 # Use a subshell so we don't have to protect the variables in
706 if [ -z "${PORTDIR:-}" ]; then
707 source "${MAKE_GLOBALS_PATH}"
708 for x in $(get_all_make_defaults); do
711 for x in $(get_all_make_conf); do
718 # This won't change while the script is running, so cache it
719 PORTDIR="$(get_portdir)"
721 # Function: get_all_overlays {{{
722 # Outputs list of portage overlays as defined in the PORTDIR_OVERLAY
723 # variable defined in make.conf
725 # Use a subshell so we don't have to protect the variables in
728 for x in $(get_all_make_conf); do
729 [[ -r "${x}" ]] && source "${x}"
731 echo ${PORTDIR_OVERLAY}
734 ALL_PORTDIRS=( "$PORTDIR" $(get_all_overlays) )
736 # Function: array_contains {{{
737 # PHP-style array_contains function.
744 # 0 (True) if needle in haystack, null (False) otherwise
746 for i in $1; do [[ $i == $2 ]] && return 0; done
750 # Function: showdesc {{{
751 # This function takes a list of use flags and shows the status and
752 # the description for each one, honoring $SCOPE
755 # * - USE flags for which to display descriptions. Undefined means to
756 # display descriptions for all known USE flags
759 # SCOPE - defines whether to output local or global USE flag descriptions
760 # Empty means to display both
763 # (STDOUT) Flag description(s) for given USE flags
773 if [ -z "${SCOPE}" ]; then
774 SCOPE="global" showdesc ${args}
776 SCOPE="local" showdesc ${args}
780 local useflags=( $(echo "$(get_useflaglist)") )
782 [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
783 [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
784 echo "************************************************************"
785 if [ "${args}" == "*" ]; then
786 args="${useflags[*]}"
792 while [[ -n "${1}" ]]; do
793 if [[ "${SCOPE}" == "global" ]]; then
794 if array_contains "${useflags[*]}" "$1"; then
795 get_flagstatus "${1}"
796 # XXX: Handle overlay
797 grep -h "^${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.desc} 2> /dev/null
801 # local flags are a bit more complicated as there can be multiple
802 # entries per flag and we can't pipe into printf
803 if [[ "${SCOPE}" == "local" ]]; then
804 if array_contains "${useflags[*]}" "$1"; then
807 # Fetch all the packages data using this flag
808 infos=$( grep -h ":${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.local.desc} 2> /dev/null \
809 | sed -re "s/^([^:]+):(.*) *- *(.+)/\1|\2|\3/g")
810 OIFS=$IFS; IFS=$'\n'; infos=($infos); IFS=$OIFS;
811 for line in "${infos[@]}"; do
812 OIFS=$IFS; IFS="|"; line=($line); IFS=$OIFS
816 if get_flagstatus "${flag}"; then
821 printf "%s\n" "${flag}"
822 printf "%s: %s\n" "${pkg}" "${desc}" \
823 | fold --width=$((${COLUMNS:-80}-10)) -s | sed -e "s/^/ /g"
824 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}"
830 if [[ ${foundone} == 0 ]]; then
831 echo "no matching entries found"
835 # Function: showinstdesc {{{
836 # Works like showdesc() but displays only descriptions of which the appropriate
837 # ebuild is installed and prints the name of those packages.
840 # * - USE flags for which to display descriptions. Undefined means to
841 # display descriptions for all known USE flags
844 # SCOPE - defines whether to output local or global USE flag descriptions
845 # Empty means to display both
848 # (STDOUT) Flag description(s) for given USE flags along with installed
861 "global") echo "global use flags (searching: ${args[@]})";;
862 "local") echo "local use flags (searching: ${args[@]})";;
863 *) SCOPE="global" showinstdesc "${args[@]}"
865 SCOPE="local" showinstdesc "${args[@]}"
869 descdir="$(get_portdir)/profiles"
870 echo "************************************************************"
872 if [ "${args}" = "*" ]; then
873 args="$(get_useflaglist | sort -u)"
878 while [ -n "${1}" ]; do
881 if desc=$(grep "^${1} *-" "${descdir}/use.desc"); then
882 get_flagstatus "${1}"
884 # get list of installed packages matching this USE flag.
886 packages=($(equery -q -C hasuse -i "${1}" | awk '{ print $(NF-1) }' | sort))
887 foundone+=${#packages[@]}
888 printf "\nInstalled packages matching this USE flag: "
889 if [ ${foundone} -gt 0 ]; then
890 echo $'\n'"${packages[*]}"
897 # local flags are a bit more complicated as there can be multiple
898 # entries per flag and we can't pipe into printf
899 IFS=': ' # Use a space instead of a dash because dashes occur in cat/pkg
900 while read pkg flag desc; do
901 # print name only if package is installed
902 # NOTE: If we implement bug #114086 's enhancement we can just use the
903 # exit status of equery instead of a subshell and pipe to wc -l
904 # Bug 274472, -e is the default
905 if [ $(equery -q -C list -i "${pkg}" | wc -l) -gt 0 ]; then
908 get_flagstatus "${flag}" "${pkg}"
910 printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc#- }"
912 done < <(grep ":${1} *-" "${descdir}/use.local.desc")
918 if [ ${foundone} -lt 1 ]; then
919 echo "no matching entries found"
924 # Function: showflags {{{
925 # show a list of all currently active flags and where they are activated
933 if [ "${args}" == "*" ]; then
934 args="$(get_useflaglist | sort -u)"
940 while [ -n "${1}" ]; do
941 if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
946 if echo " ${ACTIVE_FLAGS[4]} " | egrep -e " -?${1} " > /dev/null; then
947 for pkg in $( echo "${ACTIVE_FLAGS[4]}" | \
948 egrep " -?${1} " | cut -d " " -f 2); do
950 SCOPE="local" get_flagstatus ${1} "${pkg}"
951 printf "(%s)\n" ${pkg}
958 # two small helpers to add or remove a flag from a USE string
960 # Remove leading '-' from flag if found
962 [[ ${flag:0:1} == "-" ]] && flag=${1:1}
964 if [[ -n $(grep " -${flag} " <<< " ${ACTIVE_FLAGS[6]} ") ]]; then
965 error "Use flag \"${flag}\" is masked and should not be added" \
968 # Bug #104396 -- Only add use flags defined in use.desc and use.local.desc
969 elif [[ -z $(grep "^${flag}$" <<< "$(get_useflaglist)") ]]; then
970 error "Use flag \"${flag}\" is not defined in use.desc and should" \
971 "not be added\nto make.conf."
974 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
975 echo "Adding flag \"${1}\" to make.conf" >&2
980 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
981 echo "Removing flag \"${1}\" from make.conf" >&2
984 # Function: clean_package_use {{{
985 # Simple utility to remove empty files from package.use
986 clean_package_use() {
987 if [[ -d ${PACKAGE_USE_PATH} ]]; then
988 for f in $(find ${PACKAGE_USE_PATH} -size 0); do
989 echo "Removing empty file ""${f}"""
995 # Function: scrub_use_flag {{{
996 # Utility to remove a use flag from a file in package.use[/]
1003 # PACKAGE - Package atom for which to remove flag
1005 local atom_re="^[<>]?=?([a-z][\da-z/-]+[a-z])(-[0-9pr._*-]+)?"
1007 # Ignore leading - on flag
1009 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
1010 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
1013 # Skip (preserve) comments on their own lines
1014 if [[ -z $(echo "${line}" | sed -re "s/^ *#.*$//") ]]; then
1016 # Detect if requested package is defined on this line
1017 elif [[ -n "${PACKAGE}" ]]; then
1018 if [[ -n $(echo "${line}" | grep -Ee "${pkg_re}") ]]; then
1019 # If this is the only (remaining) use flag defined
1020 # for this package, then remove the whole line
1021 if [[ -z $(echo "${line}" | grep -Ee "${pkg_re} *-?${flag} *$") ]]; then
1022 # Remove flag from this line
1023 echo "${line}" | sed -re "s/ *-?\b${flag}\b//"
1029 # If line only has this use flag, let it be removed
1030 # (used if PACKAGE is not defined -- from pruning)
1031 elif [[ -n $(echo "${line}" | \
1032 egrep "^[^#]*${atom_re}.*-?${flag}") ]]; then
1033 echo "Removing use flag from ${line}" >&2
1034 if [[ -z $(echo "${line}" | \
1035 grep -Ee "${atom_re} *-?${flag} *$") ]]; then
1036 # Remove flag from this line
1037 echo "${line}" | sed -re "s/-?\b${flag}\b//"
1043 done > "${filename}.new" < "${filename}"
1044 mv "${filename}.new" "${filename}"
1047 # Function: modify_package {{{
1048 # Adds and removes USE flags from individual packages by modifying
1049 # files in package.use. It supports package.use both as a file and
1050 # and as a folder. Also handles "enabling" as removing a disabled flag from
1051 # a file, and "disabling" a globally enabled flag by adding a negative to
1052 # a file. Also warns about unused and unknown flags, and about flags
1053 # already enabled, disabled, or masked.
1056 # * - USE flag(s) to add or remove
1059 # PACKAGE - Package for which to add (-E) or remove (-D) the USE
1064 local atom_re="^[<>]?=?([a-z][0-9a-z/-]+[a-z])(-[0-9pr._*-]+)?"
1065 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
1066 local V=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\2/")
1067 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
1069 local all_flags=$(SCOPE= get_useflaglist)
1070 # Shift at top rather than bottom to support 'continue'
1072 while [[ -n ${2} ]]; do
1078 # Fetch flag ACTIVE status (+,-,null)
1079 get_flagstatus "${flag}" "${pkg}" > /dev/null
1080 GLOBAL_ACTIVE="$ACTIVE"
1081 # XXX: If V is not given, is this necessary? Should it use the version
1082 # that would be installed by emerge?
1083 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}" "${V}" > /dev/null
1086 # (1) make sure ${pkg} exists in portdir or an overlay
1088 for portdir in ${ALL_PORTDIRS[@]}; do
1089 if [[ -d "${portdir}/${pkg}" ]]; then
1094 if [[ $exists == 1 ]]; then
1095 fatal "Package \"${pkg}\" does not exist"
1097 # (2) make sure ${flag} is defined in get_useflaglist
1098 elif ! array_contains "$all_flags" ${flag}; then
1099 error "USE flag \"${flag}\" does not exist"
1101 # Don't bail just because of this, just warn
1102 # (3) make sure use flag is valid for the package
1103 elif [[ -z $(echo "${ACTIVE_FLAGS[5]} " | grep -Ee "^${pkg_re}" \
1104 | grep -Ee "[; ][+-]?${flag}") ]]; then
1105 # XXX: Handle version or version wildcard?
1106 warn "USE flag \"${flag}\" is not used by $PACKAGE"
1107 # Don't necessarily bail for this, just warn
1108 elif [[ -n "${V}" && -z "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1109 error "Invalid package atom. Did you forget the leading '='?"
1111 elif [[ -z "${V}" && -n "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1112 error "Invalid package atom. Did you forget the version?"
1115 # If removing a disabled flag, or adding an enabled one, emit a warning,
1116 # indicating a likely misunderstanding
1117 if [[ "${ACTION}" == "remove" ]]; then
1118 if [[ "${ACTIVE:-${GLOBAL_ACTIVE}}" == "-" ]]; then
1119 warn "USE flag \"$flag\" is already disabled for $PACKAGE"
1122 elif [[ "${ACTION}" == "prune" ]]; then
1123 # Just remove the flag below
1124 [[ "${ACTIVE}" == "-" ]] && flag="-${flag}"
1126 elif [[ "${ACTION}" == "add" ]]; then
1127 if [[ "${ACTIVE:-${GLOBAL_ACTIVE:--}}" == "+" ]]; then
1128 # XXX: Perhaps look at indicating where it is enabled
1129 warn "USE flag \"$flag\" is already enabled for $PACKAGE"
1135 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1136 # Use naming convention of package.use/package
1137 filename="${PACKAGE_USE_PATH}/${pkg#*/}"
1138 if [[ ! -s "${filename}" ]]; then
1139 # Create new file to contain flag
1140 echo "${PACKAGE} ${flag}" > "${filename}"
1141 echo "Adding \"${PACKAGE}[${flag}]\" use flag to new file ""${filename}"""
1145 # Add to package.use file instead
1146 filename="${PACKAGE_USE_PATH}"
1147 # Create as necessary
1150 # Walk through the file and add the flag manually
1151 echo "Adding \"${PACKAGE}[${flag}]\" use flag in \"${filename}\""
1154 if [[ -n $(echo "${line}" | egrep -e "^[^#]*${PACKAGE} ") ]]; then
1155 echo $(reduce_package_use "${line} ${flag}")
1161 done < "${filename}" > "${filename}.new"
1162 mv "${filename}.new" "${filename}"
1163 if [[ ${added} -eq 0 ]]; then
1164 echo "${PACKAGE} ${flag}" >> "${filename}"
1169 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1170 # Scan for file containing named package and use flag
1171 filename=$(egrep -rle "${pkg_re}.*[^-]${flag}( |$)" "${PACKAGE_USE_PATH}")
1172 if [[ -z "${filename}" ]]; then
1173 error ""${flag}" is not defined for package "${PACKAGE}""
1177 # Remove from package.use instead
1178 filename=${PACKAGE_USE_PATH}
1179 # Create as necessary
1182 # Scrub use flag from matched files
1183 for f in ${filename}; do
1184 # Remove current flags in file
1185 echo "Removing \"${PACKAGE}[${flag}]\" use flag in \"${f}\""
1186 scrub_use_flag ${f} ${flag}
1188 # Remove empty files
1195 # Function: modify {{{
1196 # USE flag modification function. Mainly a loop with calls to add_flag and
1197 # remove_flag to create a new USE string which is then inserted into make.conf.
1199 if [[ -n "${PACKAGE}" ]]; then
1200 modify_package "${*}"
1204 local make_conf_modified=0
1206 if [ -z "${*}" ]; then
1207 if [ "${ACTION}" != "prune" ]; then
1208 echo "WARNING: no USE flags listed for modification, do you really"
1209 echo " want to ${ACTION} *all* known USE flags?"
1210 echo " If you don't please press Ctrl-C NOW!!!"
1212 set $(get_useflaglist | sort -u)
1218 NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
1220 while [[ -n "${1}" ]]; do
1221 if [[ "${ACTION}" == "add" ]]; then
1222 if [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1223 warn "Use flag \"${1}\" is already enabled globally"
1225 elif [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1226 remove_flag "-${1}" || exit
1227 make_conf_modified=1
1229 add_flag "${1}" || exit
1230 make_conf_modified=1
1233 elif [[ "${ACTION}" == "remove" ]]; then
1234 if [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1235 warn "Use flag \"${1}\" is already disabled globally"
1237 elif [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1238 remove_flag "${1}" || exit
1239 make_conf_modified=1
1241 add_flag "-${1}" || exit
1242 make_conf_modified=1
1245 elif [[ "${ACTION}" == "prune" ]]; then
1246 if [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1247 remove_flag "${1}" || exit
1248 make_conf_modified=1
1249 elif [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1250 remove_flag "-${1}" || exit
1251 make_conf_modified=1
1253 warn "Use flag \"${1}\" is not set globally"
1259 # a little loop to add linebreaks so we don't end with one ultra-long line
1260 NEW_MAKE_CONF_USE_2=""
1261 for x in ${NEW_MAKE_CONF_USE}; do
1262 if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
1263 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n $x "
1265 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
1269 # Bail if there is no need to modify make.conf
1270 [[ ${make_conf_modified} == 1 ]] || return
1271 # make a backup just in case the user doesn't like the new make.conf
1272 cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
1274 # as sed doesn't really work with multi-line patterns we have to replace USE
1275 # on our own here. Basically just skip everything between USE=" and the
1276 # closing ", printing our new USE line there instead.
1280 (while [ "$x" -eq "0" ]; do
1281 read -r line || break
1283 # Bug 275362 - Handle the case where make.conf includes:
1287 # Consume USE=" when detected so the quote won't be detected
1288 # as the ending quote
1289 if [ "${line:0:4}" == "USE=" ]; then inuse=1; line=${line:5}; fi
1290 [ "${inuse}" == "0" ] && echo -E "${line}"
1291 if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
1293 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1299 if [ ${had_use} -eq 0 ]; then
1301 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1303 fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
1305 echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
1308 ##### main program comes now #####
1310 parse_arguments "$@"
1313 eval ${MODE} ${ARGUMENTS}
1315 # vim: set tabstop=4 shiftwidth=4: