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