Fix typo in normalize_emerge_opts(). Bug #259526
[gentoolkit.git] / trunk / src / revdep-rebuild / revdep-rebuild
index 70edb1614c77826b71abc67f7e570d7071fd7907..4e1df16e10cc7a2522f4f7a6b0a56b6ebf8812c8 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 1999-2007 Gentoo Foundation
+# Copyright 1999-2008 Gentoo Foundation
 
 # revdep-rebuild: Reverse dependency rebuilder.
 # Original Author: Stanislav Brabec
 ##
 # Global Variables:
 
-# Must-be-blank variables:
+# Must-be-blank:
 unset GREP_OPTIONS
 
 # Readonly variables:
 declare -r APP_NAME="${0##*/}" # The name of this application
 declare -r OIFS="$IFS"         # Save the IFS
+declare -r     ENV_FILE=0_env.rr     # Contains environment variables
+declare -r   FILES_FILE=1_files.rr   # Contains a list of files to search
+declare -r  LDPATH_FILE=2_ldpath.rr  # Contains the LDPATH
+declare -r  BROKEN_FILE=3_broken.rr  # Contains the list of broken files
+declare -r  ERRORS_FILE=3_errors.rr  # Contains the ldd error output
+declare -r     RAW_FILE=4_raw.rr     # Contains the raw list of packages
+declare -r  OWNERS_FILE=4_owners.rr  # Contains the file owners
+declare -r    PKGS_FILE=4_pkgs.rr    # Contains the unsorted bare package names
+declare -r EBUILDS_FILE=4_ebuilds.rr # Contains the unsorted atoms
+                                     # (Appropriately slotted or versioned)
+declare -r   ORDER_FILE=5_order.rr   # Contains the sorted atoms
+declare -r  STATUS_FILE=6_status.rr  # Contains the ldd error output
+declare -ra FILES=(
+       "$ENV_FILE"
+       "$FILES_FILE"
+       "$LDPATH_FILE"
+       "$BROKEN_FILE"
+       "$ERRORS_FILE"
+       "$RAW_FILE"
+       "$OWNERS_FILE"
+       "$PKGS_FILE"
+       "$EBUILDS_FILE"
+       "$ORDER_FILE"
+       "$STATUS_FILE"
+)
 
 # "Boolean" variables: Considered "true" if it has any value at all
 # "True" indicates we should...
@@ -37,7 +62,7 @@ declare PORTAGE_NICENESS       # Renice to this value
 declare PORTAGE_ROOT           # The root path for portage
 
 # Customizable incremental variables:
-# These variables can be prepended to either by setting the variable in 
+# These variables can be prepended to either by setting the variable in
 # your environment prior to execution, or by placing an entry in
 # /etc/make.conf.
 #
@@ -52,7 +77,6 @@ declare SEARCH_DIRS_MASK # List of dirs not to search
 declare OLDPROG                # Previous pass through the progress meter
 declare EXACT_PKG              # Versionated atom to emerge
 declare HEAD_TEXT              # Feedback string about the search
-declare LIST                   # Prefix for temporary filenames
 declare NOCOLOR                # Set to "true" not to output term colors
 declare OK_TEXT                # Feedback about a search which found no errors
 declare RC_NOCOLOR             # Hack to insure we respect NOCOLOR
@@ -61,22 +85,49 @@ declare SKIP_LIST              # Array of atoms that cannot be emerged (masked?)
 declare SONAME                 # Soname/soname path pattern given on commandline
 declare SONAME_SEARCH          # Value of SONAME modified to match ldd's output
 declare WORKING_TEXT           # Feedback about the search
-declare ENV_FILE               # A file containing environment variables
+declare WORKING_DIR            # Working directory where cache files are kept
 
+main() {
+       # preliminary setup
+       get_opts "$@"
+       setup_portage
+       setup_search_paths_and_masks
+       get_search_env
+       echo
+
+       # Search for broken binaries
+       get_files
+       get_ldpath
+       main_checks
+
+       # Associate broken binaries with packages to rebuild
+       if [[ $PACKAGE_NAMES ]]; then
+               get_packages
+               clean_packages
+               assign_packages_to_ebuilds
+       else
+               get_exact_ebuilds
+       fi
+
+       # Rebuild packages owning broken binaries
+       get_build_order
+       rebuild
+
+       # All done
+       cleanup
+}
+##
+# Refuse to delete anything before we cd to our tmpdir
+# (See mkdir_and_cd_to_tmpdir()
 rm() {
-       local i
-       [[ $LIST && $APP_NAME ]] ||
-               die 1 '$LIST or $APP_NAME is not defined! (This is a bug.)'
-       for i; do
-               [[ $i = -* || $i = *.$APP_NAME* ]] || 
-                       die 1 "Oops, I'm not allowed to delete that. ($@)"
-       done
-       command rm "$@"
+       eerror "I was instructed to rm '$@'"
+       die 1 "Refusing to delete anything before changing to temporary directory."
 }
-# Somewhat more portable find -executable
-# FIXME/UNTESTED (I don't have access to all of the different versions of 
-# find.)
-# Usage: find PATH ARGS -- use find like normal, except use -executable instead 
+: <<'EW'
+##
+# GNU find has -executable, but if our users' finds do not have that flag
+# we emulate it with this function. Also emulates -writable and -readable.
+# Usage: find PATH ARGS -- use find like normal, except use -executable instead
 # of various versions of -perm /+ blah blah and hacks
 find() {
        hash find || { die 1 'find not found!'; }
@@ -100,43 +151,47 @@ find() {
                }
        else # Last resort
                find() {
-                       a=(${@//-executable/-exec test -x '{}' \;})
-                       a=(${a[@]//-writable/-exec test -w '{}' \;})
-                       a=(${a[@]//-readable/-exec test -r '{}' \;})
+                       a=(${@//-executable/-exec test -x '{}' \; -print})
+                       a=(${a[@]//-writable/-exec test -w '{}' \; -print})
+                       a=(${a[@]//-readable/-exec test -r '{}' \; -print})
                        command find "${a[@]}"
                }
        fi
        find "$@"
 }
+EW
+
 print_usage() {
 cat << EOF
 Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
 
 Broken reverse dependency rebuilder.
 
+  -C, --nocolor        Turn off colored output
+  -d, --debug          Print way too much information (uses bash's set -xv)
+  -e, --exact          Emerge based on exact package version
   -h, --help           Print this usage
+  -i, --ignore         Ignore temporary files from previous runs
   -k, --keep-temp      Do not delete temporary files on exit
-  -e, --exact          Emerge based on exact package version
+  -L, --library NAME   Emerge existing packages that use the library with NAME
+      --library=NAME   NAME can be a full path to the library or a basic
+                       regular expression (man grep)
   -l, --no-ld-path     Do not set LD_LIBRARY_PATH
-  -C, --nocolor        Turn off colored output
-  -i, --ignore         Ignore temporary files from previous runs
   -o, --no-order       Do not check the build order
                        (Saves time, but may cause breakage.)
+  -p, --pretend        Do a trial run without actually emerging anything
+                       (also passed to emerge command)
   -P, --no-progress    Turn off the progress meter
   -q, --quiet          Be less verbose (also passed to emerge command)
-  -v, --verbose        Be more verbose
-  -u, --no-util UTIL   Do not use features provided by UTIL
-      --no-util=UTIL   UTIL can be one of portage-utils or pkgcore
-                       or it can be a *quoted* space-delimited list.
-  -L, --library NAME   Emerge existing packages that use the library with NAME
-      --library=NAME   NAME can be a full path to the library or a basic
-                       regular expression (man grep)
+  -v, --verbose        Be more verbose (also passed to emerge command)
 
-Calls emerge, all other options are used for it (e. g. -p, --pretend).
+Calls emerge, options after -- are ignored by $APP_NAME
+and passed directly to emerge.
 
 Report bugs to <http://bugs.gentoo.org>
 EOF
 }
+##
 # Usage: progress i n
 #        i: current item
 #        n: total number of items to process
@@ -154,6 +209,7 @@ progress() {
                progress() { :; }
        fi
 }
+##
 # Usage: countdown n
 #        n: number of seconds to count
 countdown() {
@@ -164,16 +220,15 @@ countdown() {
        done
        echo -e '\a.'
 }
-# Get the name of a package owning a file on the filesystem using one of several
-# utilities: This is a placeholder. The function is defined in get_args()
-get_file_owner() { :; }
+##
 # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
 # (If any libs have whitespace in their filenames, someone needs punishment.)
 clean_var() {
-       awk 'BEGIN         {RS="[[:space:]]"}
+       gawk 'BEGIN         {RS="[[:space:]]"}
             /-\*/         {exit}
             /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
 }
+##
 # Exit and optionally output to sterr
 die() {
        local status=$1
@@ -181,14 +236,171 @@ die() {
        eerror "$@"
        exit $status
 }
+##
 # What to do when dynamic linking is consistent
 clean_exit() {
-       [[ $KEEP_TEMP ]] || rm $LIST.?_*
+       if [[ ! $KEEP_TEMP ]]; then
+               rm -f "${FILES[@]}"
+               if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
+                       # Remove the working directory
+                       builtin cd; rmdir "$WORKING_DIR"
+               fi
+       fi
        echo
        einfo "$OK_TEXT... All done. "
        exit 0
 }
-get_args() {
+##
+# Get the name of the package that owns a file or list of files given as args.
+get_file_owner() {
+       local IFS=$'\n'
+       # ${*/%/ } adds a space to the end of each object name to prevent false
+       # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
+       find -L /var/db/pkg -name CONTENTS -print0 |
+               xargs -0 grep -Fl "${*/%/ }" |
+               sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
+}
+##
+# Normalize some EMERGE_OPTIONS
+normalize_emerge_opts() {
+       # Normalize some EMERGE_OPTIONS
+       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
+       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
+       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
+}
+##
+# Use the color preference from portage
+setup_color() {
+       # This should still work if NOCOLOR is set by the -C flag or in the user's
+       # environment.
+       export NOCOLOR=$(portageq envvar NOCOLOR)
+       [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
+       . /etc/init.d/functions.sh
+}
+##
+# Die if an argument is missing.
+die_if_missing_arg() {
+       [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
+}
+##
+# Die because an option is not recognized.
+die_invalid_option() {
+       # Can't use eerror and einfo because this gets called before function.sh
+       # is sourced
+       echo
+       echo  "Encountered unrecognized option $1." >&2
+       echo
+       echo  "$APP_NAME no longer automatically passes unrecognized options to portage."
+       echo  "Separate emerge-only options from revdep-rebuild options with the -- flag."
+       echo
+       echo  "For example, $APP_NAME -v -- --ask"
+       echo
+       echo  "See the man page or $APP_NAME -h for more detail."
+       echo
+       exit 1
+}
+##
+# Warn about deprecated options.
+warn_deprecated_opt() {
+       # Can't use eerror and einfo because this gets called before function.sh
+       # is sourced
+       echo
+       echo "Encountered deprecated option $1." >&2
+       [[ $2 ]] && echo "Please use $2 instead." >&2
+}
+##
+# Get whole-word commandline options preceded by two dashes.
+get_longopts() {
+       case $1 in
+                                              --nocolor) export NOCOLOR="yes";;
+                                             --no-color) warn_deprecated_opt "$1" "--nocolor"
+                                                         export NOCOLOR="yes";;
+                                                --debug) set -xv;;
+                                                --exact) unset PACKAGE_NAMES;;
+                                                 --help) print_usage
+                                                         exit 0;;
+                                               --ignore) RM_OLD_TEMPFILES=1;;
+                                            --keep-temp) KEEP_TEMP=1;;
+                                            --library=*) # TODO: check for invalid values
+                                                         SONAME="${1#*=}"
+                                                         unset SEARCH_BROKEN;;
+                           --soname=*|--soname-regexp=*) # TODO: check for invalid values
+                                                         warn_deprecated_opt "${1%=*}" "--library"
+                                                         SONAME="${1#*=}"
+                                                         unset SEARCH_BROKEN;;
+                                              --library) # TODO: check for invalid values
+                                                         die_if_missing_arg $1 $2
+                                                         shift
+                                                         SONAME="$1"
+                                                         unset SEARCH_BROKEN;;
+                               --soname|--soname-regexp) # TODO: check for invalid values
+                                                         warn_deprecated_opt "$1" "--library"
+                                                         die_if_missing_arg $1 $2
+                                                         shift
+                                                         SONAME="$1"
+                                                         unset SEARCH_BROKEN;;
+                                           --no-ld-path) unset FULL_LD_PATH;;
+                                             --no-order) unset ORDER_PKGS;;
+                                          --no-progress) progress() { :; };;
+                                              --pretend) EMERGE_OPTIONS+=("--pretend");;
+                                                --quiet) echo_v() { :; }
+                                                         progress() { :; }
+                                                         quiet=1
+                                                         EMERGE_OPTIONS+=($1);;
+                                              --verbose) VERBOSE=1
+                                                         EMERGE_OPTIONS+=("--verbose");;
+                                        --extra-verbose) warn_deprecated_opt "$1" "--verbose"
+                                                         VERBOSE=1
+                                                         EMERGE_OPTIONS+=("--verbose");;
+                                        --package-names) # No longer used, since it is the
+                                                         # default. We accept it for
+                                                         # backwards compatibility.
+                                                         warn_deprecated_opt "$1"
+                                                         PACKAGE_NAMES=1;;
+                                                      *) die_invalid_option $1;;
+       esac
+}
+
+##
+# Get single-letter commandline options preceded by a single dash.
+get_shortopts() {
+       local OPT OPTSTRING OPTARG OPTIND
+       while getopts ":CdehikL:loPpqu:vX" OPT; do
+               case "$OPT" in
+                       C) # TODO: Match syntax with the rest of gentoolkit
+                          export NOCOLOR="yes";;
+                       d) set -xv;;
+                       e) unset PACKAGE_NAMES;;
+                       h) print_usage
+                          exit 0;;
+                       i) RM_OLD_TEMPFILES=1;;
+                       k) KEEP_TEMP=1;;
+                       L) # TODO: Check for invalid values
+                          SONAME="${OPTARG#*=}"
+                          unset SEARCH_BROKEN;;
+                       l) unset FULL_LD_PATH;;
+                       o) unset ORDER_PKGS;;
+                       P) progress() { :; };;
+                       p) EMERGE_OPTIONS+=("--pretend");;
+                       q) echo_v() { :; }
+                          progress() { :; }
+                          quiet=1
+                          EMERGE_OPTIONS+=("--quiet");;
+                       v) VERBOSE=1
+                          EMERGE_OPTIONS+=("--verbose");;
+                       X) # No longer used, since it is the default.
+                          # We accept it for backwards compatibility.
+                          warn_deprecated_opt "-X"
+                          PACKAGE_NAMES=1;;
+                       *) die_invalid_option "-$OPTARG";;
+               esac
+       done
+}
+##
+# Get command-line options.
+get_opts() {
+       local avoid_utils
+       local -a args
        echo_v() { ewarn "$@"; }
        unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
        ORDER_PKGS=1
@@ -196,202 +408,137 @@ get_args() {
        SONAME="not found"
        SEARCH_BROKEN=1
        FULL_LD_PATH=1
-       local avoid_utils
        while [[ $1 ]]; do
                case $1 in
-               -h|--help)
-                       print_usage
-                       exit 0
-                       ;;
-               -e|--exact)
-                       unset PACKAGE_NAMES
-                       ;;
-               -o|--no-order)
-                       unset ORDER_PKGS
-                       ;;
-               -P|--no-progress)
-                       progress() { :; }
-                       ;;
-               -q|--quiet)
-                       echo_v() { :; }
-                       progress() { :; }
-                       quiet=1
-                       EMERGE_OPTIONS+=($1)
-                       ;;
-               -L=*|--library=*|--soname=*|--soname-regexp=*)
-                       SONAME="${1#*=}"
-                       unset SEARCH_BROKEN
-                       ;;
-               -L|--library|--soname|--soname-regexp)
-                       [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
-                       shift
-                       SONAME="$1"
-                       unset SEARCH_BROKEN
-                       ;;
-               -u=*|--no-util=*)
-                       # TODO: check for invalid values
-                       avoid_utils="${1#*=}"
-                       ;;
-               -u|--no-util)
-                       [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
-                       shift
-                       avoid_utils="$1"
-                       ;;
-               -nc|-C|--no-color|--nocolor)
-                       export NOCOLOR=yes
-                       ;;
-               -l|-np|--no-ld-path)
-                       unset FULL_LD_PATH
-                       ;;
-               -i|--ignore)
-                       RM_OLD_TEMPFILES=1
-                       ;;
-               -k|--keep-temp)
-                       KEEP_TEMP=1
-                       ;;
-               -vv|--extra-verbose|-v|--verbose)
-                       VERBOSE=1
-                       EMERGE_OPTIONS+=($1)
-                       ;;
-               -X|--package-names)
-                       # No longer used, since it is the default.
-                       # We accept it for backwards compatibility
-                       PACKAGE_NAMES=1
-                       ;;
-               --)
-                       ;;
-               *)
-                       EMERGE_OPTIONS+=($1)
-                       ;;
+                       --) shift
+                           EMERGE_OPTIONS+=("$@")
+                           break;;
+                       -*) while true; do
+                             args+=("$1")
+                             shift
+                             [[ ${1:--} = -* ]] && break
+                           done
+                           if [[ ${args[0]} = --* ]]; then
+                             get_longopts  "${args[@]}"
+                           else
+                             get_shortopts "${args[@]}"
+                           fi;;
+                        *) die_invalid_option "$1";;
                esac
-               shift
+               unset args
        done
-       # Check if various utils are allowed and installed
-       if [[ $avoid_utils != *portage-utils* ]] && hash qfile 2> /dev/null; then
-               get_file_owner() { qfile -qvC "$@"; }
-       elif [[ $avoid_utils != *pkgcore* ]] && hash pquery 2> /dev/null; then
-               get_file_owner() { local IFS=,; pquery --nocolor --owns="$*"; }
-       # equery disabled for incompatibility with modern portage.
-       # elif [[ $avoid_utils != *equery* ]] && hash equery 2> /dev/null; then
-       #       get_file_owner() { equery -q -C b $@; }
-       else
-               get_file_owner() {
-                       local IFS=$'\n'
-                       # ${*/%/ } adds a space to the end of each object name to prevent false
-                       # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
-                       find -L /var/db/pkg -name CONTENTS -print0 |
-                               xargs -0 grep -Fl "${*/%/ }" |
-                               sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
-               }
-       fi
 
-       # Use the color preference from portage
-       export NOCOLOR=$(portageq envvar NOCOLOR)
-       [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
-       source /etc/init.d/functions.sh
-       
-       # Normalize some EMERGE_OPTIONS
-       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
-       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
-       EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--verbose})
+       setup_color
+       normalize_emerge_opts
+
+       # If the user is not super, add --pretend to EMERGE_OPTIONS
        if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
                ewarn "You are not superuser. Adding --pretend to emerge options."
                EMERGE_OPTIONS+=(--pretend)
        fi
 }
-is_real_merge() [[
-       ${EMERGE_OPTIONS[@]} != *--pretend* && ${EMERGE_OPTIONS[@]} != *--fetchonly*
-]]
-
-get_args "$@"
-
-einfo "Configuring search environment for $APP_NAME"
-
-# Obey PORTAGE_NICENESS
-PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
-if [[ $PORTAGE_NICENESS ]]; then
-       renice $PORTAGE_NICENESS $$ > /dev/null
-       # Since we have already set our nice value for our processes,
-       # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
-       export PORTAGE_NICENESS="0"
-fi
-
-PORTAGE_ROOT=$(portageq envvar ROOT)
-PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
-
-# Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
-# portage, and the environment
-
-# Read the incremental variables from environment and portage
-# Until such time as portage supports these variables as incrementals
-# The value will be what is in /etc/make.conf
-SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
-SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
-LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
-
-# Add the defaults
-if [[ -d /etc/revdep-rebuild ]]; then
-       for ENV_FILE in /etc/revdep-rebuild/*; do
-               SEARCH_DIRS+=" "$(. $ENV_FILE; echo $SEARCH_DIRS)
-               SEARCH_DIRS_MASK+=" "$(. $ENV_FILE; echo $SEARCH_DIRS_MASK)
-               LD_LIBRARY_MASK+=" "$(. $ENV_FILE; echo $LD_LIBRARY_MASK)
-       done
-       unset ENV_FILE # HACK
-else
-       SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
-       SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
-       LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
-fi
-
-# Get the ROOTPATH and PATH from /etc/profile.env
-if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
-       SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
-fi
-
-# Get the directories from /etc/ld.so.conf
-if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
-       SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
-fi
-
-# Set the final variables
-SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
-SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
-LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
-# Filter masked paths from SEARCH_DIRS
-filter_SEARCH_DIRS=
-for sdir in ${SEARCH_DIRS} ; do
-       unset skip_me
-       for mdir in ${SEARCH_DIRS_MASK} ; do
-               [[ ${sdir} == ${mdir}/* ]] \
-                       && skip_me=1 && break
-       done
-       [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
-done
-SEARCH_DIRS=$(clean_var "${filter_SEARCH_DIRS}")
-unset sdir mdir skip_me filter_SEARCH_DIRS
-[[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
-
-set_trap() {
-       trap "rm_temp $1" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+##
+# Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
+is_real_merge() {
+       [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
+          ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
 }
-rm_temp() {
-       rm $1
-       die 1 $'  ...terminated. Removing incomplete '"$1."
+##
+# Clean up temporary files and exit
+cleanup_and_die() {
+       rm -f "$@"
+       die 1 "  ...terminated. Removing incomplete $@."
+}
+##
+# Clean trap
+clean_trap() {
+       trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+       rm -f "$@"
+}
+##
+# Returns 0 if the first arg is found in the remaining args, 1 otherwise
+# (Returns 2 if given fewer than 2 arguments)
+has() {
+       (( $# > 1 )) || return 2
+       local IFS=$'\a' target="$1"
+       shift
+       [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
+}
+##
+# Dies when it can't change directories
+cd() {
+       if builtin cd -P "$@"; then
+               if [[ $1 != $PWD ]]; then
+                       # Some symlink malfeasance is going on
+                       die 1 "Working directory expected to be $1, but it is $PWD"
+               fi
+       else
+               die 1 "Unable to change working directory to '$@'"
+       fi
+}
+##
+# Tries not to delete any files or directories it shouldn't
+setup_rm() {
+       ##
+       # Anything in the FILES array in tmpdir is fair game for removal
+       rm() {
+               local i IFS=$'\a'
+               [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
+               case $@ in
+                       */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
+               esac
+               for i; do
+                       # Don't delete files that are not listed in the array
+                       # Allow no slashes or recursive deletes at all.
+                       case $i in
+                               */*|-*r*|-*R*) :;;        # Not OK
+                                          -*) continue;; # OK
+                       esac
+                       has "$i" "${FILES[@]}" && continue
+                       die 1 "Oops, I'm not allowed to delete that. ($@)"
+               done
+               command rm "$@"
+       }
+       # delete this setup function so it's harmless to re-run
+       setup_rm() { :; }
+}
+##
+# Make our temporary files directory
+# $1 - directory name
+# $2 - user name
+verify_tmpdir() {
+       umask 007 || die $? "Unable to set umask 007"
+       if [[ ! $1 ]]; then
+               die 1 'Temporary file path is unset! (This is a bug.)'
+       elif [[ -d $1 ]]; then
+               # HACK: I hate using find this way
+               if [[ $(find "$1" -type d ! \( -user $2 -perm -0700 \) ) ]]; then
+                       eerror "Incorrect permissions on $1"
+                       eerror "or at least one file in $1."
+                       die 1  "Please make sure it's not a symlink and then remove it."
+               fi
+               cd "$1"
+       else
+               die 1 "Unable to find a satisfactory location for temporary files ($1)"
+       fi
+       [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
+       setup_rm
 }
 get_search_env() {
        local new_env
        local old_env
+       local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
        # Find a place to put temporary files
-       # TODO; let the user choose where to put tempfiles
-       # gfind $HOME/ /var/tmp/ /tmp/ -writable -print -quit
-       # HACK: This is a rather noisy, but portable way to implement -quit
-       while read LIST; do
-               break # Set LIST
-       done < <(find $HOME/ /var/tmp/ /tmp/ -writable -print)
-       [[ $LIST ]] ||
-               die 1 "Unable to find a satisfactory location for temporary files"
-
-       LIST+=".$APP_NAME"
+       if [[ "$uid" == "root" ]]; then
+               local tmp_target="/var/cache/${APP_NAME}"
+       else
+               local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
+       fi
+
+       # From here on all work is done inside the temporary directory
+       verify_tmpdir "$tmp_target" "$uid"
+       WORKING_DIR="$tmp_target"
+
        if [[ $SEARCH_BROKEN ]]; then
                SONAME_SEARCH="$SONAME"
                HEAD_TEXT="broken by a package update"
@@ -408,9 +555,6 @@ get_search_env() {
                        # Set to "<tab>$SONAME<space>"
                        SONAME_SEARCH=$'\t'"$SONAME "
                fi
-               local uuid="${SONAME##*/}"
-               uuid="${uuid//[[:space:]]}"
-               LIST+="_$uuid"
                HEAD_TEXT="using $SONAME"
                OK_TEXT="There are no dynamic links to $SONAME"
                unset WORKING_TEXT
@@ -421,7 +565,7 @@ get_search_env() {
                while read; do
                        RM_OLD_TEMPFILES=1
                        break
-               done < <(find -L "$LIST."* -maxdepth 0 -type f -mmin +1440 -print 2>/dev/null)
+               done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
        fi
 
        # Compare old and new environments
@@ -441,22 +585,31 @@ get_search_env() {
                        FULL_LD_PATH="$FULL_LD_PATH"
                EOF
        )
-       if [[ -r $LIST.0_env && -s $LIST.0_env ]]; then
-               old_env=$(<"$LIST.0_env")
+       if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
+               old_env=$(<"$ENV_FILE")
                if [[ $old_env != $new_env ]]; then
                        ewarn 'Environment mismatch from previous run, deleting temporary files...'
                        RM_OLD_TEMPFILES=1
                fi
        else
-               # No 0_env file found, silently delete any other tempfiles that may exist
+               # No env file found, silently delete any other tempfiles that may exist
                RM_OLD_TEMPFILES=1
        fi
 
        # If we should remove old tempfiles, do so
-       [[ $RM_OLD_TEMPFILES ]] && rm -f "$LIST."*
-       
+       if [[ $RM_OLD_TEMPFILES ]]; then
+               rm -f "${FILES[@]}"
+       else
+               for file in "${FILES[@]}"; do
+                       if [ -e "$file" ]; then
+                               chown ${uid}:portage "$file"
+                               chmod 700 "$file"
+                       fi
+               done
+       fi
+
        # Save the environment in a file for next time
-       echo "$new_env" > "$LIST.0_env"
+       echo "$new_env" > "$ENV_FILE"
 
        [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
 
@@ -465,48 +618,51 @@ get_search_env() {
        einfo "Packages containing binaries and libraries $HEAD_TEXT"
        einfo "will be emerged."
 }
+
 get_files() {
        einfo "Collecting system binaries and libraries"
-       if [[ -r $LIST.1_files && -s $LIST.1_files ]]; then
-               einfo "Found existing $LIST.1_files"
+       if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
+               einfo "Found existing $FILES_FILE"
        else
                # Be safe and remove any extraneous temporary files
-               rm -f $LIST.[1-9]_*
+               # Don't remove 0_env.rr - The first file in the array
+               rm -f "${FILES[@]:1}"
 
-               set_trap "$LIST.1_*"
+               clean_trap "$FILES_FILE"
 
                if [[ $SEARCH_DIRS_MASK ]]; then
                        findMask=($SEARCH_DIRS_MASK)
                        findMask="${findMask[@]/#/-o -path }"
                        findMask="( ${findMask#-o } ) -prune -o"
                fi
-               find ${SEARCH_DIRS[@]} $findMask -type f \( -executable -o \
+               # TODO: Check this -- afaict SEARCH_DIRS isn't an array, so this should just be $SEARCH_DIRS?
+               find ${SEARCH_DIRS[@]} $findMask -type f \( -perm -u+x -o -perm -g+x -o -perm -o+x -o \
                        -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
-                       sort -u > "$LIST.1_files" ||
+                       sort -u > "$FILES_FILE" ||
                        die $? "find failed to list binary files (This is a bug.)"
-               einfo "Generated new $LIST.1_files"
+               einfo "Generated new $FILES_FILE"
        fi
 }
 get_ldpath() {
        local COMPLETE_LD_LIBRARY_PATH
        [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
        einfo 'Collecting complete LD_LIBRARY_PATH'
-       if [[ -r $LIST.2_ldpath && -s $LIST.2_ldpath ]] ; then
-               einfo "Found existing $LIST.2_ldpath."
+       if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
+               einfo "Found existing $LDPATH_FILE."
        else
-               set_trap "$LIST.2_ldpath"
+               clean_trap "$LDPATH_FILE"
                # Ensure that the "trusted" lib directories are at the start of the path
                COMPLETE_LD_LIBRARY_PATH=(
                        /lib*
                        /usr/lib*
                        $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
-                       $(sed 's:/[^/]*$::' < "$LIST.1_files" | sort -ru)
+                       $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
                )
                IFS=':'
                COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
                IFS="$OIFS"
-               echo "$COMPLETE_LD_LIBRARY_PATH" > "$LIST.2_ldpath"
-               einfo "Generated new $LIST.2_ldpath"
+               echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
+               einfo "Generated new $LDPATH_FILE"
        fi
 }
 main_checks() {
@@ -518,24 +674,22 @@ main_checks() {
        local numFiles
        local COMPLETE_LD_LIBRARY_PATH
        if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
-               [[ -r $LIST.2_ldpath && -s $LIST.2_ldpath ]] || die 1 "unable to find $LIST.2_ldpath"
-               COMPLETE_LD_LIBRARY_PATH=$(<"$LIST.2_ldpath")
+               [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
+                       die 1 "Unable to find $LDPATH_FILE"
+               COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
        fi
        einfo "Checking dynamic linking $WORKING_TEXT"
-       if [[ -r $LIST.3_rebuild && -s $LIST.3_rebuild ]]; then
-               einfo "Found existing $LIST.3_rebuild."
+       if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
+               einfo "Found existing $BROKEN_FILE."
        else
-               [[ $LIST ]] || die 1 "$LIST" 'is undefined! (This is a bug.)'
-               set_trap "$LIST.3_rebuild"
-               set_trap "$LIST.3_ldd_errors"
-               rm -f "$LIST.3"*
-               files=($(<"$LIST.1_files"))
+               clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
+               files=($(<"$FILES_FILE"))
                numFiles="${#files[@]}"
                for target_file in "${files[@]}"; do
                        if [[ $target_file != *.la ]]; then
                                # Note: double checking seems to be faster than single with complete path
                                # (special add ons are rare).
-                               ldd_output=$(ldd "$target_file" 2>> "$LIST.3_ldd_errors" | sort -u)
+                               ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
                                ldd_status=$? # TODO: Check this for problems with sort
                                # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
                                if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
@@ -555,7 +709,7 @@ main_checks() {
                                                        )
                                                        MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
                                                        if [[ $MISSING_LIBS ]]; then
-                                                               echo "obj $target_file" >> "$LIST.3_rebuild"
+                                                               echo "obj $target_file" >> "$BROKEN_FILE"
                                                                echo_v "  broken $target_file (requires $MISSING_LIBS)"
                                                        fi
                                                fi
@@ -572,7 +726,7 @@ main_checks() {
                                                )
                                                MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
                                                if [[ $MISSING_LIBS ]]; then
-                                                       echo "obj $target_file" >> "$LIST.3_rebuild"
+                                                       echo "obj $target_file" >> "$BROKEN_FILE"
                                                        if [[ $SEARCH_BROKEN ]]; then
                                                                echo_v "  broken $target_file (requires $MISSING_LIBS)"
                                                        else
@@ -584,14 +738,14 @@ main_checks() {
                        elif [[ $SEARCH_BROKEN ]]; then
                                # Look for broken .la files
                                for depend in $(
-                                       awk -F"[=']" '/^dependency_libs/{
+                                       gawk -F"[=']" '/^dependency_libs/{
                                                gsub("^-[^[:space:]]*", "", $3);
                                                gsub("[[:space:]]-[^[:space:]]*", "", $3);
                                                print $3
                                        }' "$target_file"
                                ); do
                                        if [[ $depend = /* && ! -e $depend ]]; then
-                                               echo "obj $target_file" >> "$LIST.3_rebuild"
+                                               echo "obj $target_file" >> "$BROKEN_FILE"
                                                echo_v "  broken $target_file (requires $depend)"
                                        fi
                                done
@@ -603,21 +757,22 @@ main_checks() {
                if [[ $SEARCH_BROKEN ]]; then
                        # Look for missing version
                        while read target_file; do
-                               echo "obj $target_file" >> "$LIST.3_rebuild"
+                               echo "obj $target_file" >> "$BROKEN_FILE"
                                echo_v "  broken $target_file (no version information available)"
                        done < <(
                                # Regexify LD_LIBRARY_MASK. Exclude it from the search.
                                LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
-                               awk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
+                               gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
                                        /no version information available/ && $0 !~ ldmask {
                                                gsub(/[()]/, "", $NF)
                                                if (seen[$NF]++)  next
                                                print $NF
-                                       }' "$LIST.3_ldd_errors"
+                                       }' "$ERRORS_FILE"
                        )
                fi
-               [[ -r $LIST.3_rebuild && -s $LIST.3_rebuild ]] || clean_exit
-               einfo "Generated new $LIST.3_rebuild"
+               [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
+               sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
+               einfo "Generated new $BROKEN_FILE"
        fi
 }
 get_packages() {
@@ -626,49 +781,48 @@ get_packages() {
        local PKG
        local obj
        einfo 'Assigning files to packages'
-       if [[ -r $LIST.4_packages_raw && -s $LIST.4_packages_raw ]]; then
-               einfo "Found existing $LIST.4_packages_raw"
+       if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
+               einfo "Found existing $RAW_FILE"
        else
-               set_trap "$LIST.4_packages*"
-               rm -f $LIST.4*
+               clean_trap "$RAW_FILE" "$OWNERS_FILE"
                while read obj target_file; do
                        EXACT_PKG=$(get_file_owner $target_file)
                        if [[ $EXACT_PKG ]]; then
                                # Strip version information
                                PKG="${EXACT_PKG%%-r[[:digit:]]*}"
                                PKG="${PKG%-*}"
-                               echo "$EXACT_PKG" >> $LIST.4_packages_raw
-                               echo "$target_file -> $EXACT_PKG" >> $LIST.4_package_owners
+                               echo "$EXACT_PKG" >> "$RAW_FILE"
+                               echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
                                echo_v "  $target_file -> $PKG"
                        else
                                ewarn " !!! $target_file not owned by any package is broken !!!"
-                               echo "$target_file -> (none)" >> $LIST.4_package_owners
+                               echo "$target_file -> (none)" >> "$OWNERS_FILE"
                                echo_v "  $target_file -> (none)"
                        fi
-               done < "$LIST.3_rebuild"
-               einfo "Generated new $LIST.4_packages_raw and $LIST.4_package_owners"
+               done < "$BROKEN_FILE"
+               einfo "Generated new $RAW_FILE and $OWNERS_FILE"
        fi
        # if we find '(none)' on every line, exit out
-       if ! grep -qvF '(none)' "$LIST.4_package_owners"; then
+       if ! grep -qvF '(none)' "$OWNERS_FILE"; then
                ewarn "Found some broken files, but none of them were associated with known packages"
                ewarn "Unable to proceed with automatic repairs."
-               ewarn "The broken files are listed in $LIST.4_package_owners"
+               ewarn "The broken files are listed in $OWNERS_FILE"
                if [[ $VERBOSE ]]; then
                        ewarn "The broken files are:"
                        while read filename junk; do
                                ewarn "  $filename"
-                       done < "$LIST.4_package_owners"
+                       done < "$OWNERS_FILE"
                fi
                exit 0 # FIXME: Should we exit 1 here?
        fi
 }
 clean_packages() {
        einfo 'Cleaning list of packages to rebuild'
-       if [[ -r $LIST.4_packages && -s $LIST.4_packages ]]; then
-               einfo "Found existing $LIST.4_packages"
+       if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
+               einfo "Found existing $PKGS_FILE"
        else
-               sort -u $LIST.4_packages_raw > $LIST.4_packages
-               einfo "Generated new $LIST.4_packages"
+               sort -u "$RAW_FILE" > "$PKGS_FILE"
+               einfo "Generated new $PKGS_FILE"
        fi
 }
 assign_packages_to_ebuilds() {
@@ -676,18 +830,18 @@ assign_packages_to_ebuilds() {
        local PKG
        local SLOT
        einfo 'Assigning packages to ebuilds'
-       if [[ -r $LIST.4_ebuilds && -s $LIST.4_ebuilds ]]; then
-               einfo "Found existing $LIST.4_ebuilds"
-       elif [[ -r $LIST.4_packages && -s $LIST.4_packages ]]; then
-                       set_trap "$LIST.4_ebuilds"
+       if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
+               einfo "Found existing $EBUILDS_FILE"
+       elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
+                       clean_trap "$EBUILDS_FILE"
                        while read EXACT_PKG; do
                                # Get the slot
                                PKG="${EXACT_PKG%%-r[[:digit:]]*}"
                                PKG="${PKG%-*}"
                                SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
                                echo "$PKG:$SLOT"
-                       done < "$LIST.4_packages" > "$LIST.4_ebuilds"
-                       einfo "Generated new $LIST.4_ebuilds"
+                       done < "$PKGS_FILE" > "$EBUILDS_FILE"
+                       einfo "Generated new $EBUILDS_FILE"
        else
                einfo 'Nothing to rebuild.'
                die 1 '(The program should have already quit, so this is a minor bug.)'
@@ -695,13 +849,13 @@ assign_packages_to_ebuilds() {
 }
 get_exact_ebuilds() {
        einfo 'Assigning files to ebuilds'
-       if [[ -r $LIST.4_ebuilds && -s $LIST.4_ebuilds ]]; then
-               einfo "Found existing $LIST.4_ebuilds"
-       elif [[ -r $LIST.3_rebuild && -s $LIST.3_rebuild ]]; then
-               rebuildList=" $(<"$LIST.3_rebuild") "
+       if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
+               einfo "Found existing $EBUILDS_FILE"
+       elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
+               rebuildList=" $(<"$BROKEN_FILE") "
                rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
-               get_file_owner "${rebuildList[@]}" > $LIST.4_ebuilds
-               einfo "Generated new $LIST.4_ebuilds"
+               get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
+               einfo "Generated new $EBUILDS_FILE"
        else
                einfo 'Nothing to rebuild.'
                die 1 '(The program should have already quit, so this is a minor bug.)'
@@ -726,36 +880,41 @@ get_build_order() {
                return
        fi
        einfo 'Evaluating package order'
-       if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
-               einfo "Found existing $LIST.5_order"
+       if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
+               einfo "Found existing $ORDER_FILE"
        else
-               set_trap "$LIST.5_order"
-               RAW_REBUILD_LIST=$(<"$LIST.4_ebuilds")
+               clean_trap "$ORDER_FILE"
+               RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
                if [[ $RAW_REBUILD_LIST ]]; then
                        export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
-                       RAW_REBUILD_LIST=($RAW_REBUILD_LIST)
+                       RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
                        # If PACKAGE_NAMES is defined we're using slots, not versions
                        if [[ $PACKAGE_NAMES ]]; then
                                # Eliminate atoms that can't be built
-                               for (( i=0; i<${#RAW_REBUILD_LIST[@]}; i++ )); do
-                                       portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
-                                       SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
+                               for i in "${!RAW_REBUILD_LIST[@]}"; do
+                                       if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
+                                               portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
+                                               SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
+                                       fi
                                        unset RAW_REBUILD_LIST[i]
                                done
                                # If RAW_REBUILD_LIST is empty, then we have nothing to build.
                                if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
-                                       list_skipped_packages
+                                       if (( ${#SKIP_LIST[@]} == 0 )); then
+                                               ewarn "The list of packages to skip is empty, but there are no"
+                                               ewarn "packages listed to rebuild either. (This is a bug.)"
+                                       else
+                                               list_skipped_packages
+                                       fi
                                        die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
                                fi
-                       else
-                               RAW_REBUILD_LIST=("${RAW_REBUILD_LIST[@]/#/=}")
                        fi
                        RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
                        REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
                        if (( ${PIPESTATUS[0]} == 0 )); then
                                emerge --deep $RAW_REBUILD_LIST |
                                        sed 's/\[[^]]*\]//g' |
-                                       grep -F "$REBUILD_GREP" > $LIST.5_order
+                                       grep -F "$REBUILD_GREP" > "$ORDER_FILE"
                        fi
 
                        # Here we use the PIPESTATUS from the second emerge, the --deep one.
@@ -771,7 +930,7 @@ get_build_order() {
                                                        and/or /etc/portage/package.unmask to unmask it
                                        EOF
                                        countdown 5
-                                       rm -f "$LIST.5_order"
+                                       rm -f "$ORDER_FILE"
                        fi
                        export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
                else
@@ -779,105 +938,162 @@ get_build_order() {
                        die 1 '(The program should have already quit, so this is a minor bug.)'
                fi
        fi
-       [[ -r $LIST.5_order && -s $LIST.5_order ]] && einfo "Generated new $LIST.5_order"
-}
-
-get_search_env
-echo
-get_files
-echo
-get_ldpath
-echo
-main_checks
-echo
-if [[ $PACKAGE_NAMES ]]; then
-       get_packages
-       echo
-       clean_packages
-       echo
-       assign_packages_to_ebuilds
-else
-       get_exact_ebuilds
-fi
-echo
-get_build_order
-echo
+       [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
+}
 
-# Clean up no longer needed environment variables
-unset SEARCH_DIRS SEARCH_DIRS_MASK LD_LIBRARY_MASK PORTAGE_ROOT
+show_unowned_files() {
+       if grep -qF '(none)' "$OWNERS_FILE"; then
+               ewarn "Found some broken files that weren't associated with known packages"
+               ewarn "The broken files are:"
+               while read filename junk; do
+                       [[ $junk = *none* ]] && ewarn "  $filename"
+               done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
+       fi
+}
+##
+# Setup portage and the search paths
+setup_portage() {
+       local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
+       PORTAGE_ROOT=$(portageq envvar ROOT)
 
-[[ -r $LIST.5_order && -s $LIST.5_order ]] &&
-       REBUILD_LIST=($(<"$LIST.5_order")) ||
-       REBUILD_LIST=($(sort -u "$LIST.4_ebuilds"))
+       # Obey PORTAGE_NICENESS
+       if [[ $PORTAGE_NICENESS ]]; then
+               renice $PORTAGE_NICENESS $$ > /dev/null
+               # Since we have already set our nice value for our processes,
+               # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
+               export PORTAGE_NICENESS="0"
+       fi
 
-trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+       PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
+}
 
-REBUILD_LIST="${REBUILD_LIST[@]}"
-# PACKAGE_NAMES means slots, not versions, so no '=' is required
-[[ $PACKAGE_NAMES ]] || REBUILD_LIST="=${REBUILD_LIST//[[:space:]]/ =}"
+##
+# Setup the paths to search (and filter the ones to avoid)
+setup_search_paths_and_masks() {
+       local configfile sdir mdir skip_me filter_SEARCH_DIRS
 
-einfo 'All prepared. Starting rebuild'
-echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST"
+       einfo "Configuring search environment for $APP_NAME"
 
-is_real_merge && countdown 10
+       # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
+       # portage, and the environment
 
-# Link file descriptor #6 with stdin so --ask will work
-exec 6<&0
+       # Read the incremental variables from environment and portage
+       # Until such time as portage supports these variables as incrementals
+       # The value will be what is in /etc/make.conf
+       SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
+       SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
+       LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
 
-# Run in background to correctly handle Ctrl-C
-{
-       EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6
-       echo $? > $LIST.6_status
-} &
-wait
+       # Add the defaults
+       if [[ -d /etc/revdep-rebuild ]]; then
+               for configfile in /etc/revdep-rebuild/*; do
+                       SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
+                       SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
+                       LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
+               done
+       else
+               SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
+               SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
+               LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
+       fi
 
-# Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
-exec 0<&6 6<&-
+       # Get the ROOTPATH and PATH from /etc/profile.env
+       if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
+               SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
+       fi
 
-show_unowned_files() {
-       if grep -qF '(none)' "$LIST.4_package_owners"; then
-               ewarn "Found some broken files that weren't associated with known packages"
-               ewarn "The broken files are:"
-               while read filename junk; do
-                       [[ $junk = *none* ]] && ewarn "  $filename"
-               done < "$LIST.4_package_owners" | awk '!s[$0]++' # (omit dupes)
+       # Get the directories from /etc/ld.so.conf
+       if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
+               SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
        fi
+
+       # Set the final variables
+       SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
+       SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
+       LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
+       # Filter masked paths from SEARCH_DIRS
+       for sdir in ${SEARCH_DIRS} ; do
+               skip_me=
+               for mdir in ${SEARCH_DIRS_MASK}; do
+                       [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
+               done
+               [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
+       done
+       SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
+       [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
 }
+##
+# Rebuild packages owning broken binaries
+rebuild() {
+       if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
+               REBUILD_LIST=( $(<"$LIST.5_order") )
+               REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
+       else
+               REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
+       fi
 
-if (( $(<"$LIST.6_status") != 0 )); then
-       ewarn
-       ewarn "$APP_NAME failed to emerge all packages."
-       ewarn 'you have the following choices:'
-       einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
-       einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
-       einfo "  (and remove $LIST.5_order to be evaluated again)"
-       einfo '- Modify the above emerge command and run it manually.'
-       einfo '- Compile or unmerge unsatisfied packages manually,'
-       einfo '  remove temporary files, and try again.'
-       einfo '  (you can edit package/ebuild list first)'
-       einfo
-       einfo 'To remove temporary files, please run:'
-       einfo "rm $LIST*.?_*"
-       show_unowned_files
-       exit $EMERGE_STATUS
-elif is_real_merge; then
-       trap_cmd() {
-               eerror "terminated. Please remove the temporary files manually:"
-               eerror "rm $LIST*.?_*"
-               exit 1
-       }
-       (( "${#SKIP_LIST[@]}" != 0 )) && list_skipped_packages
-       trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
-       einfo 'Build finished correctly. Removing temporary files...'
-       einfo
-       einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
-       einfo 'are fixed. If some inconsistency remains, it can be orphaned file, deep'
-       einfo 'dependency, binary package or specially evaluated library.'
-       if [[ -r "$LIST.4_package_owners" && -s "$LIST.4_package_owners" ]]; then
-       show_unowned_files
+       trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+
+       einfo 'All prepared. Starting rebuild'
+       echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST"
+
+       is_real_merge && countdown 10
+
+       # Link file descriptor #6 with stdin so --ask will work
+       exec 6<&0
+
+       # Run in background to correctly handle Ctrl-C
+       {
+               EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6
+               echo $? > "$STATUS_FILE"
+       } &
+       wait
+
+       # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
+       exec 0<&6 6<&-
+}
+##
+# Finish up
+cleanup() {
+       if (( $(<"$STATUS_FILE") != 0 )); then
+               ewarn
+               ewarn "$APP_NAME failed to emerge all packages."
+               ewarn 'you have the following choices:'
+               einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
+               einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
+               einfo "  (and remove $ORDER_FILE to be evaluated again)"
+               einfo '- Modify the above emerge command and run it manually.'
+               einfo '- Compile or unmerge unsatisfied packages manually,'
+               einfo '  remove temporary files, and try again.'
+               einfo '  (you can edit package/ebuild list first)'
+               einfo
+               einfo 'To remove temporary files, please run:'
+               einfo "rm ${WORKING_DIR}/*.rr"
+               show_unowned_files
+               exit $EMERGE_STATUS
+       elif is_real_merge; then
+               trap_cmd() {
+                       eerror "terminated. Please remove the temporary files manually:"
+                       eerror "rm ${WORKING_DIR}/*.rr"
+                       exit 1
+               }
+               [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
+               trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+               einfo 'Build finished correctly. Removing temporary files...'
+               einfo
+               einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
+               einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
+               einfo '  orphaned files'
+               einfo '  deep dependencies'
+               einfo "  packages installed outside of portage's control"
+               einfo '  specially-evaluated libraries'
+               if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
+                       show_unowned_files
+               fi
+               [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
+       else
+               einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'
        fi
-       [[ $KEEP_TEMP ]] || rm $LIST*.?_*
-else
-       einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'
-fi
+}
 
+main "$@"