Bug #239000 - Improve the message about 'remaining inconsistencies'.
[gentoolkit.git] / trunk / src / revdep-rebuild / revdep-rebuild
1 #!/bin/bash
2 # Copyright 1999-2008 Gentoo Foundation
3
4 # revdep-rebuild: Reverse dependency rebuilder.
5 # Original Author: Stanislav Brabec
6 # Rewrite Author: Michael A. Smith
7 # Current Maintainer: Paul Varner <fuzzyray@gentoo.org>
8
9 # TODO:
10 # - Use more /etc/init.d/functions.sh
11 # - Try to reduce the number of global vars
12
13 ##
14 # Global Variables:
15
16 # Must-be-blank:
17 unset GREP_OPTIONS
18
19 # Readonly variables:
20 declare -r APP_NAME="${0##*/}" # The name of this application
21 declare -r OIFS="$IFS"         # Save the IFS
22 declare -r     ENV_FILE=0_env.rr     # Contains environment variables
23 declare -r   FILES_FILE=1_files.rr   # Contains a list of files to search
24 declare -r  LDPATH_FILE=2_ldpath.rr  # Contains the LDPATH
25 declare -r  BROKEN_FILE=3_broken.rr  # Contains the list of broken files
26 declare -r  ERRORS_FILE=3_errors.rr  # Contains the ldd error output
27 declare -r     RAW_FILE=4_raw.rr     # Contains the raw list of packages
28 declare -r  OWNERS_FILE=4_owners.rr  # Contains the file owners
29 declare -r    PKGS_FILE=4_pkgs.rr    # Contains the unsorted bare package names
30 declare -r EBUILDS_FILE=4_ebuilds.rr # Contains the unsorted atoms
31                                      # (Appropriately slotted or versioned)
32 declare -r   ORDER_FILE=5_order.rr   # Contains the sorted atoms
33 declare -r  STATUS_FILE=6_status.rr  # Contains the ldd error output
34 declare -ra FILES=(
35         "$ENV_FILE"
36         "$FILES_FILE"
37         "$LDPATH_FILE"
38         "$BROKEN_FILE"
39         "$ERRORS_FILE"
40         "$RAW_FILE"
41         "$OWNERS_FILE"
42         "$PKGS_FILE"
43         "$EBUILDS_FILE"
44         "$ORDER_FILE"
45         "$STATUS_FILE"
46 )
47
48 # "Boolean" variables: Considered "true" if it has any value at all
49 # "True" indicates we should...
50 declare FULL_LD_PATH           # ...search across the COMPLETE_LD_LIBRARY_PATH
51 declare KEEP_TEMP              # ...not delete tempfiles from the current run
52 declare ORDER_PKGS             # ...sort the atoms in deep dependency order
53 declare PACKAGE_NAMES          # ...emerge by slot, not by versionated atom
54 declare RM_OLD_TEMPFILES       # ...remove tempfiles from prior runs
55 declare SEARCH_BROKEN          # ...search for broken libraries and binaries
56 declare VERBOSE                # ...give verbose output
57
58 # Globals that impact portage directly:
59 declare EMERGE_DEFAULT_OPTS    # String of options portage assumes to be set
60 declare EMERGE_OPTIONS         # Array of options to pass to portage
61 declare PORTAGE_NICENESS       # Renice to this value
62 declare PORTAGE_ROOT           # The root path for portage
63
64 # Customizable incremental variables:
65 # These variables can be prepended to either by setting the variable in
66 # your environment prior to execution, or by placing an entry in
67 # /etc/make.conf.
68 #
69 # An entry of "-*" means to clear the variable from that point forward.
70 # Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS
71 # to contain only /usr/bin
72 declare LD_LIBRARY_MASK  # Mask of specially evaluated libraries
73 declare SEARCH_DIRS      # List of dirs to search for executables and libraries
74 declare SEARCH_DIRS_MASK # List of dirs not to search
75
76 # Other globals:
77 declare OLDPROG                # Previous pass through the progress meter
78 declare EXACT_PKG              # Versionated atom to emerge
79 declare HEAD_TEXT              # Feedback string about the search
80 declare NOCOLOR                # Set to "true" not to output term colors
81 declare OK_TEXT                # Feedback about a search which found no errors
82 declare RC_NOCOLOR             # Hack to insure we respect NOCOLOR
83 declare REBUILD_LIST           # Array of atoms to emerge
84 declare SKIP_LIST              # Array of atoms that cannot be emerged (masked?)
85 declare SONAME                 # Soname/soname path pattern given on commandline
86 declare SONAME_SEARCH          # Value of SONAME modified to match ldd's output
87 declare WORKING_TEXT           # Feedback about the search
88 declare WORKING_DIR            # Working directory where cache files are kept
89
90 main() {
91         # preliminary setup
92         get_opts "$@"
93         setup_portage
94         setup_search_paths_and_masks
95         get_search_env
96         echo
97
98         # Search for broken binaries
99         get_files
100         get_ldpath
101         main_checks
102
103         # Associate broken binaries with packages to rebuild
104         if [[ $PACKAGE_NAMES ]]; then
105                 get_packages
106                 clean_packages
107                 assign_packages_to_ebuilds
108         else
109                 get_exact_ebuilds
110         fi
111
112         # Rebuild packages owning broken binaries
113         get_build_order
114         rebuild
115
116         # All done
117         cleanup
118 }
119 ##
120 # Refuse to delete anything before we cd to our tmpdir
121 # (See mkdir_and_cd_to_tmpdir()
122 rm() {
123         eerror "I was instructed to rm '$@'"
124         die 1 "Refusing to delete anything before changing to temporary directory."
125 }
126 : <<'EW'
127 ##
128 # GNU find has -executable, but if our users' finds do not have that flag
129 # we emulate it with this function. Also emulates -writable and -readable.
130 # Usage: find PATH ARGS -- use find like normal, except use -executable instead
131 # of various versions of -perm /+ blah blah and hacks
132 find() {
133         hash find || { die 1 'find not found!'; }
134         # We can be pretty sure find itself should be executable.
135         local testsubject="$(type -P find)"
136         if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
137                 unset -f find # We can just use the command find
138         elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
139                 find() {
140                         a=(${@//-executable/-perm \/u+x})
141                         a=(${a[@]//-writable/-perm \/u+w})
142                         a=(${a[@]//-readable/-perm \/r+w})
143                         command find "${a[@]}"
144                 }
145         elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
146                 find() {
147                         a=(${@//-executable/-perm +u+x})
148                         a=(${a[@]//-writable/-perm +u+w})
149                         a=(${a[@]//-readable/-perm +r+w})
150                         command find "${a[@]}"
151                 }
152         else # Last resort
153                 find() {
154                         a=(${@//-executable/-exec test -x '{}' \; -print})
155                         a=(${a[@]//-writable/-exec test -w '{}' \; -print})
156                         a=(${a[@]//-readable/-exec test -r '{}' \; -print})
157                         command find "${a[@]}"
158                 }
159         fi
160         find "$@"
161 }
162 EW
163
164 print_usage() {
165 cat << EOF
166 Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
167
168 Broken reverse dependency rebuilder.
169
170   -C, --nocolor        Turn off colored output
171   -d, --debug          Print way too much information (uses bash's set -xv)
172   -e, --exact          Emerge based on exact package version
173   -h, --help           Print this usage
174   -i, --ignore         Ignore temporary files from previous runs
175   -k, --keep-temp      Do not delete temporary files on exit
176   -L, --library NAME   Emerge existing packages that use the library with NAME
177       --library=NAME   NAME can be a full path to the library or a basic
178                        regular expression (man grep)
179   -l, --no-ld-path     Do not set LD_LIBRARY_PATH
180   -o, --no-order       Do not check the build order
181                        (Saves time, but may cause breakage.)
182   -p, --pretend        Do a trial run without actually emerging anything
183                        (also passed to emerge command)
184   -P, --no-progress    Turn off the progress meter
185   -q, --quiet          Be less verbose (also passed to emerge command)
186   -v, --verbose        Be more verbose (also passed to emerge command)
187
188 Calls emerge, options after -- are ignored by $APP_NAME
189 and passed directly to emerge.
190
191 Report bugs to <http://bugs.gentoo.org>
192 EOF
193 }
194 ##
195 # Usage: progress i n
196 #        i: current item
197 #        n: total number of items to process
198 progress() {
199         if [[ -t 1 ]]; then
200                 progress() {
201                         local curProg=$(( $1 * 100 / $2 ))
202                         (( curProg == OLDPROG )) && return # no change, output nothing
203                         OLDPROG="$curProg" # must be a global variable
204                         (( $1 == $2 )) && local lb=$'\n'
205                         echo -ne '\r                         \r'"[ $curProg% ] $lb"
206                 }
207                 progress $@
208         else # STDOUT is not a tty. Disable progress meter.
209                 progress() { :; }
210         fi
211 }
212 ##
213 # Usage: countdown n
214 #        n: number of seconds to count
215 countdown() {
216         local i
217         for ((i=1; i<$1; i++)); do
218                 echo -ne '\a.'
219                 ((i<$1)) && sleep 1
220         done
221         echo -e '\a.'
222 }
223 ##
224 # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
225 # (If any libs have whitespace in their filenames, someone needs punishment.)
226 clean_var() {
227         gawk 'BEGIN         {RS="[[:space:]]"}
228              /-\*/         {exit}
229              /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
230 }
231 ##
232 # Exit and optionally output to sterr
233 die() {
234         local status=$1
235         shift
236         eerror "$@"
237         exit $status
238 }
239 ##
240 # What to do when dynamic linking is consistent
241 clean_exit() {
242         if [[ ! $KEEP_TEMP ]]; then
243                 rm -f "${FILES[@]}"
244                 if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
245                         # Remove the working directory
246                         builtin cd; rmdir "$WORKING_DIR"
247                 fi
248         fi
249         echo
250         einfo "$OK_TEXT... All done. "
251         exit 0
252 }
253 ##
254 # Get the name of the package that owns a file or list of files given as args.
255 get_file_owner() {
256         local IFS=$'\n'
257         # ${*/%/ } adds a space to the end of each object name to prevent false
258         # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
259         find -L /var/db/pkg -name CONTENTS -print0 |
260                 xargs -0 grep -Fl "${*/%/ }" |
261                 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
262 }
263 ##
264 # Normalize some EMERGE_OPTIONS
265 normalize_emerge_opts() {
266         # Normalize some EMERGE_OPTIONS
267         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
268         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
269         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--verbose})
270 }
271 ##
272 # Use the color preference from portage
273 setup_color() {
274         # This should still work if NOCOLOR is set by the -C flag or in the user's
275         # environment.
276         export NOCOLOR=$(portageq envvar NOCOLOR)
277         [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
278         . /etc/init.d/functions.sh
279 }
280 ##
281 # Die if an argument is missing.
282 die_if_missing_arg() {
283         [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
284 }
285 ##
286 # Die because an option is not recognized.
287 die_invalid_option() {
288         # Can't use eerror and einfo because this gets called before function.sh
289         # is sourced
290         echo
291         echo  "Encountered unrecognized option $1." >&2
292         echo
293         echo  "$APP_NAME no longer automatically passes unrecognized options to portage."
294         echo  "Separate emerge-only options from revdep-rebuild options with the -- flag."
295         echo
296         echo  "For example, $APP_NAME -v -- --ask"
297         echo
298         echo  "See the man page or $APP_NAME -h for more detail."
299         echo
300         exit 1
301 }
302 ##
303 # Warn about deprecated options.
304 warn_deprecated_opt() {
305         # Can't use eerror and einfo because this gets called before function.sh
306         # is sourced
307         echo
308         echo "Encountered deprecated option $1." >&2
309         [[ $2 ]] && echo "Please use $2 instead." >&2
310 }
311 ##
312 # Get whole-word commandline options preceded by two dashes.
313 get_longopts() {
314         case $1 in
315                                                --nocolor) export NOCOLOR="yes";;
316                                               --no-color) warn_deprecated_opt "$1" "--nocolor"
317                                                           export NOCOLOR="yes";;
318                                                  --debug) set -xv;;
319                                                  --exact) unset PACKAGE_NAMES;;
320                                                   --help) print_usage
321                                                           exit 0;;
322                                                 --ignore) RM_OLD_TEMPFILES=1;;
323                                              --keep-temp) KEEP_TEMP=1;;
324                                              --library=*) # TODO: check for invalid values
325                                                           SONAME="${1#*=}"
326                                                           unset SEARCH_BROKEN;;
327                             --soname=*|--soname-regexp=*) # TODO: check for invalid values
328                                                           warn_deprecated_opt "${1%=*}" "--library"
329                                                           SONAME="${1#*=}"
330                                                           unset SEARCH_BROKEN;;
331                                                --library) # TODO: check for invalid values
332                                                           die_if_missing_arg $1 $2
333                                                           shift
334                                                           SONAME="$1"
335                                                           unset SEARCH_BROKEN;;
336                                 --soname|--soname-regexp) # TODO: check for invalid values
337                                                           warn_deprecated_opt "$1" "--library"
338                                                           die_if_missing_arg $1 $2
339                                                           shift
340                                                           SONAME="$1"
341                                                           unset SEARCH_BROKEN;;
342                                             --no-ld-path) unset FULL_LD_PATH;;
343                                               --no-order) unset ORDER_PKGS;;
344                                            --no-progress) progress() { :; };;
345                                                --pretend) EMERGE_OPTIONS+=("--pretend");;
346                                                  --quiet) echo_v() { :; }
347                                                           progress() { :; }
348                                                           quiet=1
349                                                           EMERGE_OPTIONS+=($1);;
350                                                --verbose) VERBOSE=1
351                                                           EMERGE_OPTIONS+=("--verbose");;
352                                          --extra-verbose) warn_deprecated_opt "$1" "--verbose"
353                                                           VERBOSE=1
354                                                           EMERGE_OPTIONS+=("--verbose");;
355                                          --package-names) # No longer used, since it is the
356                                                           # default. We accept it for
357                                                           # backwards compatibility.
358                                                           warn_deprecated_opt "$1"
359                                                           PACKAGE_NAMES=1;;
360                                                        *) die_invalid_option $1;;
361         esac
362 }
363
364 ##
365 # Get single-letter commandline options preceded by a single dash.
366 get_shortopts() {
367         local OPT OPTSTRING OPTARG OPTIND
368         while getopts ":CdehikL:loPpqu:vX" OPT; do
369                 case "$OPT" in
370                         C) # TODO: Match syntax with the rest of gentoolkit
371                            export NOCOLOR="yes";;
372                         d) set -xv;;
373                         e) unset PACKAGE_NAMES;;
374                         h) print_usage
375                            exit 0;;
376                         i) RM_OLD_TEMPFILES=1;;
377                         k) KEEP_TEMP=1;;
378                         L) # TODO: Check for invalid values
379                            SONAME="${OPTARG#*=}"
380                            unset SEARCH_BROKEN;;
381                         l) unset FULL_LD_PATH;;
382                         o) unset ORDER_PKGS;;
383                         P) progress() { :; };;
384                         p) EMERGE_OPTIONS+=("--pretend");;
385                         q) echo_v() { :; }
386                            progress() { :; }
387                            quiet=1
388                            EMERGE_OPTIONS+=("--quiet");;
389                         v) VERBOSE=1
390                            EMERGE_OPTIONS+=("--verbose");;
391                         X) # No longer used, since it is the default.
392                            # We accept it for backwards compatibility.
393                            warn_deprecated_opt "-X"
394                            PACKAGE_NAMES=1;;
395                         *) die_invalid_option "-$OPTARG";;
396                 esac
397         done
398 }
399 ##
400 # Get command-line options.
401 get_opts() {
402         local avoid_utils
403         local -a args
404         echo_v() { ewarn "$@"; }
405         unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
406         ORDER_PKGS=1
407         PACKAGE_NAMES=1
408         SONAME="not found"
409         SEARCH_BROKEN=1
410         FULL_LD_PATH=1
411         while [[ $1 ]]; do
412                 case $1 in
413                         --) shift
414                             EMERGE_OPTIONS+=("$@")
415                             break;;
416                         -*) while true; do
417                               args+=("$1")
418                               shift
419                               [[ ${1:--} = -* ]] && break
420                             done
421                             if [[ ${args[0]} = --* ]]; then
422                               get_longopts  "${args[@]}"
423                             else
424                               get_shortopts "${args[@]}"
425                             fi;;
426                          *) die_invalid_option "$1";;
427                 esac
428                 unset args
429         done
430
431         setup_color
432         normalize_emerge_opts
433
434         # If the user is not super, add --pretend to EMERGE_OPTIONS
435         if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
436                 ewarn "You are not superuser. Adding --pretend to emerge options."
437                 EMERGE_OPTIONS+=(--pretend)
438         fi
439 }
440 ##
441 # Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
442 is_real_merge() {
443         [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
444            ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
445 }
446 ##
447 # Clean up temporary files and exit
448 cleanup_and_die() {
449         rm -f "$@"
450         die 1 "  ...terminated. Removing incomplete $@."
451 }
452 ##
453 # Clean trap
454 clean_trap() {
455         trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
456         rm -f "$@"
457 }
458 ##
459 # Returns 0 if the first arg is found in the remaining args, 1 otherwise
460 # (Returns 2 if given fewer than 2 arguments)
461 has() {
462         (( $# > 1 )) || return 2
463         local IFS=$'\a' target="$1"
464         shift
465         [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
466 }
467 ##
468 # Dies when it can't change directories
469 cd() {
470         if builtin cd -P "$@"; then
471                 if [[ $1 != $PWD ]]; then
472                         # Some symlink malfeasance is going on
473                         die 1 "Working directory expected to be $1, but it is $PWD"
474                 fi
475         else
476                 die 1 "Unable to change working directory to '$@'"
477         fi
478 }
479 ##
480 # Tries not to delete any files or directories it shouldn't
481 setup_rm() {
482         ##
483         # Anything in the FILES array in tmpdir is fair game for removal
484         rm() {
485                 local i IFS=$'\a'
486                 [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
487                 case $@ in
488                         */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
489                 esac
490                 for i; do
491                         # Don't delete files that are not listed in the array
492                         # Allow no slashes or recursive deletes at all.
493                         case $i in
494                                 */*|-*r*|-*R*) :;;        # Not OK
495                                            -*) continue;; # OK
496                         esac
497                         has "$i" "${FILES[@]}" && continue
498                         die 1 "Oops, I'm not allowed to delete that. ($@)"
499                 done
500                 command rm "$@"
501         }
502         # delete this setup function so it's harmless to re-run
503         setup_rm() { :; }
504 }
505 ##
506 # Make our temporary files directory
507 # $1 - directory name
508 # $2 - user name
509 verify_tmpdir() {
510         umask 007 || die $? "Unable to set umask 007"
511         if [[ ! $1 ]]; then
512                 die 1 'Temporary file path is unset! (This is a bug.)'
513         elif [[ -d $1 ]]; then
514                 # HACK: I hate using find this way
515                 if [[ $(find "$1" -type d ! \( -user $2 -perm -0700 \) ) ]]; then
516                         eerror "Incorrect permissions on $1"
517                         eerror "or at least one file in $1."
518                         die 1  "Please make sure it's not a symlink and then remove it."
519                 fi
520                 cd "$1"
521         else
522                 die 1 "Unable to find a satisfactory location for temporary files ($1)"
523         fi
524         [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
525         setup_rm
526 }
527 get_search_env() {
528         local new_env
529         local old_env
530         local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
531         # Find a place to put temporary files
532         if [[ "$uid" == "root" ]]; then
533                 local tmp_target="/var/cache/${APP_NAME}"
534         else
535                 local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
536         fi
537
538         # From here on all work is done inside the temporary directory
539         verify_tmpdir "$tmp_target" "$uid"
540         WORKING_DIR="$tmp_target"
541
542         if [[ $SEARCH_BROKEN ]]; then
543                 SONAME_SEARCH="$SONAME"
544                 HEAD_TEXT="broken by a package update"
545                 OK_TEXT="Dynamic linking on your system is consistent"
546                 WORKING_TEXT="consistency"
547         else
548                 # first case is needed to test against /path/to/foo.so
549                 if [[ $SONAME = /* ]]; then
550                         # Set to "<space>$SONAME<space>"
551                         SONAME_SEARCH=" $SONAME "
552                         # Escape the "/" characters
553                         SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
554                 else
555                         # Set to "<tab>$SONAME<space>"
556                         SONAME_SEARCH=$'\t'"$SONAME "
557                 fi
558                 HEAD_TEXT="using $SONAME"
559                 OK_TEXT="There are no dynamic links to $SONAME"
560                 unset WORKING_TEXT
561         fi
562
563         # If any of our temporary files are older than 1 day, remove them all
564         if [[ ! $KEEP_TEMP ]]; then
565                 while read; do
566                         RM_OLD_TEMPFILES=1
567                         break
568                 done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
569         fi
570
571         # Compare old and new environments
572         # Don't use our previous files if environment doesn't match
573         new_env=$(
574                 # We do not care if these emerge options change
575                 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
576                 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
577                 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
578                 cat <<- EOF
579                         SEARCH_DIRS="$SEARCH_DIRS"
580                         SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
581                         LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
582                         PORTAGE_ROOT="$PORTAGE_ROOT"
583                         EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
584                         ORDER_PKGS="$ORDER_PKGS"
585                         FULL_LD_PATH="$FULL_LD_PATH"
586                 EOF
587         )
588         if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
589                 old_env=$(<"$ENV_FILE")
590                 if [[ $old_env != $new_env ]]; then
591                         ewarn 'Environment mismatch from previous run, deleting temporary files...'
592                         RM_OLD_TEMPFILES=1
593                 fi
594         else
595                 # No env file found, silently delete any other tempfiles that may exist
596                 RM_OLD_TEMPFILES=1
597         fi
598
599         # If we should remove old tempfiles, do so
600         if [[ $RM_OLD_TEMPFILES ]]; then
601                 rm -f "${FILES[@]}"
602         else
603                 for file in "${FILES[@]}"; do
604                         if [ -e "$file" ]; then
605                                 chown ${uid}:portage "$file"
606                                 chmod 700 "$file"
607                         fi
608                 done
609         fi
610
611         # Save the environment in a file for next time
612         echo "$new_env" > "$ENV_FILE"
613
614         [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
615
616         echo
617         einfo "Checking reverse dependencies"
618         einfo "Packages containing binaries and libraries $HEAD_TEXT"
619         einfo "will be emerged."
620 }
621
622 get_files() {
623         einfo "Collecting system binaries and libraries"
624         if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
625                 einfo "Found existing $FILES_FILE"
626         else
627                 # Be safe and remove any extraneous temporary files
628                 # Don't remove 0_env.rr - The first file in the array
629                 rm -f "${FILES[@]:1}"
630
631                 clean_trap "$FILES_FILE"
632
633                 if [[ $SEARCH_DIRS_MASK ]]; then
634                         findMask=($SEARCH_DIRS_MASK)
635                         findMask="${findMask[@]/#/-o -path }"
636                         findMask="( ${findMask#-o } ) -prune -o"
637                 fi
638                 # TODO: Check this -- afaict SEARCH_DIRS isn't an array, so this should just be $SEARCH_DIRS?
639                 find ${SEARCH_DIRS[@]} $findMask -type f \( -perm -u+x -o -perm -g+x -o -perm -o+x -o \
640                         -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
641                         sort -u > "$FILES_FILE" ||
642                         die $? "find failed to list binary files (This is a bug.)"
643                 einfo "Generated new $FILES_FILE"
644         fi
645 }
646 get_ldpath() {
647         local COMPLETE_LD_LIBRARY_PATH
648         [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
649         einfo 'Collecting complete LD_LIBRARY_PATH'
650         if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
651                 einfo "Found existing $LDPATH_FILE."
652         else
653                 clean_trap "$LDPATH_FILE"
654                 # Ensure that the "trusted" lib directories are at the start of the path
655                 COMPLETE_LD_LIBRARY_PATH=(
656                         /lib*
657                         /usr/lib*
658                         $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
659                         $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
660                 )
661                 IFS=':'
662                 COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
663                 IFS="$OIFS"
664                 echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
665                 einfo "Generated new $LDPATH_FILE"
666         fi
667 }
668 main_checks() {
669         local target_file
670         local -a files
671         local i=0
672         local ldd_output
673         local ldd_status
674         local numFiles
675         local COMPLETE_LD_LIBRARY_PATH
676         if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
677                 [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
678                         die 1 "Unable to find $LDPATH_FILE"
679                 COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
680         fi
681         einfo "Checking dynamic linking $WORKING_TEXT"
682         if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
683                 einfo "Found existing $BROKEN_FILE."
684         else
685                 clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
686                 files=($(<"$FILES_FILE"))
687                 numFiles="${#files[@]}"
688                 for target_file in "${files[@]}"; do
689                         if [[ $target_file != *.la ]]; then
690                                 # Note: double checking seems to be faster than single with complete path
691                                 # (special add ons are rare).
692                                 ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
693                                 ldd_status=$? # TODO: Check this for problems with sort
694                                 # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
695                                 if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
696                                         grep -q "$SONAME_SEARCH"; then
697                                         if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
698                                                 if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
699                                                         grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
700                                                         # FIXME: I hate duplicating code
701                                                         # Only build missing direct dependencies
702                                                         MISSING_LIBS=$(
703                                                                 expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
704                                                                 sed -n "$expr" <<< "$ldd_output"
705                                                         )
706                                                         REQUIRED_LIBS=$(
707                                                                 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
708                                                                 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
709                                                         )
710                                                         MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
711                                                         if [[ $MISSING_LIBS ]]; then
712                                                                 echo "obj $target_file" >> "$BROKEN_FILE"
713                                                                 echo_v "  broken $target_file (requires $MISSING_LIBS)"
714                                                         fi
715                                                 fi
716                                         else
717                                                 # FIXME: I hate duplicating code
718                                                 # Only rebuild for direct dependencies
719                                                 MISSING_LIBS=$(
720                                                         expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
721                                                         sort -u <<< "$ldd_output" | sed -n "$expr"
722                                                 )
723                                                 REQUIRED_LIBS=$(
724                                                         expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
725                                                         objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
726                                                 )
727                                                 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
728                                                 if [[ $MISSING_LIBS ]]; then
729                                                         echo "obj $target_file" >> "$BROKEN_FILE"
730                                                         if [[ $SEARCH_BROKEN ]]; then
731                                                                 echo_v "  broken $target_file (requires $MISSING_LIBS)"
732                                                         else
733                                                                 echo_v "  found $target_file"
734                                                         fi
735                                                 fi
736                                         fi
737                                 fi
738                         elif [[ $SEARCH_BROKEN ]]; then
739                                 # Look for broken .la files
740                                 for depend in $(
741                                         gawk -F"[=']" '/^dependency_libs/{
742                                                 gsub("^-[^[:space:]]*", "", $3);
743                                                 gsub("[[:space:]]-[^[:space:]]*", "", $3);
744                                                 print $3
745                                         }' "$target_file"
746                                 ); do
747                                         if [[ $depend = /* && ! -e $depend ]]; then
748                                                 echo "obj $target_file" >> "$BROKEN_FILE"
749                                                 echo_v "  broken $target_file (requires $depend)"
750                                         fi
751                                 done
752                         fi
753                         [[ $VERBOSE ]] &&
754                                 progress $((++i)) $numFiles $target_file ||
755                                 progress $((++i)) $numFiles
756                 done
757                 if [[ $SEARCH_BROKEN ]]; then
758                         # Look for missing version
759                         while read target_file; do
760                                 echo "obj $target_file" >> "$BROKEN_FILE"
761                                 echo_v "  broken $target_file (no version information available)"
762                         done < <(
763                                 # Regexify LD_LIBRARY_MASK. Exclude it from the search.
764                                 LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
765                                 gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
766                                         /no version information available/ && $0 !~ ldmask {
767                                                 gsub(/[()]/, "", $NF)
768                                                 if (seen[$NF]++)  next
769                                                 print $NF
770                                         }' "$ERRORS_FILE"
771                         )
772                 fi
773                 [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
774                 sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
775                 einfo "Generated new $BROKEN_FILE"
776         fi
777 }
778 get_packages() {
779         local target_file
780         local EXACT_PKG
781         local PKG
782         local obj
783         einfo 'Assigning files to packages'
784         if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
785                 einfo "Found existing $RAW_FILE"
786         else
787                 clean_trap "$RAW_FILE" "$OWNERS_FILE"
788                 while read obj target_file; do
789                         EXACT_PKG=$(get_file_owner $target_file)
790                         if [[ $EXACT_PKG ]]; then
791                                 # Strip version information
792                                 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
793                                 PKG="${PKG%-*}"
794                                 echo "$EXACT_PKG" >> "$RAW_FILE"
795                                 echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
796                                 echo_v "  $target_file -> $PKG"
797                         else
798                                 ewarn " !!! $target_file not owned by any package is broken !!!"
799                                 echo "$target_file -> (none)" >> "$OWNERS_FILE"
800                                 echo_v "  $target_file -> (none)"
801                         fi
802                 done < "$BROKEN_FILE"
803                 einfo "Generated new $RAW_FILE and $OWNERS_FILE"
804         fi
805         # if we find '(none)' on every line, exit out
806         if ! grep -qvF '(none)' "$OWNERS_FILE"; then
807                 ewarn "Found some broken files, but none of them were associated with known packages"
808                 ewarn "Unable to proceed with automatic repairs."
809                 ewarn "The broken files are listed in $OWNERS_FILE"
810                 if [[ $VERBOSE ]]; then
811                         ewarn "The broken files are:"
812                         while read filename junk; do
813                                 ewarn "  $filename"
814                         done < "$OWNERS_FILE"
815                 fi
816                 exit 0 # FIXME: Should we exit 1 here?
817         fi
818 }
819 clean_packages() {
820         einfo 'Cleaning list of packages to rebuild'
821         if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
822                 einfo "Found existing $PKGS_FILE"
823         else
824                 sort -u "$RAW_FILE" > "$PKGS_FILE"
825                 einfo "Generated new $PKGS_FILE"
826         fi
827 }
828 assign_packages_to_ebuilds() {
829         local EXACT_PKG
830         local PKG
831         local SLOT
832         einfo 'Assigning packages to ebuilds'
833         if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
834                 einfo "Found existing $EBUILDS_FILE"
835         elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
836                         clean_trap "$EBUILDS_FILE"
837                         while read EXACT_PKG; do
838                                 # Get the slot
839                                 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
840                                 PKG="${PKG%-*}"
841                                 SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
842                                 echo "$PKG:$SLOT"
843                         done < "$PKGS_FILE" > "$EBUILDS_FILE"
844                         einfo "Generated new $EBUILDS_FILE"
845         else
846                 einfo 'Nothing to rebuild.'
847                 die 1 '(The program should have already quit, so this is a minor bug.)'
848         fi
849 }
850 get_exact_ebuilds() {
851         einfo 'Assigning files to ebuilds'
852         if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
853                 einfo "Found existing $EBUILDS_FILE"
854         elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
855                 rebuildList=" $(<"$BROKEN_FILE") "
856                 rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
857                 get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
858                 einfo "Generated new $EBUILDS_FILE"
859         else
860                 einfo 'Nothing to rebuild.'
861                 die 1 '(The program should have already quit, so this is a minor bug.)'
862         fi
863 }
864 list_skipped_packages() {
865         ewarn
866         ewarn 'Portage could not find any version of the following packages it could build:'
867         ewarn "${SKIP_LIST[@]}"
868         ewarn
869         ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
870         ewarn 'Try to emerge them manually.'
871         ewarn
872 }
873 get_build_order() {
874         local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
875         local RAW_REBUILD_LIST
876         local REBUILD_GREP
877         local i
878         if [[ ! $ORDER_PKGS ]]; then
879                 einfo 'Skipping package ordering'
880                 return
881         fi
882         einfo 'Evaluating package order'
883         if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
884                 einfo "Found existing $ORDER_FILE"
885         else
886                 clean_trap "$ORDER_FILE"
887                 RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
888                 if [[ $RAW_REBUILD_LIST ]]; then
889                         export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
890                         RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
891                         # If PACKAGE_NAMES is defined we're using slots, not versions
892                         if [[ $PACKAGE_NAMES ]]; then
893                                 # Eliminate atoms that can't be built
894                                 for i in "${!RAW_REBUILD_LIST[@]}"; do
895                                         if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
896                                                 portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
897                                                 SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
898                                         fi
899                                         unset RAW_REBUILD_LIST[i]
900                                 done
901                                 # If RAW_REBUILD_LIST is empty, then we have nothing to build.
902                                 if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
903                                         if (( ${#SKIP_LIST[@]} == 0 )); then
904                                                 ewarn "The list of packages to skip is empty, but there are no"
905                                                 ewarn "packages listed to rebuild either. (This is a bug.)"
906                                         else
907                                                 list_skipped_packages
908                                         fi
909                                         die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
910                                 fi
911                         fi
912                         RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
913                         REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
914                         if (( ${PIPESTATUS[0]} == 0 )); then
915                                 emerge --deep $RAW_REBUILD_LIST |
916                                         sed 's/\[[^]]*\]//g' |
917                                         grep -F "$REBUILD_GREP" > "$ORDER_FILE"
918                         fi
919
920                         # Here we use the PIPESTATUS from the second emerge, the --deep one.
921                         if (( ${PIPESTATUS[0]} != 0 )); then
922                                         eerror
923                                         eerror 'Warning: Failed to resolve package order.'
924                                         eerror 'Will merge in arbitrary order'
925                                         eerror
926                                         cat <<- EOF
927                                                 Possible reasons:
928                                                 - An ebuild is no longer in the portage tree.
929                                                 - An ebuild is masked, use /etc/portage/packages.keyword
930                                                         and/or /etc/portage/package.unmask to unmask it
931                                         EOF
932                                         countdown 5
933                                         rm -f "$ORDER_FILE"
934                         fi
935                         export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
936                 else
937                         einfo 'Nothing to rebuild.'
938                         die 1 '(The program should have already quit, so this is a minor bug.)'
939                 fi
940         fi
941         [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
942 }
943
944 show_unowned_files() {
945         if grep -qF '(none)' "$OWNERS_FILE"; then
946                 ewarn "Found some broken files that weren't associated with known packages"
947                 ewarn "The broken files are:"
948                 while read filename junk; do
949                         [[ $junk = *none* ]] && ewarn "  $filename"
950                 done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
951         fi
952 }
953 ##
954 # Setup portage and the search paths
955 setup_portage() {
956         local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
957         PORTAGE_ROOT=$(portageq envvar ROOT)
958
959         # Obey PORTAGE_NICENESS
960         if [[ $PORTAGE_NICENESS ]]; then
961                 renice $PORTAGE_NICENESS $$ > /dev/null
962                 # Since we have already set our nice value for our processes,
963                 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
964                 export PORTAGE_NICENESS="0"
965         fi
966
967         PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
968 }
969
970 ##
971 # Setup the paths to search (and filter the ones to avoid)
972 setup_search_paths_and_masks() {
973         local configfile sdir mdir skip_me filter_SEARCH_DIRS
974
975         einfo "Configuring search environment for $APP_NAME"
976
977         # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
978         # portage, and the environment
979
980         # Read the incremental variables from environment and portage
981         # Until such time as portage supports these variables as incrementals
982         # The value will be what is in /etc/make.conf
983         SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
984         SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
985         LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
986
987         # Add the defaults
988         if [[ -d /etc/revdep-rebuild ]]; then
989                 for configfile in /etc/revdep-rebuild/*; do
990                         SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
991                         SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
992                         LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
993                 done
994         else
995                 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
996                 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
997                 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
998         fi
999
1000         # Get the ROOTPATH and PATH from /etc/profile.env
1001         if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1002                 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1003         fi
1004
1005         # Get the directories from /etc/ld.so.conf
1006         if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1007                 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1008         fi
1009
1010         # Set the final variables
1011         SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1012         SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1013         LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1014         # Filter masked paths from SEARCH_DIRS
1015         for sdir in ${SEARCH_DIRS} ; do
1016                 skip_me=
1017                 for mdir in ${SEARCH_DIRS_MASK}; do
1018                         [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1019                 done
1020                 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1021         done
1022         SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1023         [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1024 }
1025 ##
1026 # Rebuild packages owning broken binaries
1027 rebuild() {
1028         if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1029                 REBUILD_LIST=( $(<"$LIST.5_order") )
1030                 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1031         else
1032                 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1033         fi
1034
1035         trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1036
1037         einfo 'All prepared. Starting rebuild'
1038         echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST"
1039
1040         is_real_merge && countdown 10
1041
1042         # Link file descriptor #6 with stdin so --ask will work
1043         exec 6<&0
1044
1045         # Run in background to correctly handle Ctrl-C
1046         {
1047                 EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6
1048                 echo $? > "$STATUS_FILE"
1049         } &
1050         wait
1051
1052         # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1053         exec 0<&6 6<&-
1054 }
1055 ##
1056 # Finish up
1057 cleanup() {
1058         if (( $(<"$STATUS_FILE") != 0 )); then
1059                 ewarn
1060                 ewarn "$APP_NAME failed to emerge all packages."
1061                 ewarn 'you have the following choices:'
1062                 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1063                 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1064                 einfo "  (and remove $ORDER_FILE to be evaluated again)"
1065                 einfo '- Modify the above emerge command and run it manually.'
1066                 einfo '- Compile or unmerge unsatisfied packages manually,'
1067                 einfo '  remove temporary files, and try again.'
1068                 einfo '  (you can edit package/ebuild list first)'
1069                 einfo
1070                 einfo 'To remove temporary files, please run:'
1071                 einfo "rm ${WORKING_DIR}/*.rr"
1072                 show_unowned_files
1073                 exit $EMERGE_STATUS
1074         elif is_real_merge; then
1075                 trap_cmd() {
1076                         eerror "terminated. Please remove the temporary files manually:"
1077                         eerror "rm ${WORKING_DIR}/*.rr"
1078                         exit 1
1079                 }
1080                 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1081                 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1082                 einfo 'Build finished correctly. Removing temporary files...'
1083                 einfo
1084                 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1085                 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1086                 einfo '  orphaned files'
1087                 einfo '  deep dependencies'
1088                 einfo "  packages installed outside of portage's control"
1089                 einfo '  specially-evaluated libraries'
1090                 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1091                         show_unowned_files
1092                 fi
1093                 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1094         else
1095                 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'
1096         fi
1097 }
1098
1099 main "$@"