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 # define error functions so they can be used immediately
31 echo -e "WARNING: ${*}"
34 # /etc/make.conf can now exist in /etc/portage/make.conf, prefer it over
35 # /etc/make.conf for changes. Since this will only be used for modifying
36 # the USE variable, we need to make sure the one we pick is the one with
37 # the USE variable defined.
38 if [[ -n $(grep '^USE="' "${ETC}/portage/make.conf" 2>/dev/null) ]]; then
39 MAKE_CONF_PATH="${ETC}/portage/make.conf"
40 elif [[ -e "${ETC}/make.conf" ]]; then
41 MAKE_CONF_PATH="${ETC}/make.conf"
43 fatal "make.conf does not exist"
45 MAKE_CONF_BACKUP_PATH="${MAKE_CONF_PATH}.euse_backup"
47 # /etc/make.globals has been moved to /usr/share/portage/config/make.globals
48 if [ -e "${USR_SHARE_PORTAGE}/config/make.globals" ]; then
49 MAKE_GLOBALS_PATH="${USR_SHARE_PORTAGE}/config/make.globals"
51 MAKE_GLOBALS_PATH="${ETC}/make.globals"
54 # /etc/make.profile or /etc/portage/make.profile, if /etc/make.profile exists, it will be used
55 if [ -e "${ETC}/make.profile" ]; then
56 MAKE_PROFILE_PATH="${ETC}/make.profile"
57 elif [ -e "${ETC}/portage/make.profile" ]; then
58 MAKE_PROFILE_PATH="${ETC}/portage/make.profile"
60 fatal "make.profile does not exist"
62 PACKAGE_USE_PATH=${ETC}/portage/package.use
64 [ -z "${MODE:-}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
67 if [ -z "${1}" ]; then
70 while [ -n "${1}" ]; do
72 -h | --help) MODE="showhelp";;
73 -V | -v | --version) MODE="showversion";;
74 -i | --info) MODE="showdesc";;
75 -I | --info-installed) MODE="showinstdesc";;
76 -l | --local) SCOPE="local";;
77 -g | --global) SCOPE="global";;
78 -a | --active) MODE="showflags";;
79 -E | --enable) MODE="modify"; ACTION="add";;
80 -D | --disable) MODE="modify"; ACTION="remove";;
81 -P | --prune | -R | --remove)
82 MODE="modify"; ACTION="prune";;
83 -p | --package) MODE="modify"; shift; PACKAGE=${1}; SCOPE="local";;
85 echo "ERROR: unknown option ${1} specified."
91 ARGUMENTS="${ARGUMENTS:-} ${ACTIVE_FLAGS[9]}"
94 ARGUMENTS="${ARGUMENTS:-} ${1}"
109 # Function: check_sanity {{{
110 # Performs some basic system sanity checks
112 # file permission tests
117 [[ ! -d "${MAKE_PROFILE_PATH}" || ! -r "${MAKE_PROFILE_PATH}" ]] && error "${MAKE_PROFILE_PATH} is not readable"
119 for make_conf in $(get_all_make_conf); do
120 [ ! -r "${make_conf}" ] && fatal "${make_conf} is not readable"
123 descdir="$(get_portdir)/profiles"
125 [ ! -r "${MAKE_GLOBALS_PATH}" ] && fatal "${MAKE_GLOBALS_PATH} is not readable"
126 [ -z "$(get_portdir)" ] && fatal "\$PORTDIR couldn't be determined"
127 [ ! -d "${descdir}" ] && fatal "${descdir} does not exist or is not a directory"
128 [ ! -r "${descdir}/use.desc" ] && fatal "${descdir}/use.desc is not readable"
129 [ ! -r "${descdir}/use.local.desc" ] && fatal "${descdir}/use.local.desc is not readable"
131 for make_defaults in $(get_all_make_defaults); do
132 [ ! -r "$make_defaults" ] && fatal "$_make_defaults is not readable"
134 [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && fatal ""${MAKE_CONF_PATH}" is not writable"
135 [ "${MODE}" == "modify" -a -s "${PACKAGE_USE_PATH}" -a ! -w "${PACKAGE_USE_PATH}" ] && fatal ""${PACKAGE_USE_PATH}" is not writable"
141 ${PROGRAM_NAME} (${VERSION})
143 Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]
145 Options: -h, --help - show this message
146 -V, --version - show version information
147 -i, --info - show descriptions for the given useflags
148 -I, --info-installed - show descriptions for the given useflags and
149 their current impact on the installed system
150 -g, --global - show only global use flags (suboption)
151 -l, --local - show only local use flags (suboption)
152 -a, --active - show currently active useflags and their origin
153 -E, --enable - enable the given useflags
154 -D, --disable - disable the given useflags
155 -R, --remove - remove all references to the given flags from
156 make.conf and package.use to revert to default
158 -P, --prune - alias for --remove
159 -p, --package - used with -E, -D, and -R to apply to a
160 specific package only
162 Notes: ${PROGRAM_NAME} currently works for global flags defined
163 in make.globals, make.defaults, make.conf, use.force, and use.mask
164 and local flags defined in package.use and individual package ebuilds.
165 It might have issues with cascaded profiles. If multiple options are
166 specified only the last one will be used.
172 ${PROGRAM_NAME} (${VERSION})
173 Written by Marius Mauch
175 Copyright (C) 2004-2009 Gentoo Foundation, Inc.
176 This is free software; see the source for copying conditions.
180 # Function: reduce_incrementals {{{
181 # remove duplicate flags from the given list in both positive and negative forms
182 # (but unlike portage always keep the last value even if it's negative)
183 # Otherwise the status flags could be incorrect if a flag appers multiple times in
184 # one location (like make.conf).
185 # Using python here as bash sucks for list handling.
186 # NOTE: bash isn't actually that bad at handling lists -- sh is. This may be
187 # worth another look to avoid calling python unnecessariy. Or we could
188 # just write the whole thing in python. ;)
189 reduce_incrementals() {
190 echo $@ | python -c "from __future__ import print_function;import sys
192 for x in sys.stdin.read().split():
193 if x[0] == '-' and x[1:] in r:
196 elif x[0] != '-' and '-'+x in r:
199 elif x == '-*': r = ['-*']
200 elif x not in r: r.append(x)
204 # Function: reduce_incrementals_trump {{{
205 # Similar to reduce_incrementals but negative flags trump positive
206 # flags, regardless of which follows which
207 reduce_incrementals_trump() {
208 echo $@ | python -c "from __future__ import print_function;import sys
210 for x in sys.stdin.read().split():
211 if x[0] == '-' and x[1:] in r:
214 elif x == '-*': r = ['-*']
215 elif x not in r and not '-'+x in r: r.append(x)
219 # Function: reduce_package_use {{{
220 # Similar to reduce_incrementals except converts lines from package atoms
221 # in /etc/portage/package.use files to lines of "pkg {[-]flag}*"
224 # * - Lines of package atom followed by flags
225 # (app-editors/vim flag1 flag2 -flag3)
226 reduce_package_use() {
227 echo "${@}" | python -c "from __future__ import print_function;import sys,re
228 h={}; getflags=re.compile(r'(-?[\w*-]+)')
229 for x in sys.stdin.read().split('\n'):
231 parts = x.lstrip().split(' ',1)
232 if len(parts)==1: continue
234 flags = getflags.findall(parts[1])
235 if not pkg in h: h[pkg]=[]
238 if x[0] == '-' and x[1:] in r:
241 elif x[0] != '-' and '-'+x in r:
244 elif x == '-*': r = h[pkg] = ['-*']
247 print('\n'.join(['%s %s' % (pkg,' '.join(flgs)) for pkg,flgs in h.items() if len(flgs)]))"
250 # Function: get_useflags {{{
251 # Creates a bash array ACTIVE_FLAGS that contains the global use flags,
252 # indexed by origin: 0: environment, 1: make.conf, 2: make.defaults,
253 # 3: make.globals, and local use flags, indexed by origin: 4: package.use,
254 # 5: ebuild IUSE, 6: use.mask, 7: use.force,
255 # 9: flags indicated active by emerge --info (get_portageuseflags)
258 if [[ -z ${ACTIVE_FLAGS[4]} && ( $SCOPE == "local" || -z $SCOPE ) ]]; then
259 # Parse through /etc/portage/package.use
260 if [[ -d ${PACKAGE_USE_PATH} ]]; then
261 ACTIVE_FLAGS[4]="$( cat ${PACKAGE_USE_PATH}/* \
262 | sed -re "s/ *#.*$//g" -e "s/^ *$//g" )"
263 elif [[ -e ${PACKAGE_USE_PATH} ]]; then
264 # JWM, 23/12/2009: I edited this following line but I'm not sure if it's 100% correct.
265 ACTIVE_FLAGS[4]="$( sed -re "s/ *#.*$//g" -e "s/^ *$//g" \
266 ${PACKAGE_USE_PATH})"
268 # Simplify ACTIVE_FLAGS[4] to be lines of pkg {[-]flag}*
269 ACTIVE_FLAGS[4]="$(reduce_package_use "${ACTIVE_FLAGS[4]}")"
271 # ACTIVE_FLAGS[5] reserved for USE flags defined in ebuilds and
272 # is generated/maintained in the get_useflaglist_ebuild() function
275 # only calculate once as calling emerge is painfully slow
276 [ -n "${USE_FLAGS_CALCULATED}" ] && return
278 # backup portdir so get_portdir() doesn't give false results later
279 portdir_backup="${PORTDIR}"
281 ACTIVE_FLAGS[0]="$(reduce_incrementals "${USE}")"
283 for x in $(get_all_make_conf); do
285 ACTIVE_FLAGS[1]="$(reduce_incrementals "${ACTIVE_FLAGS[1]}" "${USE}")"
288 for x in $(get_all_make_defaults); do
290 ACTIVE_FLAGS[2]="${ACTIVE_FLAGS[2]} ${USE}"
292 ACTIVE_FLAGS[2]="$(reduce_incrementals "${ACTIVE_FLAGS[2]}")"
294 source "${MAKE_GLOBALS_PATH}"
295 ACTIVE_FLAGS[3]="$(reduce_incrementals "${USE}")"
297 # restore saved env variables
298 USE="${ACTIVE_FLAGS[0]}"
299 PORTDIR="${portdir_backup}"
302 # Traverse through use.mask and use.force (0.5s)
303 # Flip signs of use.mask (it's interpreted oppositely),
304 ACTIVE_FLAGS[6]=$(reduce_incrementals_trump \
305 $(cat $(traverse_profile "use.mask") | sed -re "/^#.*$/{d}") \
306 | sed -re "s/(^| )-[^ ]*//g" -e "s/(^| )([a-z0-9])/ -\2/g")
307 ACTIVE_FLAGS[7]=$(reduce_incrementals \
308 $(cat $(traverse_profile "use.force") \
309 | sed -re "/^#.*$/ {d}"))
311 USE_FLAGS_CALCULATED=1
315 # Function: get_portageuseflags # {{{
316 # Fetch USE flags reported active by Portage
317 get_portageuseflags() {
318 # only calculate once as calling emerge is painfully slow
319 [ -n "${_PORTAGE_USE_FLAGS_CALCULATED}" ] && return
320 # get the currently active USE flags as seen by portage, this has to be after
321 # restoring USE or portage won't see the original environment
322 # Bug 181309, emerge may complain if EMERGE_DEFAULT_OPTS="--ask" is set
323 ACTIVE_FLAGS[9]="$(portageq envvar USE)" #'
324 _PORTAGE_USE_FLAGS_CALCULATED=1
327 # Function: get_useflaglist {{{
328 # Get the list of all known USE flags by reading use.desc and/or
329 # use.local.desc (depending on the value of $SCOPE). Also searches any
330 # registered overlays after searching the main portage tree first.
331 # Use flags visible in both the main tree and overlays are trumped by
332 # the main tree. Overlays are indicated by brackets [xxx] at the
333 # beginning of the description.
336 # (written to stdout) Sorted, unique list of system-wide USE flags and
337 # descriptions. Flags defined in overlays have the overlay in brackets
338 # prepended to the descriptions.
341 # SCOPE - [local|global] constrain search to local (use.local.desc) or
346 for profiledir in ${ALL_PORTDIRS[@]}; do
347 descdir="${profiledir}/profiles"
348 if [[ -z ${SCOPE} || ${SCOPE} == "global" ]]; then
349 [[ ! -s "${descdir}/use.desc" ]] && continue
350 egrep "^[^# ]+ +-" "${descdir}/use.desc"
352 if [[ -z ${SCOPE} || ${SCOPE} == "local" ]]; then
353 [[ ! -s "${descdir}/use.local.desc" ]] && continue
354 egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" \
357 done | cut -d " " -f1 | sort --field=":" --key=1,1 --unique
360 # Function: get_useflaglist_ebuild {{{
361 # Builds USE flag information for specified package atom into
362 # ACTIVE_FLAGS[5]. For the atom, the versions available are found, and
363 # for each, the corresponding SLOT, IUSE are stored along with which
364 # overlay the ebuild lives in. Considering that the pieces of information
365 # may be required in any order or any subsets, it is intended for the
366 # function to cache the information and it be retrieved from
367 # ACTIVE_FLAGS[5]. So the format of ACTIVE_FLAGS[5] is newline-separated
370 # category/packge;version;SLOT;IUSE;overlay
373 # $1 - Package atom to lookup (app-editor/vim)
376 # Nothing significant
379 # PORTDIR - Root of portage tree
380 # ACTIVE_FLAGS - Array of current use flag info
382 get_useflaglist_ebuild() {
383 local known=$(echo "${ACTIVE_FLAGS[5]}" | egrep "^${1}")
384 if [[ -n $known ]]; then
388 local pkg=$(echo ${1} | cut -d/ -f2)
390 for portdir in ${ALL_PORTDIRS[@]}; do
391 if [[ -s "${portdir}/profiles/repo_name" ]]; then
392 overlay="$(cat "${portdir}/profiles/repo_name")"
394 # XXX: Portage uses "x-<basename of the overlay>
395 overlay="x-$(basename "${portdir}")"
397 # Open the ebuild file and retrieve defined USE flags
398 [[ ! -d "$portdir/${1}" ]] && continue
399 if [[ ! -d "$portdir/metadata/cache" ]]; then
400 echo "!!! Metadata cache not found. You need to run " >&2
401 echo "!!! 'egencache --repo=$overlay --update'" >&2
402 echo "!!! to generate metadata for your overlays" >&2
405 append=$(set +f; ls $portdir/metadata/cache/${1}-* \
406 | egrep "${1}-[0-9.]+" \
407 | sed -e "s:$portdir/metadata/cache/${1}-::g" \
408 | while read -d $'\n' version; do
410 if [[ ! -e "$portdir/metadata/cache/${1}-$version" ]]; then
411 # Repo does not have this particular package
414 iuse=$(head -11 "$portdir/metadata/cache/${1}-$version"|tail -1)
415 slot=$(head -3 "$portdir/metadata/cache/${1}-$version"|tail -1)
416 echo "${1};${version};${slot};${iuse};${overlay}"
419 if [[ -z ${ACTIVE_FLAGS[5]} ]]; then ACTIVE_FLAGS[5]="$append"
420 else ACTIVE_FLAGS[5]="${ACTIVE_FLAGS[5]}"$'\n'"$append"
425 # get all make.conf files that exist on the system
426 get_all_make_conf() {
427 # At least one of the files exists or we would not have made it this far
428 for x in ${ETC}/make.conf ${ETC}/portage/make.conf; do
429 [ -e "${x}" ] && echo "${x}"
432 # Function: traverse_profile {{{
433 # General method of collecting the contents of a profile
434 # component by traversing through the cascading profile
437 # $1 - Filename (make.profile)
438 # [$2] - Current directory (unspecified means to start at the top)
444 curdir="${2:-$(get_real_path ${MAKE_PROFILE_PATH})}"
446 if [[ -f "${curdir}/parent" ]]; then
447 for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
448 # Bug 231394, handle parent path being absolute
449 if [[ ${parent:0:1} == "/" ]]; then
450 pdir="$(get_real_path ${parent})"
452 pdir="$(get_real_path ${curdir}/${parent})"
454 rvalue="${rvalue} $(traverse_profile ${1} ${pdir})"
457 [[ -f "${curdir}/${1}" ]] && rvalue="${rvalue} ${curdir}/${1}"
462 # Function: get_all_make_defaults {{{
463 # Det all make.defaults by traversing the cascaded profile directories
464 get_all_make_defaults() {
465 if [[ -z ${MAKE_DEFAULTS:-} ]]; then
466 MAKE_DEFAULTS=$(traverse_profile "make.defaults")
470 MAKE_DEFAULTS=$(get_all_make_defaults)
472 # Function: get_flagstatus_helper # {{{
473 # Little helper function to get the status of a given flag in one of the
474 # ACTIVE_FLAGS elements.
477 # (Written to STDOUT) Flag active status (+/-) or default string given
478 # in argument 4 or an empty space
482 # 2 - index of ACTIVE_FLAGS
483 # 3 - echo value for positive (and as lowercase for negative) test result
484 # 4 - (optional) echo value for "missing" test result, defaults to blank
485 get_flagstatus_helper() {
486 if [[ -z ${flags} ]]; then
487 local flags=${ACTIVE_FLAGS[${2}]}
489 local flag=$(echo " $flags " | grep -Eo " [+-]?${1} ")
490 if [[ ${flag:1:1} == "-" ]]; then
491 echo -e -n "${3}" | tr [:upper:]+ [:lower:]-
492 elif [[ -n ${flag} ]]; then
499 # Function: get_flagstatus_helper_pkg # {{{
500 # Entry to get_flagstatus_helper for packages which will fetch use
501 # flags set in package.use for the package and pass them on to
502 # get_flagstatus_helper. Also correcly handles lines in package.use
503 # specified for individual package versions
504 get_flagstatus_helper_pkg() {
505 if [[ -z ${2} ]]; then
508 elif [[ -z ${flags} ]]; then
509 # If no atoms are matchers (start with >,<,=, then they all match
511 if [[ -z "${atoms[@]/[<>=]*/}" ]]; then
513 echo "${atoms[@]}" | python -c "
514 from __future__ import print_function;import portage.dep as dep, sys
515 print(' '.join(dep.match_to_list('$5-$6',sys.stdin.read().split())))"))
517 flags=$(for atom in ${atoms[@]}; do
518 [[ -z $atom ]] && continue
519 echo "${ACTIVE_FLAGS[4]}" | \
520 grep "^ *$atom" | cut -d\ -f2-
523 if [[ -z ${5} || -z ${flags} ]]; then
527 get_flagstatus_helper "$@"
530 # Function: get_flagstatus_helper_ebuild {{{
531 # get_flagstatus_helper replacement for packages to fetch ebuild USE flag
535 # (Written to STDOUT) Flag active status (+/-) or default string given
536 # in argument 4 or an empty space. If USE flag is not defined in the list
537 # of flags (2), an '!' is written
541 # 2 - IUSE line from ebuild file
542 # 3 - echo value for positive (and as lowercase for negative) test result
543 # 4 - (optional) echo value for "missing" test result, defaults to blank space
544 get_flagstatus_helper_ebuild() {
545 local flags=$(echo $2 | cut -d\" -f2)
546 local flag=$(echo " $flags " | grep -Eo " [+-]?$1 ")
547 if [[ ${flag:1:1} == "+" ]]; then
549 elif [[ ${flag:1:1} == "-" ]]; then
550 echo -en "${3}" | tr [:upper:]+ [:lower:]-
551 elif [[ -z $flag ]]; then
558 # Function: get_flagstatus {{{
559 # Prints a status string for the given flag, each column indicating the presence
560 # for portage, in the environment, in make.conf, in make.defaults, in
561 # make.globals, and in use.force and flipped in use.mask.
564 # 1 - use flag for which to retrieve status
567 # 0 (True) if flag is active, 1 (False) if not active
570 # Full positive value would be "[+ECDGFm] ", full negative value would be [-ecdgfM],
571 # full missing value would be "[- ] " (portage only sees present or not present)
575 local E=$(get_flagstatus_helper "${1}" 0 "E")
576 local C=$(get_flagstatus_helper "${1}" 1 "C")
577 local D=$(get_flagstatus_helper "${1}" 2 "D")
578 local G=$(get_flagstatus_helper "${1}" 3 "G")
579 local M=$(get_flagstatus_helper "${1}" 6 "M")
580 local F=$(get_flagstatus_helper "${1}" 7 "F")
581 # Use flags are disabled by default
584 # Use flag precedence is defined (at least) at:
585 # http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=2
586 for location in "E" "C" "D" "G" "F" "M"; do
587 if [[ ${!location} == $location ]]; then
590 elif [[ ${!location} != " " ]]; then
595 echo -n "[${ACTIVE:--}$E$C$D$G$F$M] "
596 [[ $ACTIVE == "+" ]]; return $?
599 # Function: get_flagstatus_pkg {{{
600 # Outputs local flag status information for a specific package and perhaps
601 # specific package version.
605 # 2 - package atom (www-client/elinks)
606 # 3 - +/- whether flag is enabled by global configuration files
607 # 4 - (Optional) version of package to evaluate (empty means all versions)
610 # Flag status for package.use and ebuild, slot and version, and overlay
611 # the version lives is if not PORTDIR
613 # Full positive would be "[+PB]", full negative would be "[-pb]", and full
614 # missing would be "[? ]", question because the sign will default to the
615 # sign of the global status of the flag
616 get_flagstatus_pkg() {
618 # Pre-cache the use flags declared in ebuilds first.
619 # This is required as calling it inside a $() seems to
620 # prevent caching of results into $ACTIVE_FLAGS array
621 get_useflaglist_ebuild ${2}
623 # Get list of ebuilds available for this package. The
624 # get_useflaglist_ebuild() function stores them in $ACTIVE_FLAGS[5]
625 # with the package name leading the lines. The other information
626 # is in the same line, semi-colon (;) separated. The fields are
627 # package;version;SLOT;IUSE;overlay
629 # Fetch package atoms and flags from package.use only once
630 local atoms=$(echo "${ACTIVE_FLAGS[4]}" | \
631 grep -Eo "^ *[<>]?=?${2}(-[0-9rp._*-]*)?")
632 local IFS=$'\n'; for pkgline in $(echo "${ACTIVE_FLAGS[5]}" | grep "^$2" \
633 | sort -t \; -k2,2 -V); do
634 OIFS=$IFS; IFS=";"; INFO=($pkgline); IFS=$OIFS;
635 local version=${INFO[1]}
636 [[ -n $4 && $4 != $version ]] && continue
637 local slot=${INFO[2]}
638 local iuse=${INFO[3]}
639 if [[ $slot == '0' || $slot == "" ]]; then
642 slot="($(echo ${slot} | cut -d\" -f2)) "
644 local overlay=${INFO[4]}
645 [[ -n $overlay ]] && overlay="[$overlay]"
647 # Fetch enabled status for this version
648 local P=$(get_flagstatus_helper_pkg "${1}" "${atoms}" "P" "" "${2}" "${version}")
649 local B=$(get_flagstatus_helper_ebuild "${1}" "${iuse}" "B" "" "${2}" "${version}")
652 for location in "P" "B"; do
653 if [[ ${!location} == $location ]]; then
655 elif [[ ${!location} == "!" ]]; then
658 elif [[ ${!location} != " " ]]; then
663 if [[ $UNUSED == 1 ]]; then
664 echo " ${slot}${version} ${overlay}"
666 echo " [${ACTIVE:-${3:- }}$P$B] ${slot}${version} ${overlay}"
672 # Function: get_portdir {{{
673 # faster replacement to `portageq portdir`
676 # Location of portage tree root
678 # Use a subshell so we don't have to protect the variables in
681 if [ -z "${PORTDIR:-}" ]; then
682 source "${MAKE_GLOBALS_PATH}"
683 for x in $(get_all_make_defaults); do
686 for x in $(get_all_make_conf); do
693 # This won't change while the script is running, so cache it
694 PORTDIR="$(get_portdir)"
696 # Function: get_all_overlays {{{
697 # Outputs list of portage overlays as defined in the PORTDIR_OVERLAY
698 # variable defined in make.conf
700 # Use a subshell so we don't have to protect the variables in
703 for x in $(get_all_make_conf); do
704 [[ -r "${x}" ]] && source "${x}"
706 echo ${PORTDIR_OVERLAY}
709 ALL_PORTDIRS=( "$PORTDIR" $(get_all_overlays) )
711 # Function: array_contains {{{
712 # PHP-style array_contains function.
719 # 0 (True) if needle in haystack, null (False) otherwise
721 for i in $1; do [[ $i == $2 ]] && return 0; done
725 # Function: showdesc {{{
726 # This function takes a list of use flags and shows the status and
727 # the description for each one, honoring $SCOPE
730 # * - USE flags for which to display descriptions. Undefined means to
731 # display descriptions for all known USE flags
734 # SCOPE - defines whether to output local or global USE flag descriptions
735 # Empty means to display both
738 # (STDOUT) Flag description(s) for given USE flags
748 if [ -z "${SCOPE}" ]; then
749 SCOPE="global" showdesc ${args}
751 SCOPE="local" showdesc ${args}
755 local useflags=( $(echo "$(get_useflaglist)") )
757 [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
758 [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
759 echo "************************************************************"
760 if [ "${args}" == "*" ]; then
761 args="${useflags[*]}"
767 while [[ -n "${1}" ]]; do
768 if [[ "${SCOPE}" == "global" ]]; then
769 if array_contains "${useflags[*]}" "$1"; then
770 get_flagstatus "${1}"
771 # XXX: Handle overlay
772 grep -h "^${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.desc} 2> /dev/null
776 # local flags are a bit more complicated as there can be multiple
777 # entries per flag and we can't pipe into printf
778 if [[ "${SCOPE}" == "local" ]]; then
779 if array_contains "${useflags[*]}" "$1"; then
782 # Fetch all the packages data using this flag
783 infos=$( grep -h ":${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.local.desc} 2> /dev/null \
784 | sed -re "s/^([^:]+):(${1}) *- *(.+)/\1|\2|\3/g")
785 OIFS=$IFS; IFS=$'\n'; infos=($infos); IFS=$OIFS;
786 for line in "${infos[@]}"; do
787 OIFS=$IFS; IFS="|"; line=($line); IFS=$OIFS
791 if get_flagstatus "${flag}"; then
796 printf "%s\n" "${flag}"
797 printf "%s: %s\n" "${pkg}" "${desc}" \
798 | fold --width=$((${COLUMNS:-80}-10)) -s | sed -e "s/^/ /g"
799 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}"
805 if [[ ${foundone} == 0 ]]; then
806 echo "no matching entries found"
810 # Function: showinstdesc {{{
811 # Works like showdesc() but displays only descriptions of which the appropriate
812 # ebuild is installed and prints the name of those packages.
815 # * - USE flags for which to display descriptions. Undefined means to
816 # display descriptions for all known USE flags
819 # SCOPE - defines whether to output local or global USE flag descriptions
820 # Empty means to display both
823 # (STDOUT) Flag description(s) for given USE flags along with installed
836 "global") echo "global use flags (searching: ${args[@]})";;
837 "local") echo "local use flags (searching: ${args[@]})";;
838 *) SCOPE="global" showinstdesc "${args[@]}"
840 SCOPE="local" showinstdesc "${args[@]}"
844 descdir="$(get_portdir)/profiles"
845 echo "************************************************************"
847 if [ "${args}" = "*" ]; then
848 args="$(get_useflaglist | sort -u)"
853 while [ -n "${1}" ]; do
856 if desc=$(grep "^${1} *-" "${descdir}/use.desc"); then
857 get_flagstatus "${1}"
859 # get list of installed packages matching this USE flag.
861 packages=($(equery -q -C hasuse -i "${1}" | awk '{ print $(NF-1) }' | sort))
862 foundone+=${#packages[@]}
863 printf "\nInstalled packages matching this USE flag: "
864 if [ ${foundone} -gt 0 ]; then
865 echo $'\n'"${packages[*]}"
872 # local flags are a bit more complicated as there can be multiple
873 # entries per flag and we can't pipe into printf
874 IFS=': ' # Use a space instead of a dash because dashes occur in cat/pkg
875 while read pkg flag desc; do
876 # print name only if package is installed
877 # NOTE: If we implement bug #114086 's enhancement we can just use the
878 # exit status of equery instead of a subshell and pipe to wc -l
879 # Bug 274472, -e is the default
880 if [ $(equery -q -C list -i "${pkg}" | wc -l) -gt 0 ]; then
883 get_flagstatus "${flag}" "${pkg}"
885 printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc#- }"
887 done < <(grep ":${1} *-" "${descdir}/use.local.desc")
893 if [ ${foundone} -lt 1 ]; then
894 echo "no matching entries found"
899 # Function: showflags {{{
900 # show a list of all currently active flags and where they are activated
908 if [ "${args}" == "*" ]; then
909 args="$(get_useflaglist | sort -u)"
915 while [ -n "${1}" ]; do
916 if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
921 if echo " ${ACTIVE_FLAGS[4]} " | egrep -e " -?${1} " > /dev/null; then
922 for pkg in $( echo "${ACTIVE_FLAGS[4]}" | \
923 egrep " -?${1} " | cut -d " " -f 2); do
925 SCOPE="local" get_flagstatus ${1} "${pkg}"
926 printf "(%s)\n" ${pkg}
933 # two small helpers to add or remove a flag from a USE string
935 # Remove leading '-' from flag if found
937 [[ ${flag:0:1} == "-" ]] && flag=${1:1}
939 if [[ -n $(grep " -${flag} " <<< " ${ACTIVE_FLAGS[6]} ") ]]; then
940 error "Use flag \"${flag}\" is masked and should not be added" \
943 # Bug #104396 -- Only add use flags defined in use.desc and use.local.desc
944 elif [[ -z $(grep "^${flag}$" <<< "$(get_useflaglist)") ]]; then
945 error "Use flag \"${flag}\" is not defined in use.desc and should" \
946 "not be added\nto make.conf."
949 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
950 echo "Adding flag \"${1}\" to make.conf" >&2
955 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
956 echo "Removing flag \"${1}\" from make.conf" >&2
959 # Function: clean_package_use {{{
960 # Simple utility to remove empty files from package.use
961 clean_package_use() {
962 if [[ -d ${PACKAGE_USE_PATH} ]]; then
963 for f in $(find ${PACKAGE_USE_PATH} -size 0); do
964 echo "Removing empty file ""${f}"""
970 # Function: scrub_use_flag {{{
971 # Utility to remove a use flag from a file in package.use[/]
978 # PACKAGE - Package atom for which to remove flag
980 local atom_re="^[<>]?=?([a-z][\da-z/-]+[a-z])(-[0-9pr._*-]+)?"
982 # Ignore leading - on flag
984 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
985 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
988 # Skip (preserve) comments on their own lines
989 if [[ -z $(echo "${line}" | sed -re "s/^ *#.*$//") ]]; then
991 # Detect if requested package is defined on this line
992 elif [[ -n "${PACKAGE}" ]]; then
993 if [[ -n $(echo "${line}" | grep -Ee "${pkg_re}") ]]; then
994 # If this is the only (remaining) use flag defined
995 # for this package, then remove the whole line
996 if [[ -z $(echo "${line}" | grep -Ee "${pkg_re} *-?${flag} *$") ]]; then
997 # Remove flag from this line
998 echo "${line}" | sed -re "s/ *-?\b${flag}\b//"
1004 # If line only has this use flag, let it be removed
1005 # (used if PACKAGE is not defined -- from pruning)
1006 elif [[ -n $(echo "${line}" | \
1007 egrep "^[^#]*${atom_re}.*-?${flag}") ]]; then
1008 echo "Removing use flag from ${line}" >&2
1009 if [[ -z $(echo "${line}" | \
1010 grep -Ee "${atom_re} *-?${flag} *$") ]]; then
1011 # Remove flag from this line
1012 echo "${line}" | sed -re "s/-?\b${flag}\b//"
1018 done > "${filename}.new" < "${filename}"
1019 mv "${filename}.new" "${filename}"
1022 # Function: modify_package {{{
1023 # Adds and removes USE flags from individual packages by modifying
1024 # files in package.use. It supports package.use both as a file and
1025 # and as a folder. Also handles "enabling" as removing a disabled flag from
1026 # a file, and "disabling" a globally enabled flag by adding a negative to
1027 # a file. Also warns about unused and unknown flags, and about flags
1028 # already enabled, disabled, or masked.
1031 # * - USE flag(s) to add or remove
1034 # PACKAGE - Package for which to add (-E) or remove (-D) the USE
1039 local atom_re="^[<>]?=?([a-z][0-9a-z/-]+[a-z])(-[0-9pr._*-]+)?"
1040 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
1041 local V=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\2/")
1042 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
1044 local all_flags=$(SCOPE= get_useflaglist)
1045 # Shift at top rather than bottom to support 'continue'
1047 while [[ -n ${2} ]]; do
1053 # Fetch flag ACTIVE status (+,-,null)
1054 get_flagstatus "${flag}" "${pkg}" > /dev/null
1055 GLOBAL_ACTIVE="$ACTIVE"
1056 # XXX: If V is not given, is this necessary? Should it use the version
1057 # that would be installed by emerge?
1058 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}" "${V}" > /dev/null
1061 # (1) make sure ${pkg} exists in portdir or an overlay
1063 for portdir in ${ALL_PORTDIRS[@]}; do
1064 if [[ -d "${portdir}/${pkg}" ]]; then
1069 if [[ $exists == 1 ]]; then
1070 fatal "Package \"${pkg}\" does not exist"
1072 # (2) make sure ${flag} is defined in get_useflaglist
1073 elif ! array_contains "$all_flags" ${flag}; then
1074 error "USE flag \"${flag}\" does not exist"
1076 # Don't bail just because of this, just warn
1077 # (3) make sure use flag is valid for the package
1078 elif [[ -z $(echo "${ACTIVE_FLAGS[5]} " | grep -Ee "^${pkg_re}" \
1079 | grep -Ee "[; ][+-]?${flag}") ]]; then
1080 # XXX: Handle version or version wildcard?
1081 warn "USE flag \"${flag}\" is not used by $PACKAGE"
1082 # Don't necessarily bail for this, just warn
1083 elif [[ -n "${V}" && -z "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1084 error "Invalid package atom. Did you forget the leading '='?"
1086 elif [[ -z "${V}" && -n "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1087 error "Invalid package atom. Did you forget the version?"
1090 # If removing a disabled flag, or adding an enabled one, emit a warning,
1091 # indicating a likely misunderstanding
1092 if [[ "${ACTION}" == "remove" ]]; then
1093 if [[ "${ACTIVE:-${GLOBAL_ACTIVE}}" == "-" ]]; then
1094 warn "USE flag \"$flag\" is already disabled for $PACKAGE"
1097 elif [[ "${ACTION}" == "prune" ]]; then
1098 # Just remove the flag below
1099 [[ "${ACTIVE}" == "-" ]] && flag="-${flag}"
1101 elif [[ "${ACTION}" == "add" ]]; then
1102 if [[ "${ACTIVE:-${GLOBAL_ACTIVE:--}}" == "+" ]]; then
1103 # XXX: Perhaps look at indicating where it is enabled
1104 warn "USE flag \"$flag\" is already enabled for $PACKAGE"
1110 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1111 # Use naming convention of package.use/package
1112 filename="${PACKAGE_USE_PATH}/${pkg#*/}"
1113 if [[ ! -s "${filename}" ]]; then
1114 # Create new file to contain flag
1115 echo "${PACKAGE} ${flag}" > "${filename}"
1116 echo "Adding \"${PACKAGE}[${flag}]\" use flag to new file ""${filename}"""
1120 # Add to package.use file instead
1121 filename="${PACKAGE_USE_PATH}"
1122 # Create as necessary
1125 # Walk through the file and add the flag manually
1126 echo "Adding \"${PACKAGE}[${flag}]\" use flag in \"${filename}\""
1129 if [[ -n $(echo "${line}" | egrep -e "^[^#]*${PACKAGE} ") ]]; then
1130 echo $(reduce_package_use "${line} ${flag}")
1136 done < "${filename}" > "${filename}.new"
1137 mv "${filename}.new" "${filename}"
1138 if [[ ${added} -eq 0 ]]; then
1139 echo "${PACKAGE} ${flag}" >> "${filename}"
1144 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1145 # Scan for file containing named package and use flag
1146 filename=$(egrep -rle "${pkg_re}.*[^-]${flag}( |$)" "${PACKAGE_USE_PATH}")
1147 if [[ -z "${filename}" ]]; then
1148 error ""${flag}" is not defined for package "${PACKAGE}""
1152 # Remove from package.use instead
1153 filename=${PACKAGE_USE_PATH}
1154 # Create as necessary
1157 # Scrub use flag from matched files
1158 for f in ${filename}; do
1159 # Remove current flags in file
1160 echo "Removing \"${PACKAGE}[${flag}]\" use flag in \"${f}\""
1161 scrub_use_flag ${f} ${flag}
1163 # Remove empty files
1170 # Function: modify {{{
1171 # USE flag modification function. Mainly a loop with calls to add_flag and
1172 # remove_flag to create a new USE string which is then inserted into make.conf.
1174 if [[ -n "${PACKAGE}" ]]; then
1175 modify_package "${*}"
1179 local make_conf_modified=0
1181 if [ -z "${*}" ]; then
1182 if [ "${ACTION}" != "prune" ]; then
1183 echo "WARNING: no USE flags listed for modification, do you really"
1184 echo " want to ${ACTION} *all* known USE flags?"
1185 echo " If you don't please press Ctrl-C NOW!!!"
1187 set $(get_useflaglist | sort -u)
1193 NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
1195 while [[ -n "${1}" ]]; do
1196 if [[ "${ACTION}" == "add" ]]; then
1197 if [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1198 warn "Use flag \"${1}\" is already enabled globally"
1200 elif [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1201 remove_flag "-${1}" || exit
1202 make_conf_modified=1
1204 add_flag "${1}" || exit
1205 make_conf_modified=1
1208 elif [[ "${ACTION}" == "remove" ]]; then
1209 if [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1210 warn "Use flag \"${1}\" is already disabled globally"
1212 elif [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1213 remove_flag "${1}" || exit
1214 make_conf_modified=1
1216 add_flag "-${1}" || exit
1217 make_conf_modified=1
1220 elif [[ "${ACTION}" == "prune" ]]; then
1221 if [[ -n $(grep " ${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1222 remove_flag "${1}" || exit
1223 make_conf_modified=1
1224 elif [[ -n $(grep " -${1} " <<< " ${NEW_MAKE_CONF_USE} ") ]]; then
1225 remove_flag "-${1}" || exit
1226 make_conf_modified=1
1228 warn "Use flag \"${1}\" is not set globally"
1234 # a little loop to add linebreaks so we don't end with one ultra-long line
1235 NEW_MAKE_CONF_USE_2=""
1236 for x in ${NEW_MAKE_CONF_USE}; do
1237 if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
1238 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n $x "
1240 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
1244 # Bail if there is no need to modify make.conf
1245 [[ ${make_conf_modified} == 1 ]] || return
1246 # make a backup just in case the user doesn't like the new make.conf
1247 cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
1249 # as sed doesn't really work with multi-line patterns we have to replace USE
1250 # on our own here. Basically just skip everything between USE=" and the
1251 # closing ", printing our new USE line there instead.
1255 (while [ "$x" -eq "0" ]; do
1256 read -r line || break
1258 # Bug 275362 - Handle the case where make.conf includes:
1262 # Consume USE=" when detected so the quote won't be detected
1263 # as the ending quote
1264 if [ "${line:0:4}" == "USE=" ]; then inuse=1; line=${line:5}; fi
1265 [ "${inuse}" == "0" ] && echo -E "${line}"
1266 if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
1268 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1274 if [ ${had_use} -eq 0 ]; then
1276 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1278 fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
1280 echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
1283 ##### main program comes now #####
1285 parse_arguments "$@"
1288 eval ${MODE} ${ARGUMENTS}
1290 # vim: set tabstop=4 shiftwidth=4: