#!/bin/bash # useflag v0.3.0 # Script to help users manage USE flags in Gentoo Linux # # Distributed under the terms of the GNU General Public License, v2 or later # Author: Michael Thompson , (c) 2002 run_name=`basename $0` use_desc="/usr/portage/profiles/use.desc" make_conf_dir="/etc" make_conf="${make_conf_dir}/make.conf" # Home directory was chosen as the use of /tmp allows for symlink attacks make_temp="${HOME}/use.make.conf.tmp" use_cache_parent="/var/cache" use_cache_dir="${use_cache_parent}/use_desc" use_cache="${use_cache_dir}/use.cache" lock_cache="${use_cache_dir}/lock.cache" changes="0" # Get flag description # parm1 = Use flag to get description of do_get_desc() { local parm1=$1 # Strip the comments and find the flag. local out_get_desc=`grep -v "#" ${use_desc} | grep -w -e "${parm1} -"` if [ "${out_get_desc}" = "" ]; then local lcl_avail=`grep -v "#" ${use_desc} | cut -d ' ' -f1 | \ grep -w -e "${parm1}"` if [ "${lcl_avail}" != "" ]; then echo "${parm1} - No description available." else echo "!!! ${parm1} does not exist." fi else echo "${out_get_desc}" fi } # Get the contents of the USE variable # parm1 controls whether or not to include the '-' with each flag do_get_make() { local parm1=$1 # Get the USE flags from make.conf local get_make_out=`grep "USE=\"" ${make_conf} | grep -v "#" | \ cut -d "\"" -f2 -s` # If called with "nodashes", then strip the leading dashes if [ "${parm1}" = "nodashes" ]; then for tr_sucks in ${get_make_out}; do if [ "${tr_sucks:0:1}" = "-" ]; then local tr_out="${tr_out} ${tr_sucks:1}" else local tr_out="${tr_out} ${tr_sucks}" fi done get_make_out="${tr_out}" fi echo "${get_make_out}" } # Yes, it's pointless. But it could be used more than once in the future # so it's a function. # Gets the master list of available USE flags from use.desc do_get_avail() { grep -v "#" ${use_desc} | cut -d " " -f1 | tr '\n' ' ' } # Get depreciated flags. # parm1 = flag to check for depreciation # parm2 = list of available flags do_get_depr() { local parm1=$1 local parm2=${@:2} # This next var can't be local get_depr_tmp=`echo "${parm2}" | tr ' ' '\n' | grep -x -e "${parm1}"` local ret_code=$? if [ "${ret_code}" != "0" ]; then echo "${parm1}" | tr '\n' ' ' fi } # Removes a USE flag from make.conf # parm1 = flag to remove # use_rm_out = list of available flags do_use_rm() { local parm1=$1 local use_rm_out=${@:2} # Strip matching USE flags. Yes, this is ugly, I know. use_rm_out=`echo "${use_rm_out}" | tr ' ' '\n' | \ grep -x -v -e "${parm1}" | tr '\n' ' '` # Also strip the inverse. Even uglier... if [ "${parm1:0:1}" = "-" ]; then use_rm_out=`echo "${use_rm_out}" | tr ' ' '\n' | \ grep -x -v -e "${parm1:1}" | tr '\n' ' '` else use_rm_out=`echo "${use_rm_out}" | tr ' ' '\n' | \ grep -x -v -e "-${parm1}" | tr '\n' ' '` fi echo "${use_rm_out}" } # Adds a USE flag to make.conf # parm1 = flag to add # use_add_out = list of available flags do_use_add() { local parm1=$1 local use_add_out=${@:2} # First strip existing flags (matching or inverse), then add. # This is not the best way to do this. Better would be to replace a # flag if it already exists. That turned out to be a real PITA. # Maybe in a later version... use_add_out=`do_use_rm ${parm1} ${use_add_out}` use_add_out="${use_add_out} ${parm1}" echo "${use_add_out}" } # Adds a flag to the locked flag cache # Pass list of flags to lock as parameter. do_lock_flags() { local flag_list=$@ # Merge the new list of flags flags that are already locked. if [ -r ${lock_cache} ]; then local lock_old=`cat ${lock_cache}` fi flag_list="${lock_old} ${flag_list}" # Remove duplicates. echo "${flag_list}" | tr ' ' '\n' | sort | uniq | tr '\n' ' ' } # Writes the list of locked flags to the cache file # Pass list of flags to write as parameter. do_write_lock() { local write_flags=$@ if [ -r ${make_conf} ]; then local make_prune=`do_get_make nodashes` else do_report_err ${make_conf} read fi # Be sure and remove any locked flags that no longer exist in USE. for prune in ${write_flags}; do local prune_test=`do_get_depr ${prune} ${make_prune}` if [ "$prune_test" = "" ]; then local new_cache="${prune} ${new_cache}" fi done if [ -w ${use_cache_parent} ]; then mkdir -p ${use_cache_dir} chmod 700 ${use_cache_dir} echo "${new_cache}" > ${lock_cache} chmod 600 ${lock_cache} else do_report_err ${lock_cache} write fi } # Writes new USE variable to make.conf # Pass new list of USE flags as parameter. do_write_make() { local use_write="USE=\"$@\"" local old_use="USE=\"`do_get_make dashes`\"" if [ -w ${make_conf} ] && [ -w ${make_conf_dir} ]; then cat ${make_conf} | \ sed -e "s/${old_use}/${use_write}/" > ${make_temp} cp ${make_temp} ${make_conf} rm ${make_temp} else do_report_err ${make_conf} write fi } # Reports a read/write error and exits # parm1 = File to report on # parm2 = read, write, etc do_report_err() { local parm1=$1 local parm2=$2 if [ "${parm2}" = "read" ]; then echo "!!! Could not read ${parm1}" echo -n "!!! Verify that file exists and that you have " echo "appropriate permissions." elif [ "${parm2}" = "write" ]; then echo "!!! Could not write ${parm1}" echo "!!! Got root?" elif [ "${parm2}" = "nolock" ]; then echo "!!! Could not read ${parm1}" echo -n "!!! You have no locked flags or you have " echo "insufficient permissions." fi exit 1 } # The main section of the script # desc: # This is the feature for getting USE descriptions. if [ "$1" = "desc" ] || [ "$1" = "-i" ]; then if [ -r ${use_desc} ]; then for flag in ${@:2}; do do_get_desc ${flag} done else do_report_err ${use_desc} read fi # show: # This is the feature for showing the contents of the USE variable. elif [ "$1" = "show" ] || [ "$1" = "-s" ]; then if [ -r ${make_conf} ]; then do_get_make dashes else do_report_err ${make_conf} read fi # del: # This is the feature for removing a USE flag. elif [ "$1" = "del" ] || [ "$1" = "-d" ]; then if [ -r ${make_conf} ]; then make_use=`do_get_make dashes` else do_report_err ${make_conf} read fi for flag in ${@:2}; do # Strip leading dashes. if [ "${flag:0:1}" = "-" ]; then flag="${flag:1}" fi del_test1=`do_get_depr ${flag} ${make_use}` del_test2=`do_get_depr -${flag} ${make_use}` if [ "${del_test1}" = "" ] || [ "${del_test2}" = "" ]; then changes="1" make_use=`do_use_rm ${flag} ${make_use}` else echo "!!! ${flag} is not in your USE variable." fi done if [ "${changes}" != "0" ]; then do_write_make ${make_use} # Prune deleted USE flags from lock cache lock_flags=`do_lock_flags $2` do_write_lock ${lock_flags} fi # add: # This is the feature for explicitly enabling or disabling a USE flag. elif [ "$1" = "add" ] || [ "$1" = "-a" ]; then if [ -r ${make_conf} ]; then make_use=`do_get_make dashes` else do_report_err ${make_conf} read fi for flag in ${@:2}; do changes="1" make_use=`do_use_add ${flag} ${make_use}` done if [ "${changes}" != "0" ]; then do_write_make ${make_use} fi # lock: # This is the feature to lock a depreciated USE flag to prevent removal elif [ "$1" = "lock" ] || [ "$1" = "-l" ]; then if [ -r ${make_conf} ]; then make_use=`do_get_make nodashes` else do_report_err ${make_conf} read fi for flag in ${@:2}; do # Strip leading dashes. if [ "${flag:0:1}" = "-" ]; then flag="${flag:1}" fi lock_test=`do_get_depr ${flag} ${make_use}` if [ "${lock_test}" = "" ]; then lock_flags="${lock_flags} ${flag}" else echo "!!! ${flag} is not in your USE variable." fi done lock_out=`do_lock_flags ${lock_flags}` do_write_lock ${lock_out} # unlock: # This is the feature to unlock a depreciated USE flag to allow removal elif [ "$1" = "unlock" ] || [ "$1" = "-k" ]; then if [ -r ${lock_cache} ]; then lock_out=`cat ${lock_cache}` else do_report_err nolock fi for flag in ${@:2}; do # Strip leading dashes. if [ "${flag:0:1}" = "-" ]; then flag="${flag:1}" fi lock_flag_check=`do_get_depr ${flag} ${lock_out}` if [ "${lock_flag_check}" = "" ]; then lock_out=`do_use_rm ${flag} ${lock_out}` else echo "!!! ${flag} is not a locked flag." fi done do_write_lock ${lock_out} # showlock: # This feature prints a list of USE flags that have been locked elif [ "$1" = "showlock" ] || [ "$1" = "-w" ]; then if [ -r ${lock_cache} ]; then cat ${lock_cache} else do_report_err nolock fi # update: # This is the feature to update your USE by removing depreciated flags and # handling new flags. elif [ "$1" = "update" ] || [ "$1" = "-u" ]; then if [ -r ${make_conf} ]; then # Get our USE but strip leading dashes make_use=`do_get_make nodashes` else do_report_err ${make_conf} read fi # Get available USE flags from use.desc if [ -r ${use_desc} ]; then use_avail=`do_get_avail` else do_report_err ${use_desc} read fi # First we check for depreciated flags. echo "Your USE variable currently looks like this:" echo echo `do_get_make dashes` echo # Print the list of locked flags if any exist. if [ -r ${lock_cache} ]; then lock_test=`cat ${lock_cache} | tr -d ' '` if [ "${lock_test}" != "" ]; then echo "The following flags are locked:" cat ${lock_cache} echo fi fi echo echo "*** Checking for depreciated USE flags ..." echo for check_flag in ${make_use}; do depr_ret=`do_get_depr ${check_flag} ${use_avail}` flag_depr="${flag_depr}${depr_ret}" done # Filter out locked flags if [ -r ${lock_cache} ] && [ "${lock_test}" != "" ]; then lock_list=`cat ${lock_cache}` for check_locks in ${flag_depr}; do lock_ret=`do_get_depr ${check_locks} ${lock_list}` lock_out="${lock_out}${lock_ret}" done flag_depr="${lock_out}" fi make_use=`do_get_make dashes` if [ "${flag_depr}" = "" ]; then echo "!!! No depreciated flags were found." else echo "The following USE flags appear to be depreciated:" echo "${flag_depr}" echo echo "How would you like to handle them?" echo "1) Handle them individually" echo "2) Remove all depreciated flags" echo "3) Don't remove any depreciated flags" echo "4) Lock all depreciated flags" echo echo -n "Type (1, 2, 3, or 4): " while [ "${luser_input}" = "" ]; do read luser_input case ${luser_input} in "2") changes="1" for flag in ${flag_depr}; do make_use=`do_use_rm \ ${flag} ${make_use}` done echo echo -n "*** All depreciated flags were " echo "removed." ;; "3") echo echo "*** No flags were removed." ;; "1") for flag in ${flag_depr}; do echo -n "${flag} appears to be " echo -n "depreciated. Remove it? " echo -n "[Y]es/[N]o/[L]ock : " luser_yn="" while [ "${luser_yn}" = "" ]; do read luser_yn case ${luser_yn} in "y" | "Y") changes="1" make_use=`do_use_rm \ ${flag} \ ${make_use}` ;; "n" | "N") ;; "l" | "L") wlk="${flag} ${wlk}" ;; *) luser_yn="" ;; esac done done echo echo -n "*** All depreciated flags " echo "processed." ;; "4") wlk="${flag_depr}" echo echo "*** All depreciated flags were locked." ;; *) luser_input="" ;; esac done fi if [ "${wlk}" != "" ]; then do_write_lock ${wlk} fi # Now we check for new flags. echo echo echo "*** Checking for new USE flags ..." echo # Load up our cached USE flags for comparison with use.desc # Create the cache if it does not exist if [ -w ${use_cache} ]; then use_old=`cat ${use_cache}` echo "${use_avail}" > ${use_cache} chmod 600 ${use_cache} elif [ -w ${use_cache_parent} ]; then mkdir -p ${use_cache_dir} chmod 700 ${use_cache_dir} echo "${use_avail}" > ${use_cache} chmod 600 ${use_cache} use_old="" else do_report_err ${use_cache} write fi # Grab the contents of the USE variable. make_cand=`do_get_make nodashes` # Build a list of flags that do not exist in the USE variable. for flag in ${use_avail}; do new_cand="${new_cand}`do_get_depr ${flag} ${make_cand}`" done # Filter that list through the cached master list of flags. for flag in ${new_cand}; do new_flags="${new_flags}`do_get_depr ${flag} ${use_old}`" done if [ "${new_flags}" = "" ]; then echo "!!! No new USE flags are available." else echo "The following new USE flags are available:" echo "${new_flags}" echo echo "How would you like to handle them?" echo "1) Handle them individually" echo "2) Use Portage defaults (do not add to USE)" echo "3) Explicitly enable all new flags" echo "4) Explicitly disable all new flags" echo echo -n "Type (1, 2, 3, or 4): " luser_input="" while [ "${luser_input}" = "" ]; do read luser_input case ${luser_input} in "1") for h_flag in ${new_flags}; do do_get_desc ${h_flag} echo -n "How would you like to handle " echo -n "${h_flag}? [e]nable, " echo -n "[d]isable, [u]se default : " luser_handle="" while [ "${luser_handle}" = "" ]; do read luser_handle case ${luser_handle} in "e" | "E") changes="1" make_use=`do_use_add \ ${h_flag} \ ${make_use}` echo ;; "d" | "D") changes="1" make_use=`do_use_add \ "-${h_flag}" \ ${make_use}` echo ;; "u" | "U") echo ;; *) luser_handle="" ;; esac done done echo -n "*** All new flags have been " echo "processed." ;; "2") echo echo -n "*** No new flags were added to " echo "your USE." ;; "3") changes="1" for h_flag in ${new_flags}; do make_use=`do_use_add ${h_flag} \ ${make_use}` done echo echo -n "*** All new flags were enabled in " echo "your USE." ;; "4") changes="1" for h_flag in ${new_flags}; do make_use=`do_use_add \ "-${h_flag}" ${make_use}` done echo echo -n "*** All new flags were disabled in " echo "your USE." ;; *) luser_input="" ;; esac done fi # Write the changes if necessary. if [ "${changes}" != "0" ]; then do_write_make ${make_use} # Prune any locked flags that do not exist in the USE variable lock_prot=`do_lock_flags fakeflag` do_write_lock ${lock_prot} fi echo echo echo "*** Script finished ..." # Display USAGE statement for unhandled parameters else echo "Usage:" echo " ${run_name} action [flag] [...]" echo echo "Actions:" echo "-s, show Displays the contents of the USE variable." echo "-i, desc Displays a description of one or more USE flags." echo -n "-a, add Adds the specified flag(s) to the USE " echo "variable." echo -n "-d, del Deletes the specified flag(s) from " echo "the USE variable." echo "-l, lock Locks the specified flag(s) to prevent depreciation." echo -n "-k, unlock Unlocks the specified flags to allow " echo "depreciation." echo "-w, showlock Displays a list of locked flags." echo -n "-u, update Interactively updates the USE variable to " echo "reflect changes" echo " to use.desc." echo exit 1 fi exit 0