5 # bash replacement for the original euse by Arun Bhanu
6 # Author: Marius Mauch <genone@gentoo.org>
7 # Licensed under the GPL v2
12 EPREFIX=${EPREFIX:-$(portageq envvar EPREFIX)}
14 USR_SHARE_PORTAGE="${EPREFIX}/usr/share/portage"
16 # define error function so it can be used immediately
23 # /etc/make.conf can now exist in /etc/portage/make.conf, prefer it over /etc/make.conf for changes
24 if [ -e "${ETC}/portage/make.conf" ]; then
25 MAKE_CONF_PATH="${ETC}/portage/make.conf"
26 elif [ -e "${ETC}/make.conf" ]; then
27 MAKE_CONF_PATH="${ETC}/make.conf"
29 error "make.conf does not exist"
31 MAKE_CONF_BACKUP_PATH="${MAKE_CONF_PATH}.euse_backup"
33 # /etc/make.globals has been moved to /usr/share/portage/config/make.globals
34 if [ -e "${USR_SHARE_PORTAGE}/config/make.globals" ]; then
35 MAKE_GLOBALS_PATH="${USR_SHARE_PORTAGE}/config/make.globals"
37 MAKE_GLOBALS_PATH="${ETC}/make.globals"
40 # /etc/make.profile or /etc/portage/make.profile, if /etc/make.profile exists, it will be used
41 if [ -e "${ETC}/make.profile" ]; then
42 MAKE_PROFILE_PATH="${ETC}/make.profile"
43 elif [ -e "${ETC}/portage/make.profile" ]; then
44 MAKE_PROFILE_PATH="${ETC}/portage/make.profile"
46 error "make.profile does not exist"
49 [ -z "${MODE}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
52 if [ -z "${1}" ]; then
55 while [ -n "${1}" ]; do
57 -h | --help) MODE="showhelp";;
58 -V | -v | --version) MODE="showversion";;
59 -i | --info) MODE="showdesc";;
60 -I | --info-installed) MODE="showinstdesc";;
61 -l | --local) SCOPE="local";;
62 -g | --global) SCOPE="global";;
63 -a | --active) MODE="showflags";;
64 -E | --enable) MODE="modify"; ACTION="add";;
65 -D | --disable) MODE="modify"; ACTION="remove";;
66 -P | --prune) MODE="modify"; ACTION="prune";;
68 echo "ERROR: unknown option ${1} specified."
74 ARGUMENTS="${ARGUMENTS} ${ACTIVE_FLAGS[9]}"
77 ARGUMENTS="${ARGUMENTS} ${1}"
93 # file permission tests
98 [[ ! -d "${MAKE_PROFILE_PATH}" || ! -r "${MAKE_PROFILE_PATH}" ]] && error "${MAKE_PROFILE_PATH} is not readable"
100 for make_conf in $(get_all_make_conf); do
101 [ ! -r "${make_conf}" ] && error "${make_conf} is not readable"
104 descdir="$(get_portdir)/profiles"
106 [ ! -r "${MAKE_GLOBALS_PATH}" ] && error "${MAKE_GLOBALS_PATH} is not readable"
107 [ -z "$(get_portdir)" ] && error "\$PORTDIR couldn't be determined"
108 [ ! -d "${descdir}" ] && error "${descdir} does not exist or is not a directory"
109 [ ! -r "${descdir}/use.desc" ] && error "${descdir}/use.desc is not readable"
110 [ ! -r "${descdir}/use.local.desc" ] && error "${descdir}/use.local.desc is not readable"
111 for make_defaults in $(get_all_make_defaults); do
112 [ ! -r "$make_defaults" ] && error "$_make_defaults is not readable"
114 # [ ! -r "$(get_make_defaults)" ] && error "$(get_make_defaults) is not readable"
115 [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && error ""${MAKE_CONF_PATH}" is not writable"
120 ${PROGRAM_NAME} (${VERSION})
122 Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]
124 Options: -h, --help - show this message
125 -V, --version - show version information
126 -i, --info - show descriptions for the given useflags
127 -I, --info-installed - show descriptions for the given useflags and
128 their current impact on the installed system
129 -g, --global - show only global use flags (suboption)
130 -l, --local - show only local use flags (suboption)
131 -a, --active - show currently active useflags and their origin
132 -E, --enable - enable the given useflags
133 -D, --disable - disable the given useflags
134 -P, --prune - remove all references to the given flags from
135 make.conf to revert to default settings
137 Notes: ${PROGRAM_NAME} currently only works for global flags defined
138 in make.globals, make.defaults or make.conf, it doesn't handle
139 use.defaults, use.mask or package.use yet (see portage(5) for details on
140 these files). It also might have issues with cascaded profiles.
141 If multiple options are specified only the last one will be used.
147 ${PROGRAM_NAME} (${VERSION})
148 Written by Marius Mauch
150 Copyright (C) 2004-2009 Gentoo Foundation, Inc.
151 This is free software; see the source for copying conditions.
155 # remove duplicate flags from the given list in both positive and negative forms
156 # (but unlike portage always keep the last value even if it's negative)
157 # Otherwise the status flags could be incorrect if a flag appers multiple times in
158 # one location (like make.conf).
159 # Using python here as bash sucks for list handling.
160 # NOTE: bash isn't actually that bad at handling lists -- sh is. This may be
161 # worth another look to avoid calling python unnecessariy. Or we could
162 # just write the whole thing in python. ;)
163 reduce_incrementals() {
164 echo $@ | python -c "import sys
166 for x in sys.stdin.read().split():
167 if x[0] == '-' and x[1:] in r:
170 elif x[0] != '-' and '-'+x in r:
181 # the following function creates a bash array ACTIVE_FLAGS that contains the
182 # global use flags, indexed by origin: 0: environment, 1: make.conf,
183 # 2: make.defaults, 3: make.globals
185 # only calculate once as calling emerge is painfully slow
186 [ -n "${USE_FLAGS_CALCULATED}" ] && return
188 # backup portdir so get_portdir() doesn't give false results later
189 portdir_backup="${PORTDIR}"
191 ACTIVE_FLAGS[0]="$(reduce_incrementals ${USE})"
193 for x in $(get_all_make_conf); do
195 ACTIVE_FLAGS[1]="$(reduce_incrementals ${ACTIVE_FLAGS[1]} ${USE})"
198 for x in $(get_all_make_defaults); do
200 ACTIVE_FLAGS[2]="$(reduce_incrementals ${ACTIVE_FLAGS[2]} ${USE})"
203 source "${MAKE_GLOBALS_PATH}"
204 ACTIVE_FLAGS[3]="$(reduce_incrementals ${USE})"
206 # restore saved env variables
207 USE="${ACTIVE_FLAGS[0]}"
208 PORTDIR="${portdir_backup}"
210 # get the currently active USE flags as seen by portage, this has to be after
211 # restoring USE or portage won't see the original environment
212 ACTIVE_FLAGS[9]="$(portageq envvar USE)" #'
213 USE_FLAGS_CALCULATED=1
216 # get the list of all known USE flags by reading use.desc and/or use.local.desc
217 # (depending on the value of $SCOPE)
221 descdir="$(get_portdir)/profiles"
223 if [ -z "${SCOPE}" -o "${SCOPE}" == "global" ]; then
224 egrep "^[^# ]+ +-" "${descdir}/use.desc" | cut -d\ -f 1
226 if [ -z "${SCOPE}" -o "${SCOPE}" == "local" ]; then
227 egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" | cut -d: -f 2 | cut -d\ -f 1
231 # get all make.conf files that exist on the system
232 get_all_make_conf() {
233 # At least one of the files exists or we would not have made it this far
234 for x in ${ETC}/make.conf ${ETC}/portage/make.conf; do
235 [ -e "${x}" ] && echo "${x}"
238 # get all make.defaults by traversing the cascaded profile directories
239 get_all_make_defaults() {
244 curdir="${1:-$(get_real_path ${MAKE_PROFILE_PATH})}"
246 [ -f "${curdir}/make.defaults" ] && rvalue="${curdir}/make.defaults ${rvalue}"
247 if [ -f "${curdir}/parent" ]; then
248 for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
249 pdir="$(get_real_path ${curdir}/${parent})"
250 rvalue="$(get_all_make_defaults ${pdir}) ${rvalue}"
257 # get the path to make.defaults by traversing the cascaded profile directories
258 get_make_defaults() {
262 curdir="${1:-$(get_real_path ${MAKE_PROFILE_PATH})}"
264 if [ ! -f "${curdir}/make.defaults" -a -f "${curdir}/parent" ]; then
265 for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
266 if [ -f "$(get_make_defaults ${curdir}/${parent})" ]; then
267 curdir="${curdir}/${parent}"
273 echo "${curdir}/make.defaults"
276 # little helper function to get the status of a given flag in one of the
277 # ACTIVE_FLAGS elements. Arguments are 1: flag to test, 2: index of ACTIVE_FLAGS,
278 # 3: echo value for positive (and as lowercase for negative) test result,
279 # 4 (optional): echo value for "missing" test result, defaults to blank
280 get_flagstatus_helper() {
281 if echo " ${ACTIVE_FLAGS[${2}]} " | grep " ${1} " > /dev/null; then
283 elif echo " ${ACTIVE_FLAGS[${2}]} " | grep " -${1} " > /dev/null; then
284 echo -n "$(echo ${3} | tr [[:upper:]] [[:lower:]])"
290 # prints a status string for the given flag, each column indicating the presence
291 # for portage, in the environment, in make.conf, in make.defaults and in make.globals.
292 # full positive value would be "[+ECDG]", full negative value would be [-ecdg],
293 # full missing value would be "[- ]" (portage only sees present or not present)
298 get_flagstatus_helper "${1}" 9 "+" "-"
299 get_flagstatus_helper "${1}" 0 "E"
300 get_flagstatus_helper "${1}" 1 "C"
301 get_flagstatus_helper "${1}" 2 "D"
302 get_flagstatus_helper "${1}" 3 "G"
306 # faster replacement to `portageq portdir`
308 if [ -z "${PORTDIR}" ]; then
310 source "${MAKE_GLOBALS_PATH}"
311 for x in $(get_all_make_defaults); do
314 for x in $(get_all_make_conf); do
322 # This function takes a list of use flags and shows the status and
323 # the description for each one, honoring $SCOPE
332 if [ -z "${SCOPE}" ]; then
333 SCOPE="global" showdesc ${args}
335 SCOPE="local" showdesc ${args}
339 descdir="$(get_portdir)/profiles"
341 [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
342 [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
343 echo "************************************************************"
345 if [ "${args}" == "*" ]; then
346 args="$(get_useflaglist | sort -u)"
352 while [ -n "${1}" ]; do
353 if [ "${SCOPE}" == "global" ]; then
354 if grep "^${1} *-" "${descdir}/use.desc" > /dev/null; then
355 get_flagstatus "${1}"
358 grep "^${1} *-" "${descdir}/use.desc"
360 # local flags are a bit more complicated as there can be multiple
361 # entries per flag and we can't pipe into printf
362 if [ "${SCOPE}" == "local" ]; then
363 if grep ":${1} *-" "${descdir}/use.local.desc" > /dev/null; then
366 grep ":${1} *-" "${descdir}/use.local.desc" \
367 | sed -e "s/^\([^:]\+\):\(${1}\) *- *\(.\+\)/\1|\2|\3/g" \
368 | while read line; do
369 pkg="$(echo $line | cut -d\| -f 1)"
370 flag="$(echo $line | cut -d\| -f 2)"
371 desc="$(echo $line | cut -d\| -f 3)"
372 get_flagstatus "${flag}"
373 printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc}"
379 if [ ${foundone} == 0 ]; then
380 echo "no matching entries found"
384 # Works like showdesc() but displays only descriptions of which the appropriate
385 # ebuild is installed and prints the name of those packages.
396 "global") echo "global use flags (searching: ${args})";;
397 "local") echo "local use flags (searching: ${args})";;
398 *) SCOPE="global" showinstdesc "${args[@]}"
400 SCOPE="local" showinstdesc "${args[@]}"
404 descdir="$(get_portdir)/profiles"
405 echo "************************************************************"
407 if [ "${args}" = "*" ]; then
408 args="$(get_useflaglist | sort -u)"
413 while [ -n "${1}" ]; do
416 if desc=$(grep "^${1} *-" "${descdir}/use.desc"); then
417 get_flagstatus "${1}"
419 # get list of installed packages matching this USE flag.
421 packages=($(equery -q -C hasuse -i "${1}" | awk '{ print $(NF-1) }' | sort))
422 foundone+=${#packages[@]}
423 printf "\nInstalled packages matching this USE flag: "
424 if [ ${foundone} -gt 0 ]; then
425 echo $'\n'"${packages[*]}"
432 # local flags are a bit more complicated as there can be multiple
433 # entries per flag and we can't pipe into printf
434 IFS=': ' # Use a space instead of a dash because dashes occur in cat/pkg
435 while read pkg flag desc; do
436 # print name only if package is installed
437 # NOTE: If we implement bug #114086 's enhancement we can just use the
438 # exit status of equery instead of a subshell and pipe to wc -l
439 if [ $(equery -q -C list -i -e "${pkg}" | wc -l) -gt 0 ]; then
442 get_flagstatus "${flag}"
444 printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc#- }"
446 done < <(grep ":${1} *-" "${descdir}/use.local.desc")
452 if [ ${foundone} -lt 1 ]; then
453 echo "no matching entries found"
458 # show a list of all currently active flags and where they are activated
466 if [ "${args}" == "*" ]; then
467 args="$(get_useflaglist | sort -u)"
472 while [ -n "${1}" ]; do
473 if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
482 # two small helpers to add or remove a flag from a USE string
484 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
488 NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
491 # USE flag modification function. Mainly a loop with calls to add_flag and
492 # remove_flag to create a new USE string which is then inserted into make.conf.
494 if [ -z "${*}" ]; then
495 if [ "${ACTION}" != "prune" ]; then
496 echo "WARNING: no USE flags listed for modification, do you really"
497 echo " want to ${ACTION} *all* known USE flags?"
498 echo " If you don't please press Ctrl-C NOW!!!"
500 set $(get_useflaglist | sort -u)
506 NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
508 while [ -n "${1}" ]; do
509 if [ "${ACTION}" == "add" ]; then
510 if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
512 elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
518 elif [ "${ACTION}" == "remove" ]; then
519 if echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
521 elif echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
527 elif [ "${ACTION}" == "prune" ]; then
528 if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
530 elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
538 #echo ${ACTIVE_FLAGS[1]}
541 #echo ${NEW_MAKE_CONF_USE}
543 # a little loop to add linebreaks so we don't end with one ultra-long line
544 NEW_MAKE_CONF_USE_2=""
545 for x in ${NEW_MAKE_CONF_USE}; do
546 if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
547 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n $x "
549 NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
553 # make a backup just in case the user doesn't like the new make.conf
554 cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
556 # as sed doesn't really work with multi-line patterns we have to replace USE
557 # on our own here. Basically just skip everything between USE=" and the
558 # closing ", printing our new USE line there instead.
562 (while [ "$x" -eq "0" ]; do
565 [[ "${x}" -ne "0" ]] && break
566 [ "${line:0:4}" == "USE=" ] && inuse=1
567 [ "${inuse}" == "0" ] && echo -E "${line}"
568 if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
570 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
576 if [ ${had_use} -eq 0 ]; then
578 echo -ne "${NEW_MAKE_CONF_USE_2%% }"
580 fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
582 echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
585 ##### main program comes now #####
587 # disable globbing as it fucks up with args=*
592 eval ${MODE} ${ARGUMENTS}