5 # bash replacement for the original euse by Arun Bhanu
6 # Author: Marius Mauch <genone@gentoo.org>
7 # Jared Hancock (Signigicant rewrite for package.use support)
8 # Licensed under the GPL v2
13 EPREFIX=${EPREFIX:-$(portageq envvar EPREFIX)}
15 USR_SHARE_PORTAGE="${EPREFIX}/usr/share/portage"
17 # define error functions so they can be used immediately
29 echo -e "WARNING: ${*}"
32 # /etc/make.conf can now exist in /etc/portage/make.conf, prefer it over /etc/make.conf for changes
33 if [ -e "${ETC}/portage/make.conf" ]; then
34 MAKE_CONF_PATH="${ETC}/portage/make.conf"
35 elif [ -e "${ETC}/make.conf" ]; then
36 MAKE_CONF_PATH="${ETC}/make.conf"
38 fatal "make.conf does not exist"
40 MAKE_CONF_BACKUP_PATH="${MAKE_CONF_PATH}.euse_backup"
42 # /etc/make.globals has been moved to /usr/share/portage/config/make.globals
43 if [ -e "${USR_SHARE_PORTAGE}/config/make.globals" ]; then
44 MAKE_GLOBALS_PATH="${USR_SHARE_PORTAGE}/config/make.globals"
46 MAKE_GLOBALS_PATH="${ETC}/make.globals"
49 # /etc/make.profile or /etc/portage/make.profile, if /etc/make.profile exists, it will be used
50 if [ -e "${ETC}/make.profile" ]; then
51 MAKE_PROFILE_PATH="${ETC}/make.profile"
52 elif [ -e "${ETC}/portage/make.profile" ]; then
53 MAKE_PROFILE_PATH="${ETC}/portage/make.profile"
55 fatal "make.profile does not exist"
57 PACKAGE_USE_PATH=${ETC}/portage/package.use
59 [ -z "${MODE}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
62 if [ -z "${1}" ]; then
65 while [ -n "${1}" ]; do
67 -h | --help) MODE="showhelp";;
68 -V | -v | --version) MODE="showversion";;
69 -i | --info) MODE="showdesc";;
70 -I | --info-installed) MODE="showinstdesc";;
71 -l | --local) SCOPE="local";;
72 -g | --global) SCOPE="global";;
73 -a | --active) MODE="showflags";;
74 -E | --enable) MODE="modify"; ACTION="add";;
75 -D | --disable) MODE="modify"; ACTION="remove";;
76 -P | --prune | -R | --remove)
77 MODE="modify"; ACTION="prune";;
78 -p | --package) MODE="modify"; shift; PACKAGE=${1}; SCOPE="local";;
80 echo "ERROR: unknown option ${1} specified."
86 ARGUMENTS="${ARGUMENTS} ${ACTIVE_FLAGS[9]}"
89 ARGUMENTS="${ARGUMENTS} ${1}"
104 # Function: check_sanity {{{
105 # Performs some basic system sanity checks
107 # file permission tests
112 [[ ! -d "${MAKE_PROFILE_PATH}" || ! -r "${MAKE_PROFILE_PATH}" ]] && error "${MAKE_PROFILE_PATH} is not readable"
114 for make_conf in $(get_all_make_conf); do
115 [ ! -r "${make_conf}" ] && fatal "${make_conf} is not readable"
118 descdir="$(get_portdir)/profiles"
120 [ ! -r "${MAKE_GLOBALS_PATH}" ] && fatal "${MAKE_GLOBALS_PATH} is not readable"
121 [ -z "$(get_portdir)" ] && fatal "\$PORTDIR couldn't be determined"
122 [ ! -d "${descdir}" ] && fatal "${descdir} does not exist or is not a directory"
123 [ ! -r "${descdir}/use.desc" ] && fatal "${descdir}/use.desc is not readable"
124 [ ! -r "${descdir}/use.local.desc" ] && fatal "${descdir}/use.local.desc is not readable"
126 for make_defaults in $(get_all_make_defaults); do
127 [ ! -r "$make_defaults" ] && fatal "$_make_defaults is not readable"
129 [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && fatal ""${MAKE_CONF_PATH}" is not writable"
130 [ "${MODE}" == "modify" -a -s "${PACKAGE_USE_PATH}" -a ! -w "${PACKAGE_USE_PATH}" ] && fatal ""${PACKAGE_USE_PATH}" is not writable"
135 ${PROGRAM_NAME} (${VERSION})
137 Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]
139 Options: -h, --help - show this message
140 -V, --version - show version information
141 -i, --info - show descriptions for the given useflags
142 -I, --info-installed - show descriptions for the given useflags and
143 their current impact on the installed system
144 -g, --global - show only global use flags (suboption)
145 -l, --local - show only local use flags (suboption)
146 -a, --active - show currently active useflags and their origin
147 -E, --enable - enable the given useflags
148 -D, --disable - disable the given useflags
149 -R, --remove - remove all references to the given flags from
150 make.conf and package.use to revert to default
152 -P, --prune - alias for --remove
153 -p, --package - used with -E, -D, and -R to apply to a
156 Notes: ${PROGRAM_NAME} currently works for global flags defined
157 in make.globals, make.defaults, make.conf, use.force, and use.mask
158 and local flags defined in package.use and individual package ebuilds.
159 It might have issues with cascaded profiles. If multiple options are
160 specified only the last one will be used.
166 ${PROGRAM_NAME} (${VERSION})
167 Written by Marius Mauch
169 Copyright (C) 2004-2009 Gentoo Foundation, Inc.
170 This is free software; see the source for copying conditions.
174 # Function: reduce_incrementals {{{
175 # remove duplicate flags from the given list in both positive and negative forms
176 # (but unlike portage always keep the last value even if it's negative)
177 # Otherwise the status flags could be incorrect if a flag appers multiple times in
178 # one location (like make.conf).
179 # Using python here as bash sucks for list handling.
180 # NOTE: bash isn't actually that bad at handling lists -- sh is. This may be
181 # worth another look to avoid calling python unnecessariy. Or we could
182 # just write the whole thing in python. ;)
183 reduce_incrementals() {
184 echo $@ | python -c "import sys
186 for x in sys.stdin.read().split():
187 if x[0] == '-' and x[1:] in r:
190 elif x[0] != '-' and '-'+x in r:
193 elif x == '-*': r = ['-*']
194 elif x not in r: r.append(x)
198 # Function: reduce_incrementals_trump {{{
199 # Similar to reduce_incrementals but negative flags trump positive
200 # flags, regardless of which follows which
201 reduce_incrementals_trump() {
202 echo $@ | python -c "import sys
204 for x in sys.stdin.read().split():
205 if x[0] == '-' and x[1:] in r:
208 elif x == '-*': r = ['-*']
209 elif x not in r and not '-'+x in r: r.append(x)
213 # Function: reduce_package_use {{{
214 # Similar to reduce_incrementals except converts lines from package atoms
215 # in /etc/portage/package.use files to lines of "pkg {[-]flag}*"
218 # * - Lines of package atom followed by flags
219 # (app-editors/vim flag1 flag2 -flag3)
220 reduce_package_use() {
221 echo "${@}" | python -c "import sys,re
222 h={}; getflags=re.compile(r'(-?[\w*-]+)')
223 for x in sys.stdin.read().split('\n'):
225 parts = x.lstrip().split(' ',1)
226 if len(parts)==1: continue
228 flags = getflags.findall(parts[1])
229 if not pkg in h: h[pkg]=[]
232 if x[0] == '-' and x[1:] in r:
235 elif x[0] != '-' and '-'+x in r:
238 elif x == '-*': r = h[pkg] = ['-*']
241 print '\n'.join(['%s %s' % (pkg,' '.join(flgs)) for pkg,flgs in h.iteritems() if len(flgs)])"
244 # Function: get_useflags {{{
245 # Creates a bash array ACTIVE_FLAGS that contains the global use flags,
246 # indexed by origin: 0: environment, 1: make.conf, 2: make.defaults,
247 # 3: make.globals, and local use flags, indexed by origin: 4: package.use,
248 # 5: ebuild IUSE, 6: use.mask, 7: use.force,
249 # 9: flags indicated active by emerge --info (get_portageuseflags)
251 if [[ -z ${ACTIVE_FLAGS[4]} && ( $SCOPE == "local" || -z $SCOPE ) ]]; then
252 # Parse through /etc/portage/package.use
253 if [[ -d ${PACKAGE_USE_PATH} ]]; then
254 ACTIVE_FLAGS[4]="$( cat ${PACKAGE_USE_PATH}/* \
255 | sed -re "s/ *#.*$//g" -e "s/^ *$//g" )"
256 elif [[ -e ${PACKAGE_USE_PATH} ]]; then
257 # JWM, 23/12/2009: I edited this following line but I'm not sure if it's 100% correct.
258 ACTIVE_FLAGS[4]="$( sed -re "s/ *#.*$//g" -e "s/^ *$//g" \
259 ${PACKAGE_USE_PATH})"
261 # Simplify ACTIVE_FLAGS[4] to be lines of pkg {[-]flag}*
262 ACTIVE_FLAGS[4]="$(reduce_package_use "${ACTIVE_FLAGS[4]}")"
264 # ACTIVE_FLAGS[5] reserved for USE flags defined in ebuilds and
265 # is generated/maintained in the get_useflaglist_ebuild() function
268 # only calculate once as calling emerge is painfully slow
269 [ -n "${USE_FLAGS_CALCULATED}" ] && return
271 # backup portdir so get_portdir() doesn't give false results later
272 portdir_backup="${PORTDIR}"
274 ACTIVE_FLAGS[0]="$(reduce_incrementals ${USE})"
276 for x in $(get_all_make_conf); do
278 ACTIVE_FLAGS[1]="$(reduce_incrementals ${ACTIVE_FLAGS[1]} ${USE})"
281 for x in $(get_all_make_defaults); do
283 ACTIVE_FLAGS[2]="${ACTIVE_FLAGS[2]} ${USE}"
285 ACTIVE_FLAGS[2]="$(reduce_incrementals ${ACTIVE_FLAGS[2]})"
287 source "${MAKE_GLOBALS_PATH}"
288 ACTIVE_FLAGS[3]="$(reduce_incrementals ${USE})"
290 # restore saved env variables
291 USE="${ACTIVE_FLAGS[0]}"
292 PORTDIR="${portdir_backup}"
295 # Traverse through use.mask and use.force (0.5s)
296 # Flip signs of use.mask (it's interpreted oppositely),
297 ACTIVE_FLAGS[6]=$(reduce_incrementals_trump \
298 $(cat $(traverse_profile "use.mask") | sed -re "/^#.*$/{d}") \
299 | sed -re "s/(^| )-[^ ]*//g" -e "s/(^| )([a-z0-9])/ -\2/g")
300 ACTIVE_FLAGS[7]=$(reduce_incrementals \
301 $(cat $(traverse_profile "use.force") \
302 | sed -re "/^#.*$/ {d}"))
304 USE_FLAGS_CALCULATED=1
307 # Function: get_portageuseflags # {{{
308 # Fetch USE flags reported active by Portage
309 get_portageuseflags() {
310 # only calculate once as calling emerge is painfully slow
311 [ -n "${_PORTAGE_USE_FLAGS_CALCULATED}" ] && return
312 # get the currently active USE flags as seen by portage, this has to be after
313 # restoring USE or portage won't see the original environment
314 # Bug 181309, emerge may complain if EMERGE_DEFAULT_OPTS="--ask" is set
315 ACTIVE_FLAGS[9]="$(portageq envvar USE)" #'
316 _PORTAGE_USE_FLAGS_CALCULATED=1
319 # Function: get_useflaglist {{{
320 # Get the list of all known USE flags by reading use.desc and/or
321 # use.local.desc (depending on the value of $SCOPE). Also searches any
322 # registered overlays after searching the main portage tree first.
323 # Use flags visible in both the main tree and overlays are trumped by
324 # the main tree. Overlays are indicated by brackets [xxx] at the
325 # beginning of the description.
328 # (written to stdout) Sorted, unique list of system-wide USE flags and
329 # descriptions. Flags defined in overlays have the overlay in brackets
330 # prepended to the descriptions.
333 # SCOPE - [local|global] constrain search to local (use.local.desc) or
338 for profiledir in ${ALL_PORTDIRS[@]}; do
339 descdir="${profiledir}/profiles"
340 if [[ -z ${SCOPE} || ${SCOPE} == "global" ]]; then
341 [[ ! -s "${descdir}/use.desc" ]] && continue
342 egrep "^[^# ]+ +-" "${descdir}/use.desc"
344 if [[ -z ${SCOPE} || ${SCOPE} == "local" ]]; then
345 [[ ! -s "${descdir}/use.local.desc" ]] && continue
346 egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" \
349 done | cut -d " " -f1 | sort --field=":" --key=1,1 --unique
352 # Function: get_useflaglist_ebuild {{{
353 # Builds USE flag information for specified package atom into
354 # ACTIVE_FLAGS[5]. For the atom, the versions available are found, and
355 # for each, the corresponding SLOT, IUSE are stored along with which
356 # overlay the ebuild lives in. Considering that the pieces of information
357 # may be required in any order or any subsets, it is intended for the
358 # function to cache the information and it be retrieved from
359 # ACTIVE_FLAGS[5]. So the format of ACTIVE_FLAGS[5] is newline-separated
362 # category/packge;version;SLOT;IUSE;overlay
365 # $1 - Package atom to lookup (app-editor/vim)
368 # Nothing significant
371 # PORTDIR - Root of portage tree
372 # ACTIVE_FLAGS - Array of current use flag info
374 get_useflaglist_ebuild() {
375 local known=$(echo "${ACTIVE_FLAGS[5]}" | egrep "^${1}")
376 if [[ -n $known ]]; then
380 local pkg=$(echo ${1} | cut -d/ -f2)
382 for portdir in ${ALL_PORTDIRS[@]}; do
383 # Open the ebuild file and retrieve defined USE flags
384 [[ ! -d "$portdir/${1}" ]] && continue
385 if [[ ! -d "$portdir/metadata/cache" ]]; then
386 echo "!!! Metadata cache not found. You need to run " >&2
387 echo "!!! 'egencache --repo=$overlay --update'" >&2
388 echo "!!! to generate metadata for your overlays" >&2
391 append=$(ls $portdir/metadata/cache/${1}-* \
392 | egrep "${1}-[0-9.]+" \
393 | sed -e "s:$portdir/metadata/cache/${1}-::g" \
394 | while read -d $'\n' version; do
396 if [[ $portdir == $PORTDIR ]]; then
399 if [[ -s $(dirname ${portdir}/repo_name) ]]; then
400 overlay="$(cat "${portdir}/profiles/repo_name")"
402 # XXX: May be better to use full path
403 overlay="$(basename "${portdir}")"
406 if [[ ! -e "$portdir/metadata/cache/${1}-$version" ]]; then
407 # Repo does not have this particular package
410 iuse=$(head -11 "$portdir/metadata/cache/${1}-$version"|tail -1)
411 slot=$(head -3 "$portdir/metadata/cache/${1}-$version"|tail -1)
412 echo "${1};${version};${slot};${iuse};${overlay}"
415 if [[ -z ${ACTIVE_FLAGS[5]} ]]; then ACTIVE_FLAGS[5]="$append"
416 else ACTIVE_FLAGS[5]="${ACTIVE_FLAGS[5]}"$'\n'"$append"
421 # get all make.conf files that exist on the system
422 get_all_make_conf() {
423 # At least one of the files exists or we would not have made it this far
424 for x in ${ETC}/make.conf ${ETC}/portage/make.conf; do
425 [ -e "${x}" ] && echo "${x}"
428 # Function: traverse_profile {{{
429 # General method of collecting the contents of a profile
430 # component by traversing through the cascading profile
433 # $1 - Filename (make.profile)
434 # [$2] - Current directory (unspecified means to start at the top)
440 curdir="${2:-$(get_real_path ${MAKE_PROFILE_PATH})}"
442 [[ -f "${curdir}/${1}" ]] && rvalue="${curdir}/${1} ${rvalue}"
443 if [[ -f "${curdir}/parent" ]]; then
444 for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
445 # Bug 231394, handle parent path being absolute
446 if [[ ${parent:0:1} == "/" ]]; then
447 pdir="$(get_real_path ${parent})"
449 pdir="$(get_real_path ${curdir}/${parent})"
451 rvalue="$(traverse_profile ${1} ${pdir}) ${rvalue}"
458 # Function: get_all_make_defaults {{{
459 # Det all make.defaults by traversing the cascaded profile directories
460 get_all_make_defaults() {
461 if [[ -z $MAKE_DEFAULTS ]]; then
462 MAKE_DEFAULTS=$(traverse_profile "make.defaults")
466 MAKE_DEFAULTS=$(get_all_make_defaults)
468 # Function: get_flagstatus_helper # {{{
469 # Little helper function to get the status of a given flag in one of the
470 # ACTIVE_FLAGS elements.
473 # (Written to STDOUT) Flag active status (+/-) or default string given
474 # in argument 4 or an empty space
478 # 2 - index of ACTIVE_FLAGS
479 # 3 - echo value for positive (and as lowercase for negative) test result
480 # 4 - (optional) echo value for "missing" test result, defaults to blank
481 get_flagstatus_helper() {
482 if [[ -z ${flags} ]]; then
483 local flags=${ACTIVE_FLAGS[${2}]}
485 local flag=$(echo " $flags " | grep -Eo " [+-]?${1} ")
486 if [[ ${flag:1:1} == "-" ]]; then
487 echo -e -n "${3}" | tr [:upper:]+ [:lower:]-
488 elif [[ -n ${flag} ]]; then
495 # Function: get_flagstatus_helper_pkg # {{{
496 # Entry to get_flagstatus_helper for packages which will fetch use
497 # flags set in package.use for the package and pass them on to
498 # get_flagstatus_helper. Also correcly handles lines in package.use
499 # specified for individual package versions
500 get_flagstatus_helper_pkg() {
501 if [[ -z ${2} ]]; then
504 elif [[ -z ${flags} ]]; then
505 # If no atoms are matchers (start with >,<,=, then they all match
507 if [[ -z "${atoms[@]/[<>=]*/}" ]]; then
509 echo "${atoms[@]}" | python -c "
510 import portage.dep as dep, sys
511 print ' '.join(dep.match_to_list('$5-$6',sys.stdin.read().split()))"))
513 flags=$(for atom in ${atoms[@]}; do
514 [[ -z $atom ]] && continue
515 echo "${ACTIVE_FLAGS[4]}" | \
516 grep "^ *$atom" | cut -d\ -f2-
519 if [[ -z ${5} || -z ${flags} ]]; then
523 get_flagstatus_helper "$@"
526 # Function: get_flagstatus_helper_ebuild {{{
527 # get_flagstatus_helper replacement for packages to fetch ebuild USE flag
531 # (Written to STDOUT) Flag active status (+/-) or default string given
532 # in argument 4 or an empty space. If USE flag is not defined in the list
533 # of flags (2), an '!' is written
537 # 2 - IUSE line from ebuild file
538 # 3 - echo value for positive (and as lowercase for negative) test result
539 # 4 - (optional) echo value for "missing" test result, defaults to blank space
540 get_flagstatus_helper_ebuild() {
541 local flags=$(echo $2 | cut -d\" -f2)
542 local flag=$(echo " $flags " | grep -Eo " [+-]?$1 ")
543 if [[ ${flag:1:1} == "+" ]]; then
545 elif [[ ${flag:1:1} == "-" ]]; then
546 echo -en "${3}" | tr [:upper:]+ [:lower:]-
547 elif [[ -z $flag ]]; then
554 # Function: get_flagstatus {{{
555 # Prints a status string for the given flag, each column indicating the presence
556 # for portage, in the environment, in make.conf, in make.defaults, in
557 # make.globals, and in use.force and flipped in use.mask.
560 # 1 - use flag for which to retrieve status
563 # 0 (True) if flag is active, 1 (False) if not active
566 # Full positive value would be "[+ECDGFm] ", full negative value would be [-ecdgfM],
567 # full missing value would be "[- ] " (portage only sees present or not present)
571 local E=$(get_flagstatus_helper "${1}" 0 "E")
572 local C=$(get_flagstatus_helper "${1}" 1 "C")
573 local D=$(get_flagstatus_helper "${1}" 2 "D")
574 local G=$(get_flagstatus_helper "${1}" 3 "G")
575 local M=$(get_flagstatus_helper "${1}" 6 "M")
576 local F=$(get_flagstatus_helper "${1}" 7 "F")
577 # Use flags are disabled by default
580 # Use flag precedence is defined (at least) at:
581 # http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=2
582 for location in "E" "C" "D" "G" "F" "M"; do
583 if [[ ${!location} == $location ]]; then
586 elif [[ ${!location} != " " ]]; then
591 echo -n "[${ACTIVE:--}$E$C$D$G$F$M] "
592 [[ $ACTIVE == "+" ]]; return $?
595 # Function: get_flagstatus_pkg {{{
596 # Outputs local flag status information for a specific package and perhaps
597 # specific package version.
601 # 2 - package atom (www-client/elinks)
602 # 3 - +/- whether flag is enabled by global configuration files
603 # 4 - (Optional) version of package to evaluate (empty means all versions)
606 # Flag status for package.use and ebuild, slot and version, and overlay
607 # the version lives is if not PORTDIR
609 # Full positive would be "[+PB]", full negative would be "[-pb], and full
610 # missing would be "[? ]", question because the sign will default to the
611 # sign of the global status of the flag
612 get_flagstatus_pkg() {
614 # Pre-cache the use flags declared in ebuilds first.
615 # This is required as calling it inside a $() seems to
616 # prevent caching of results into $ACTIVE_FLAGS array
617 get_useflaglist_ebuild ${2}
619 # Get list of ebuilds available for this package. The
620 # get_useflaglist_ebuild() function stores them in $ACTIVE_FLAGS[5]
621 # with the package name leading the lines. The other information
622 # is in the same line, semi-colon (;) separated. The fields are
623 # package;version;SLOT;IUSE;overlay
625 # Fetch package atoms and flags from package.use only once
626 local atoms=$(echo "${ACTIVE_FLAGS[4]}" | \
627 grep -Eo "^ *[<>]?=?${2}(-[0-9rp._*-]*)?")
628 local IFS=$'\n'; for pkgline in $(echo "${ACTIVE_FLAGS[5]}" | grep "^$2" \
629 | sort -t \; -k2,2 -V); do
630 OIFS=$IFS; IFS=";"; INFO=($pkgline); IFS=$OIFS;
631 local version=${INFO[1]}
632 [[ -n $4 && $4 != $version ]] && continue
633 local slot=${INFO[2]}
634 local iuse=${INFO[3]}
635 if [[ $slot == '0' || $slot == "" ]]; then
638 slot="($(echo ${slot} | cut -d\" -f2)) "
640 local overlay=${INFO[4]}
641 [[ -n $overlay ]] && overlay="[$overlay]"
643 # Fetch enabled status for this version
644 local P=$(get_flagstatus_helper_pkg "${1}" "${atoms}" "P" "" "${2}" "${version}")
645 local B=$(get_flagstatus_helper_ebuild "${1}" "${iuse}" "B" "" "${2}" "${version}")
648 for location in "P" "B"; do
649 if [[ ${!location} == $location ]]; then
651 elif [[ ${!location} == "!" ]]; then
654 elif [[ ${!location} != " " ]]; then
659 if [[ $UNUSED == 1 ]]; then
660 echo " ${slot}${version} ${overlay}"
662 echo " [${ACTIVE:-${3:- }}$P$B] ${slot}${version} ${overlay}"
668 # Function: get_portdir {{{
669 # faster replacement to `portageq portdir`
672 # Location of portage tree root
674 if [ -z "${PORTDIR}" ]; then
676 source "${MAKE_GLOBALS_PATH}"
677 for x in $(get_all_make_defaults); do
680 for x in $(get_all_make_conf); do
687 # This won't change while the script is running, so cache it
688 PORTDIR="$(get_portdir)"
690 # Function: get_all_overlays {{{
691 # Outputs list of portage overlays as defined in the PORTDIR_OVERLAY
692 # variable defined in make.conf
695 source "${MAKE_CONF_PATH}"
697 echo ${PORTDIR_OVERLAY}
699 ALL_PORTDIRS=( "$PORTDIR" $(get_all_overlays) )
701 # Function: array_contains {{{
702 # PHP-style array_contains function.
709 # 0 (True) if needle in haystack, null (False) otherwise
711 for i in $1; do [[ $i == $2 ]] && return 0; done
715 # Function: showdesc {{{
716 # This function takes a list of use flags and shows the status and
717 # the description for each one, honoring $SCOPE
720 # * - USE flags for which to display descriptions. Undefined means to
721 # display descriptions for all known USE flags
724 # SCOPE - defines whether to output local or global USE flag descriptions
725 # Empty means to display both
728 # (STDOUT) Flag description(s) for given USE flags
739 if [ -z "${SCOPE}" ]; then
740 SCOPE="global" showdesc ${args}
742 SCOPE="local" showdesc ${args}
746 local useflags=( $(echo "$(get_useflaglist)") )
748 [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
749 [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
750 echo "************************************************************"
752 if [ "${args}" == "*" ]; then
753 args="${useflags[*]}"
759 while [[ -n "${1}" ]]; do
760 if [[ "${SCOPE}" == "global" ]]; then
761 if array_contains "${useflags[*]}" "$1"; then
762 get_flagstatus "${1}"
763 # XXX: Handle overlay
764 grep "^${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.desc} 2> /dev/null \
765 | sed -re "s/^([^:]+)://"
769 # local flags are a bit more complicated as there can be multiple
770 # entries per flag and we can't pipe into printf
771 if [[ "${SCOPE}" == "local" ]]; then
772 if array_contains "${useflags[*]}" "$1"; then
775 # Fetch all the packages data using this flag
776 infos=$( grep ":${1} *-" ${ALL_PORTDIRS[@]/%//profiles/use.local.desc} 2> /dev/null \
777 | sed -re "s/^([^:]+):([^:]+):(${1}) *- *(.+)/\1|\2|\3|\4/g")
778 OIFS=$IFS; IFS=$'\n'; infos=($infos); IFS=$OIFS;
779 for line in "${infos[@]}"; do
780 OIFS=$IFS; IFS="|"; line=($line); IFS=$OIFS
784 if get_flagstatus "${flag}"; then
789 printf "%s\n" "${flag}"
790 printf "%s: %s\n" "${pkg}" "${desc}" \
791 | fold --width=$((${COLUMNS:-80}-10)) -s | sed -e "s/^/ /g"
792 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}"
798 if [[ ${foundone} == 0 ]]; then
799 echo "no matching entries found"
803 # Function: showinstdesc {{{
804 # Works like showdesc() but displays only descriptions of which the appropriate
805 # ebuild is installed and prints the name of those packages.
808 # * - USE flags for which to display descriptions. Undefined means to
809 # display descriptions for all known USE flags
812 # SCOPE - defines whether to output local or global USE flag descriptions
813 # Empty means to display both
816 # (STDOUT) Flag description(s) for given USE flags along with installed
829 "global") echo "global use flags (searching: ${args[@]})";;
830 "local") echo "local use flags (searching: ${args[@]})";;
831 *) SCOPE="global" showinstdesc "${args[@]}"
833 SCOPE="local" showinstdesc "${args[@]}"
837 descdir="$(get_portdir)/profiles"
838 echo "************************************************************"
840 if [ "${args}" = "*" ]; then
841 args="$(get_useflaglist | sort -u)"
846 while [ -n "${1}" ]; do
849 if desc=$(grep "^${1} *-" "${descdir}/use.desc"); then
850 get_flagstatus "${1}"
852 # get list of installed packages matching this USE flag.
854 packages=($(equery -q -C hasuse -i "${1}" | awk '{ print $(NF-1) }' | sort))
855 foundone+=${#packages[@]}
856 printf "\nInstalled packages matching this USE flag: "
857 if [ ${foundone} -gt 0 ]; then
858 echo $'\n'"${packages[*]}"
865 # local flags are a bit more complicated as there can be multiple
866 # entries per flag and we can't pipe into printf
867 IFS=': ' # Use a space instead of a dash because dashes occur in cat/pkg
868 while read pkg flag desc; do
869 # print name only if package is installed
870 # NOTE: If we implement bug #114086 's enhancement we can just use the
871 # exit status of equery instead of a subshell and pipe to wc -l
872 # Bug 274472, -e is the default
873 if [ $(equery -q -C list -i "${pkg}" | wc -l) -gt 0 ]; then
876 get_flagstatus "${flag}" "${pkg}"
878 printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc#- }"
880 done < <(grep ":${1} *-" "${descdir}/use.local.desc")
886 if [ ${foundone} -lt 1 ]; then
887 echo "no matching entries found"
892 # Function: showflags {{{
893 # show a list of all currently active flags and where they are activated
901 if [ "${args}" == "*" ]; then
902 args="$(get_useflaglist | sort -u)"
908 while [ -n "${1}" ]; do
909 if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
914 if echo " ${ACTIVE_FLAGS[4]} " | egrep -e " -?${1} " > /dev/null; then
915 for pkg in $( echo "${ACTIVE_FLAGS[4]}" | \
916 egrep " -?${1} " | cut -d " " -f 2); do
918 SCOPE="local" get_flagstatus ${1} "${pkg}"
919 printf "(%s)\n" ${pkg}
926 # two small helpers to add or remove a flag from a USE string
928 if [[ -n $(grep " -${1//-/} " <<< " ${ACTIVE_FLAGS[6]} ") ]]; then
929 error "Use flag \"${1//-/}\" is masked and should not be added" \
932 # Bug #104396 -- Only add use flags defined in use.desc and use.local.desc
933 elif [[ -z $(grep "^${1//-/}$" <<< "$(get_useflaglist)") ]]; then
934 error "Use flag \"${1//-/}\" is not defined in use.desc and should" \
935 "not be added\nto make.conf."
938 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
943 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
946 # Function: clean_package_use {{{
947 # Simple utility to remove empty files from package.use
948 clean_package_use() {
949 if [[ -d ${PACKAGE_USE_PATH} ]]; then
950 for f in $(find ${PACKAGE_USE_PATH} -size 0); do
951 echo "Removing empty file ""${f}"""
957 # Function: scrub_use_flag {{{
958 # Utility to remove a use flag from a file in package.use[/]
965 # PACKAGE - Package atom for which to remove flag
967 local atom_re="^[<>]?=?([a-z][\da-z/-]+[a-z])(-[0-9pr._*-]+)?"
969 # Ignore leading - on flag
971 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
972 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
975 # Skip (preserve) comments on their own lines
976 if [[ -z $(echo "${line}" | sed -re "s/^ *#.*$//") ]]; then
978 # Detect if requested package is defined on this line
979 elif [[ -n "${PACKAGE}" ]]; then
980 if [[ -n $(echo "${line}" | grep -Ee "${pkg_re}") ]]; then
981 # If this is the only (remaining) use flag defined
982 # for this package, then remove the whole line
983 if [[ -z $(echo "${line}" | \
984 grep -Ee "${pkg_re} *-?${flag} *$") ]]; then
985 # Remove flag from this line
986 echo "${line}" | sed -re "s/ *-?\b${flag}\b//"
992 # If line only has this use flag, let it be removed
993 # (used if PACKAGE is not defined -- from pruning)
994 elif [[ -n $(echo "${line}" | \
995 egrep "^[^#]*${atom_re}.*-?${flag}") ]]; then
996 echo "Removing use flag from ${line}" >&2
997 if [[ -z $(echo "${line}" | \
998 grep -Ee "${atom_re} *-?${flag} *$") ]]; then
999 # Remove flag from this line
1000 echo "${line}" | sed -re "s/-?\b${flag}\b//"
1006 done > "${filename}.new" < "${filename}"
1007 mv "${filename}.new" "${filename}"
1010 # Function: modify_package {{{
1011 # Adds and removes USE flags from individual packages by modifying
1012 # files in package.use. It supports package.use both as a file and
1013 # and as a folder. Also handles "enabling" as removing a disabled flag from
1014 # a file, and "disabling" a globally enabled flag by adding a negative to
1015 # a file. Also warns about unused and unknown flags, and about flags
1016 # already enabled, disabled, or masked.
1019 # * - USE flag(s) to add or remove
1022 # PACKAGE - Package for which to add (-E) or remove (-D) the USE
1027 local atom_re="^[<>]?=?([a-z][\da-z/-]+[a-z])(-[0-9pr._*-]+)?"
1028 local pkg=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\1/")
1029 local V=$(echo "${PACKAGE}" | sed -re "s/${atom_re}/\2/")
1030 local pkg_re="[<>]?=?${pkg}(-[\dpr._*-]+)?"
1032 local all_flags=$(SCOPE= get_useflaglist)
1033 # Shift at top rather than bottom to support 'continue'
1035 while [[ -n ${2} ]]; do
1040 # Fetch flag ACTIVE status (+,-,null)
1041 get_flagstatus "${flag}" "${pkg}" > /dev/null
1042 GLOBAL_ACTIVE="$ACTIVE"
1043 # XXX: If V is not given, is this necessary? Should it use the version
1044 # that would be installed by emerge?
1045 get_flagstatus_pkg "${flag}" "${pkg}" "${ACTIVE}" "${V}" > /dev/null
1048 # (1) make sure ${pkg} exists in portdir
1049 if [[ ! -d "$(get_portdir)/${pkg}" ]]; then
1050 fatal "Package \"${pkg}\" does not exist"
1052 # (2) make sure ${flag} is defined in get_useflaglist
1053 elif ! array_contains "$all_flags" ${flag}; then
1054 error "USE flag \"${flag}\" does not exist"
1056 # Don't bail just because of this, just warn
1057 # (3) make sure use flag is valid for the package
1058 elif [[ -z $(echo "${ACTIVE_FLAGS[5]} " | grep -Ee "^${pkg_re}" \
1059 | grep -Ee "[; ][+-]?${flag}") ]]; then
1060 # XXX: Handle version or version wildcard?
1061 warn "USE flag \"${flag}\" is not used by $PACKAGE"
1062 # Don't necessarily bail for this, just warn
1063 elif [[ -n "${V}" && -z "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1064 error "Invalid package atom. Did you forget the leading '='?"
1066 elif [[ -z "${V}" && -n "$(egrep "<|>|=" <<< "${PACKAGE:0:1}")" ]]; then
1067 error "Invalid package atom. Did you forget the version?"
1070 # If flag is enabled in portage USE flags (emerge --info),
1071 # then "remove"ing the flag should be replaced with adding
1072 # the negative flag instead
1073 if [[ "${ACTION}" == "remove" ]]; then
1074 if [[ "${ACTIVE:-${GLOBAL_ACTIVE}}" == "+" ]]; then
1075 if [[ -n $(echo "${ACTIVE_FLAGS[4]}" | grep "^$PACKAGE" \
1076 | grep " $flag") ]]; then
1077 iuse=$(echo "${ACTIVE_FLAGS[5]} " | grep -Ee "^${pkg_re}" \
1078 | cut -d ";" -f4 | egrep -o "[+-]?${flag}")
1079 # Ensure the flag is enabled in the ebuild _and_ in package.use,
1080 # if so, enable it in package.use
1081 if [[ "${iuse}" =~ "+" ]]; then
1082 # The flag is currently enabled in the ebuild, so add a
1088 # Not enabled in package.use, so disable it there
1093 error "USE flag \"$flag\" is already disabled for $PACKAGE"
1096 elif [[ "${ACTION}" == "prune" ]]; then
1097 # Just remove the flag below
1098 [[ "${ACTIVE}" == "-" ]] && flag="-${flag}"
1100 # If flag is currently disabled for the package requested
1101 # to be enabled in, then "remove" the negative
1102 elif [[ "${ACTION}" == "add" ]]; then
1103 if [[ "${ACTIVE}" == "-" ]]; then
1104 # If flag is masked, it should be added to package.mask, instead
1105 # of package.use. For now, yield a warning and quit
1106 if [[ -n $(echo " ${ACTIVE_FLAGS[6]}" | grep " -${flag} ") ]]; then
1107 error "USE flag \"$flag\" is masked. Enabling in package.use will" \
1108 "\nbe ineffective. You may have an incorrect profile selected."
1110 elif [[ -n $(echo "${ACTIVE_FLAGS[4]}" | grep "^$PACKAGE" \
1111 | grep " -$flag") ]]; then
1112 iuse=$(echo "${ACTIVE_FLAGS[5]} " | grep -Ee "^${pkg_re}" \
1113 | cut -d ";" -f4 | egrep -o "[+-]?${flag}")
1114 # Ensure the flag is disabled in the ebuild _and_ in package.use,
1115 # if so, enable it in package.use
1116 if [[ "${iuse}" =~ "+" ]]; then
1117 # The flag is currently disabled by package.use only, so remove the
1123 elif [[ "${ACTIVE:-${GLOBAL_ACTIVE:--}}" == "+" ]]; then
1124 # XXX: Perhaps look at indicating where it is enabled
1125 error "USE flag \"$flag\" is already enabled for $PACKAGE"
1132 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1133 # Use naming convention of package.use/package
1134 filename="${PACKAGE_USE_PATH}/${pkg#*/}"
1135 if [[ ! -s "${filename}" ]]; then
1136 # Create new file to contain flag
1137 echo "${PACKAGE} ${flag}" > "${filename}"
1138 echo "Adding \"${PACKAGE}[${flag}]\" use flag to new file ""${filename}"""
1142 # Add to package.use file instead
1143 filename="${PACKAGE_USE_PATH}"
1144 # Create as necessary
1147 # Walk through the file and add the flag manually
1148 echo "Adding \"${PACKAGE}[${flag}]\" use flag in \"${filename}\""
1151 if [[ -n $(echo "${line}" | egrep -re "^[^#]*${PACKAGE} ") ]]; then
1152 echo $(reduce_package_use "${line} ${flag}")
1158 done < "${filename}" > "${filename}.new"
1159 mv "${filename}.new" "${filename}"
1160 if [[ ${added} -eq 0 ]]; then
1161 echo "${PACKAGE} ${flag}" >> "${filename}"
1166 if [[ -d ${PACKAGE_USE_PATH} ]]; then
1167 # Scan for file containing named package and use flag
1168 filename=$(egrep -rle "${pkg_re}.*[^-]${flag}( |$)" "${PACKAGE_USE_PATH}")
1169 if [[ -z "${filename}" ]]; then
1170 error ""${flag}" is not defined for package "${PACKAGE}""
1174 # Remove from package.use instead
1175 filename=${PACKAGE_USE_PATH}
1176 # Create as necessary
1179 # Scrub use flag from matched files
1180 for f in ${filename}; do
1181 # Remove current flags in file
1182 echo "Removing \"${PACKAGE}[${flag}]\" use flag in \"${f}\""
1183 scrub_use_flag ${f} ${flag}
1185 # Remove empty files
1192 # Function: modify {{{
1193 # USE flag modification function. Mainly a loop with calls to add_flag and
1194 # remove_flag to create a new USE string which is then inserted into make.conf.
1196 if [[ -n "${PACKAGE}" ]]; then
1197 modify_package "${*}"
1201 if [ -z "${*}" ]; then
1202 if [ "${ACTION}" != "prune" ]; then
1203 echo "WARNING: no USE flags listed for modification, do you really"
1204 echo " want to ${ACTION} *all* known USE flags?"
1205 echo " If you don't please press Ctrl-C NOW!!!"
1207 set $(get_useflaglist | sort -u)
1213 NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
1215 while [ -n "${1}" ]; do
1216 if [ "${ACTION}" == "add" ]; then
1217 if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
1219 elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
1222 add_flag "${1}" || exit
1225 elif [ "${ACTION}" == "remove" ]; then
1226 if echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
1228 elif echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
1231 add_flag "-${1}" || exit
1234 elif [ "${ACTION}" == "prune" ]; then
1235 if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
1237 elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
1245 #echo ${ACTIVE_FLAGS[1]}
1248 #echo ${NEW_MAKE_CONF_USE}
1250 # a little loop to add linebreaks so we don't end with one ultra-long line
1251 NEW_MAKE_CONF_USE_2=""
1252 for x in ${NEW_MAKE_CONF_USE}; do
1253 if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
1254 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n $x "
1256 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
1260 # make a backup just in case the user doesn't like the new make.conf
1261 cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
1263 # as sed doesn't really work with multi-line patterns we have to replace USE
1264 # on our own here. Basically just skip everything between USE=" and the
1265 # closing ", printing our new USE line there instead.
1269 (while [ "$x" -eq "0" ]; do
1270 read -r line || break
1272 # Bug 275362 - Handle the case where make.conf includes:
1276 # Consume USE=" when detected so the quote won't be detected
1277 # as the ending quote
1278 if [ "${line:0:4}" == "USE=" ]; then inuse=1; line=${line:5}; fi
1279 [ "${inuse}" == "0" ] && echo -E "${line}"
1280 if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
1282 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1288 if [ ${had_use} -eq 0 ]; then
1290 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
1292 fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
1294 echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
1297 ##### main program comes now #####
1299 # disable globbing as it fucks up with args=*
1301 parse_arguments "$@"
1305 eval ${MODE} ${ARGUMENTS}
1307 # vim: set tabstop=4 shiftwidth=4: