#!/bin/bash # # Copyright © Ed Catmur ¡børk børk! # This program is distributed under the terms of the GPL version 2. # # Parts inspired by qpkg, copyright © gentoo.org # It is believed all code copyright gentoo.org has been replaced by my own # (better) algorithms. :P PROG="pruneworld" DESC="cut your world down to size" VERSION=0.1.12c TAG="Nothing But Cats" shopt -s extglob #Set up colors NO=$'\x1b[0;0m' BR=$'\x1b[0;01m' RD=$'\x1b[31;01m' Rd=$'\x1b[00;31m' GR=$'\x1b[32;01m' Gr=$'\x1b[00;32m' YL=$'\x1b[33;01m' Yl=$'\x1b[00;33m' BL=$'\x1b[34;01m' Bl=$'\x1b[00;34m' FC=$'\x1b[35;01m' Fc=$'\x1b[00;35m' CY=$'\x1b[36;01m' Cy=$'\x1b[00;36m' COLUMNS=${COLUMNS:-80} spaces=$(for ((i=0;i # Licensed under the GPL, version 2 or later. # Some notes. # The bash language is incredible. Imperative, but with (as good as) first # class functions. Pipe-based (and what other language has that?). # Even OO is possible with a little effort. # A little behind in the performance stakes, but for sorting and searching # the greatest tools ever written (GNU textutils) are a fork() away. # Conventions: # Whenever you see "mlsr" that means a full version specifier: # mega.macro.major.minor.micro(letter)-status-revision # When constructing pipelines, do not leave | tokens as the last character of # the feeding line; they should begin the next line. Similar for && and ||. # Keep if and then, for/while and do, on the same line where possible # (think 1TBS) # needle is being sought (in a haystack, normally) # A useful trick to stack e.g. USE flags # 0. Concatenate the lists, from highest to lowest priority (i.e. in reverse # order of application) function stacking_sort() { discard_negatives="$1" # 1. Transform removals from `-foo' to `foo -': sed 's/^-\(.*\)$/\1 -/' | \ # 2. Pipe the resulting mess through the `stacking sort': sort -s -k1,1 -u | \ # this is a stable sort on first column, unique i.e. output first for each key # 3. Finally reverse the transform: if [[ "$discard_negatives" ]]; then sed '/ -$/d' else sed 's/^\(.*\) -$/-\1/' fi # Example: # AFTER 0. AFTER 1. AFTER 2. AFTER 3. # foo foo foo foo # -bar bar - bar - -bar (if not $1) # -foo foo - # baz baz baz baz # bar bar } function kill_comments_and_blanks() { sed 's/#.*//;/^$/d' "$@" } # Neither POSIX [ nor the bash [[ operator accept the output of wildcard # expansion when testing for file existence. This is damn annoying, as we # often want to test for the existence of foo* and don't care whether there # are 1 or 100 files that match that wildcard. # So, we use the standard Lisp functional programming constructs: function car() { echo "$1"; } function cdr() { shift; echo "$@"; } function abl() { echo "${@:1:$(($#-1))}"; } function has() { a="$1"; shift for x in "${@}"; do [[ "${x}" == "${a}" ]] && return 0 done return 1 } function pads() { n=$1; shift; s="$*"; ((n=n<${#s}?0:n-${#s})) echo "${spaces:0:$n}" } function pad() { echo "$2$(pads $1 "$2")" } function rpad() { echo "$(pads $1 "$2")$2" } function format_error() { fmt -w $((COLUMNS-4)) \ | while read line; do echo ${RD}!!!${NO} "$line"; done } function format_cpv() { cpv="$1"; cat="${cpv%/*}"; pv="${cpv#$cat/}" cat_col=${2:-CY}; pv_col=${3:-GR} echo ${!cat_col}"$cat"${NO}/${!pv_col}"$pv"${NO} } function format_taint() { for tok in $*; do if [[ ${tok%?} == ${tok} ]]; then echo -n "$tok"' ' elif [[ ${tok:0:1} == "!" ]]; then echo -n "${BL}${tok%?}${NO}"'? ' else echo -n "${RD}${tok%?}${NO}"'? ' fi done } function format_depatom() { atom=$1; asvirtual=$2 cpvs=${atom/#[<>]=}; cpvs=${cpvs/#[=~<>]}; cpvs=${cpvs#\*}; vs=${atom%$cpvs} cpv=${cpvs%\*}; star=${cpvs#$cpv} [[ "$asvirtual" ]] && col=FC || col=Gr echo $vs${!col}$cpv${NO}${star} } # Structure of /var/cache/edb/dep: # # In portage 2.0.50, all cache files are /var/cache/edb/dep/${P} # In portage 2.0.51, they are /var/cache/edb/dep/${TREE}/${P} # e.g. /var/cache/edb/dep/usr/portage/x11-base/xorg-x11-6.7.0 # # The files themselves have a simple line-based structure: # 1. DEPEND # 2. RDEPEND # 3. SLOT # 4. SRC_URI # 5. RESTRICT # 6. HOMEPAGE # 7. LICENSE # 8. DESCRIPTION # 9. KEYWORDS # 10. INHERITED # 11. IUSE # 12. CDEPEND # 13. PDEPEND # 14. PROVIDE # 15-22. # # See /usr/lib/portage/bin/ebuild.sh @@ 1516: for myarg in $*; do... depend)... # and /usr/lib/portage/pym/portage.py @@ 4844: auxdbkeys=[...] function grab_setting() { name="$1"; default="$2" [[ "${!name+ISSET}" ]] && return export $name="$( eval $name="$default" [[ -r /etc/make.globals ]] \ && source /etc/make.globals [[ -r ${PORTDIR-/usr/portage}/profiles/base/make.defaults ]] \ && source ${PORTDIR-/usr/portage}/profiles/base/make.defaults [[ -r /etc/make.profile/make.defaults ]] \ && source /etc/make.profile/make.defaults [[ -r /etc/make.conf ]] \ && source /etc/make.conf echo ${!name} )" } grab_setting PORTDIR /usr/portage grab_setting DISTDIR ${PORTDIR}/distfiles grab_setting PKGDIR ${PORTDIR}/packages grab_setting PORT_LOGDIR /var/log/portage grab_setting PORTDIR_OVERLAY /usr/local/portage grab_setting PORTAGE_TMPDIR /var/tmp grab_setting CCACHE_DIR $PORTAGE_TMPDIR/ccache grab_setting CHOST i386-pc-linux-gnu grab_setting ARCH x86 grab_setting ACCEPT_KEYWORDS $ARCH portage_trees="$PORTDIR_OVERLAY $PORTDIR " accept_keywords="$ARCH $ACCEPT_KEYWORDS" # Version specifier: # ([[:digit:]]+.)*[[:digit:]]+[[:lower:]]?(_(alpha|beta|pre|rc|p)[[:digit:]]*)?(-r[[:digit:]]+)? # as: (...,mega,macro),major,(minor,micro,nano,...),(letter),(status(number)),(ebuild revision) # e.g. 3.57.2.20030722c_beta37-r12 VER_SHPAT="*(+([[:digit:]]).)+([[:digit:]])?([[:lower:]])?(_@(alpha|beta|pre|rc|p)*([[:digit:]]))?(-r+([[:digit:]]))" VER_REGEXP="\([[:digit:]]\+.\)*[[:digit:]]\+[[:lower:]]\?\(_\(alpha\|beta\|pre\|rc\|p\)[[:digit:]]*\)\?\(-r[[:digit:]]\+\)\?" function get_contingent_virtuals() { if [[ -f /var/cache/edb/virtuals ]]; then cat /var/cache/edb/virtuals return fi find /var/db/pkg -type d -mindepth 2 -maxdepth 2 \ | cut -c13- \ | sort \ | while read cpv; do for provide in $(provide_for $cpv); do echo $provide ${cpv/%-$VER_SHPAT} done done } allvirtuals=$( ( cat /etc/portage/virtuals get_contingent_virtuals cat /etc/portage/profile/virtuals /etc/make.profile/virtuals /usr/portage/profiles/base/virtuals ) 2>/dev/null \ | sed 's/#.*//;/^$/d' \ | sort -s -k1,1 \ | ( virtual="" while read newv packages; do [[ "$newv" == "$virtual" ]] \ || echo -n ${virtual:+$'\n'}$newv echo -n '' $packages virtual=$newv done echo ) ) allprofilepackages=$( cat /etc/portage/profile/packages /etc/make.profile/packages $PORTDIR/profiles/base/packages 2>/dev/null | kill_comments_and_blanks | stacking_sort yes ) alluse=$( ( echo $ARCH echo $USE ( source /etc/make.conf; echo $USE ) ( source /etc/make.profile/make.defaults; echo $USE ) cat /etc/make.profile/use.defaults $PORTDIR/profiles/base/use.defaults \ | sed 's/#.*//;/^$/d' \ | while read use package; do [[ -z "${package}" || -d /var/db/pkg/${package} || -d $(car /var/db/pkg/${package}-$VER_SHPAT) ]] && echo $use done ) \ | sed 's/[[:space:]]\+/\n/g' \ | stacking_sort yes \ | sed ':a;N;s/\n/ /;ba' ) # Compare version specifiers s1, s2. Returns <0, 0, >0 if s1 is resp lower, # equal, higher than s2. Note that <0 actually means >127 (-1 -> 255, etc) # 0: equal; 1: differ in erev; 2: differ in status number; 3: differ in status; # 4: differ in letter; 5: differ in mmm length; 6: differ in mmm # status code number: alpha->0, beta->1, pre->2, rc->3, (none)->4, p->5 function comm_ver() { s1_mlsr="$1"; s2_mlsr="$2" [[ "$s1_mlsr" == "$s2_mlsr" ]] && return 0 s1_mls="${s1_mlsr/%-r+([[:digit:]])}"; s1_r="${s1_mlsr#${s1_mls}}" s1_r="${s1_r/#-r*(0)}" s1_ml="${s1_mls/%_@(alpha|beta|pre|rc|p)*([[:digit:]])}" s1_s="${s1_mls#${s1_ml}}"; s1_sc="${s1_s/%*([[:digit:]])}" s1_scn="$((6-${#s1_sc}))"; [[ $s1_scn -ge 4 ]] && s1_scn=$((10-$s1_scn)) s1_sn="${s1_s#${s1_sc}}"; s1_m="${s1_ml/%[[:lower:]]}" s1_l="${s1_ml#${s1_m}}"; s1_m=( ${s1_m//./ } ) s2_mls="${s2_mlsr/%-r+([[:digit:]])}"; s2_r="${s2_mlsr#${s2_mls}}" s2_r="${s2_r/#-r*(0)}" s2_ml="${s2_mls/%_@(alpha|beta|pre|rc|p)*([[:digit:]])}" s2_s="${s2_mls#${s2_ml}}"; s2_sc="${s2_s/%*([[:digit:]])}" s2_scn="$((6-${#s2_sc}))"; [[ $s2_scn -ge 4 ]] && s2_scn=$((10-$s2_scn)) s2_sn="${s2_s#${s2_sc}}"; s2_m="${s2_ml/%[[:lower:]]}" s2_l="${s2_ml#${s2_m}}"; s2_m=( ${s2_m//./ } ) for ((i=0; i<${#s1_m[@]}; ++i)); do [[ "${s2_m[$i]}" ]] || return 5 s1mi="${s1_m[$i]}"; s1mi="${s1mi/#+(0)}" s2mi="${s2_m[$i]}"; s2mi="${s2mi/#+(0)}" [[ "$s1mi" -lt "$s2mi" ]] && return -6 [[ "$s1mi" -gt "$s2mi" ]] && return 6 done [[ "${#s1_m[@]}" -lt "${#s2_m[@]}" ]] && return -5 [[ "$s1_l" < "$s2_l" ]] && return -4 [[ "$s1_l" > "$s2_l" ]] && return 4 [[ "$s1_scn" < "$s2_scn" ]] && return -3 [[ "$s1_scn" > "$s2_scn" ]] && return 3 [[ "$s1_sn" -lt "$s2_sn" ]] && return -2 [[ "$s1_sn" -gt "$s2_sn" ]] && return 2 [[ "${s1_r}" -lt "${s2_r}" ]] && return -1 [[ "${s1_r}" -gt "${s2_r}" ]] && return 1 echo "Error! comm_ver failed $1 $2" >&2 exit 128 # should not reach here } function highest_mlsr() { local highest="" local mlsr for mlsr in $*; do if ! [[ "$highest" ]]; then highest=$mlsr continue fi comm_ver $mlsr $highest if [[ $? -gt 0 && $? -lt 128 ]]; then highest=$mlsr fi done echo $highest } function method_sort() { # An insertion sort, or something like it: we expect the input list to # be largely sorted so want to compare new elements to the top end of # the partial list; bash prefers iterating through lists forwards so we # store the partial list reversed. # $* is a function which returns true if $1 sorts lower than $2. read x && sorted_partial=" $x " || return 1 while read x; do for t in $sorted_partial; do "$@" $t $x && break; done \ && sorted_partial="${sorted_partial/ $t / $x $t }" \ || sorted_partial="$sorted_partial $x " done echo $sorted_partial | sed 's/ /\n/g' | tac } function is_ver_lower() { comm_ver $1 $2; [[ $? -ge 128 ]] } function mlsr_sort() { method_sort is_ver_lower } function has_version() { pkgspec="$1" if [[ "${pkgspec:0:8}" == "virtual/" ]]; then grep -q "${pkgspec}" /var/db/pkg/*/*/PROVIDE return $? fi # (<|<=|=|~|>=|>)?category/package(-version)?*? if [[ "${pkgspec:0:2}" == "<=" ]]; then vs="<=";pkgspec="${pkgspec:2}" elif [[ "${pkgspec:0:1}" == "<" ]]; then vs="<"; pkgspec="${pkgspec:1}" elif [[ "${pkgspec:0:1}" == "=" ]]; then vs="="; pkgspec="${pkgspec:1}" elif [[ "${pkgspec:0:1}" == "~" ]]; then vs="~"; pkgspec="${pkgspec:1}" elif [[ "${pkgspec:0:2}" == ">=" ]]; then vs=">=";pkgspec="${pkgspec:2}" elif [[ "${pkgspec:0:2}" == ">" ]]; then vs=">"; pkgspec="${pkgspec:2}" else vs=""; fi if [[ "$vs" == "=" ]]; then arr=( /var/db/pkg/$pkgspec ) [[ -d "${arr}" ]] && return 0 || return 1 elif [[ "$vs" == "~" ]]; then arr=( /var/db/pkg/${pkgspec}?(-r+([[:digit:]])) ) [[ -d "${arr}" ]] && return 0 || return 1 elif [[ "$vs" == "" ]]; then arr=( /var/db/pkg/${pkgspec}-$VER_SHPAT ) [[ -d "${arr}" ]] && return 0 || return 1 else cp="${pkgspec/%-$VER_SHPAT}" verstring_req="${pkgspec#${cp}-}" avail=( /var/db/pkg/${cp}-$VER_SHPAT ) [[ -d "${avail}" ]] || return 1 [[ "${vs}" ]] || return 0 for inst in "${avail[@]}"; do verstring_inst="${inst#/var/db/pkg/${cp}-}" comm_ver "${verstring_inst}" "${verstring_req}"; x=$? [[ ( $x -eq 0 && "${vs:1}" == "=" ) \ || ( $x -ge 128 && "${vs:0:1}" == "<" ) \ || ( $x -gt 0 && "${vs:0:1}" == ">" ) ]] \ && return 0 done return 1 fi } function virtuals_from() { cp=$1 echo "${allvirtuals}" \ | sed 's!^\([^[:space:]]\+\)[[:space:]]\+'"$cp"'\([[:space:]].*\|\)$!\1!;t;d' } function virtual_version() { cp=$1; mlsr=$2; virtual=$3; tree=$4 for provide in $(provide_for $cp-$mlsr $tree); do provide_base="${provide%-$VER_SHPAT}" [[ "${provide_base}" == "${virtual}" ]] || continue if [[ "${provide_base}" == "${provide}" ]]; then echo ${mlsr} else echo ${provide#${provide_base}-} fi return 0 done return 1 } function dbuse() { local cpv=$1 ( [[ -f /etc/portage/package.use ]] \ && cat /etc/portage/package.use \ | while read package use; do [[ "${package:0:1}" == "#" ]] && continue dep_satisfies $cpv $package && echo $use done echo "${alluse}" | sed 's/[[:space:]]\+/\n/g' ) | stacking_sort } function ebuild_dep_to_stdout() { cpv="$1"; tree="$2"; cp="${cpv/%-$VER_SHPAT}"; mlsr="${cpv#$cp-}" mls="${mlsr%-r+([[:digit:]])}"; rev="${mlsr#$mls-}"; cat="${cp%*/}"; pn="${cp#${cat}/}" [[ -d /var/cache/edb/dep/$tree ]] \ && depfile=/var/cache/edb/dep/$tree/$cpv \ || depfile=/var/cache/edb/dep/$cpv mkdir -p ${depfile%/*} 2>/dev/null dbkey=/dev/stdout ECLASSDIR=$PORTDIR/eclass \ EBUILD=${tree}/${cpv/%-$VER_SHPAT}/${cpv#*/}.ebuild P="$pn-$mls" \ PN="$pn" PV="$mls" PR="${rev:-r0}" PVR="$mlsr" PF="$pn-$mlsr" \ /usr/sbin/ebuild.sh depend 2>/dev/null \ | tee $depfile 2>/dev/null } function pkgdb_ebuild_dep_to_stdout() { cpv="$1"; cp="${cpv/%-$VER_SHPAT}"; mlsr="${cpv#$cp-}" mls="${mlsr%-r+([[:digit:]])}"; rev="${mlsr#$mls-}"; cat="${cp%*/}"; pn="${cp#${cat}/}" dbkey=/dev/stdout ECLASSDIR=$PORTDIR/eclass \ EBUILD=/var/db/pkg/${cpv}/${cpv#*/}.ebuild P="$pn-$mls" \ PN="$pn" PV="$mls" PR="${rev:-r0}" PVR="$mlsr" PF="$pn-$mlsr" \ /usr/sbin/ebuild.sh depend 2>/dev/null } function extract_var() { num="$1" name="$2" cpv="$3" tree="$4" if [[ "$original_depends" && -d /var/db/pkg/$cpv ]]; then if [[ -r /var/db/pkg/$cpv/$name ]]; then cat /var/db/pkg/$cpv/$name else pkgdb_ebuild_dep_to_stdout ${cpv} | sed "$num!d" fi elif [[ -f /var/cache/edb/dep/$tree/$cpv ]]; then sed "$num!d" /var/cache/edb/dep/$tree/$cpv elif [[ -f /var/cache/edb/dep/$cpv ]]; then sed "$num!d" /var/cache/edb/dep/$cpv elif [[ "${tree}" ]]; then ebuild_dep_to_stdout ${cpv} ${tree} | sed "$num!d" elif [[ -f /var/cache/edb/dep/$PORTDIR/$cpv ]]; then sed "$num!d" /var/cache/edb/dep/$PORTDIR/$cpv elif [[ -f $PORTDIR/${cpv/%-$VER_SHPAT}/${cpv#*/}.ebuild ]]; then ebuild_dep_to_stdout ${cpv} $PORTDIR | sed "$num!d" else extract_var $num $name $cpv $(best_tree $cpv) fi } function keywords_for() { extract_var 9 KEYWORDS $1 $2 } function provide_for() { extract_var 14 PROVIDE $1 $2 } function best_tree() { cpv="$1" ignore_keywords="$2" cp="${cpv/%-$VER_SHPAT}" mlsr="${cpv#$cp-}" pv="${cpv#*/}" my_accept="$(accept_for ${cp} ${mlsr})" for tree in ${portage_trees}; do [[ -f ${tree}/${cp}/${pv}.ebuild ]] || continue if [[ "$ignore_keywords" ]]; then echo ${tree%/} return 0 fi for keyword in $(keywords_for $cpv $tree); do if has $keyword $my_accept; then echo ${tree%/} return 0 fi done done echo $PORTDIR return 1 } function best_tree_cpv() { echo $(best_tree $1)/$1 } function raw_depends() { cpv=$1 ( [[ "$build_time_depends" ]] && extract_var 1 DEPEND $cpv extract_var 2 RDEPEND $cpv extract_var 12 CDEPEND $cpv extract_var 13 PDEPEND $cpv ) | sed 's/[[:space:]]\+/\n/g' } function rdepend() { cpv=$1 taint="" taint_for_ifno="" raw_depends $cpv | \ while read tok; do if [[ "${tok:$((${#tok}-1)):1}" == "?" ]]; then flag=${tok%?}; flag=${flag#!} has $flag $(dbuse $cpv) if [[ ( $? -eq 0 && "${tok:0:1}" == "!" ) || \ ( $? -ne 0 && "${tok:0:1}" != "!" ) ]]; then taint_for_ifno="$taint $tok" read tok if [[ "$tok" == "(" ]]; then ((nest=0)) while read tok; do [[ "$tok" == "(" ]]&& ((++nest)) [[ "$tok" == ")" ]]&& ((--nest)) ((nest == 0)) && break done fi else taint="$taint $tok" taint_for_ifno="" read tok if [[ "${tok}" != "(" ]]; then echo "$tok" "$taint" taint="$(abl $taint)" fi fi elif [[ "$tok" == ":" ]]; then read tok [[ "$taint_for_ifno" ]] && echo $tok $taint_for_ifno elif [[ "$tok" == "||" ]]; then read tok # ( - assume || ( ) contains only atoms read tok # first atom sel="$tok" while [[ "$tok" != ")" ]]; do if has_version "$tok"; then sel="$tok" while [[ "$tok" != ")" ]]; do read tok || break; done else read tok fi done echo "$sel" "$taint" elif [[ "$tok" != "(" && "$tok" != ")" && "${tok:0:1}" != "!" ]]; then echo "$tok" "$taint" elif [[ "$tok" == ")" ]]; then taint="$(abl $taint)" fi done | sort -u } function potential_revdepends() { needle="$1" exclude="$2" excl_sed="\!^$" for excl in $exclude; do [[ "${excl:0:1}" == "=" ]] \ && excl_sed="$excl_sed"'\|^'"${excl:1}"'$' \ || excl_sed="$excl_sed"'\|^'"${excl}-$VER_REGEXP"'$' done excl_sed="$excl_sed!d" grep -r "${needle}" /var/cache/edb/dep \ | sed '/.pickle:/d;s!^.*/\([^/]\+/[^/]\+\):.*$!\1!;t;d' \ | sort -u \ | while read cpv; do [[ -d /var/db/pkg/$cpv ]] && echo $cpv; done \ | sed -e "$excl_sed" } function qpkg () { # originally stolen from qpkg, then totally rewritten # smartdep below does exactly the same but it handles versions. Um... # time to refactor? local needle="$1" # cp local -i ndeps=0 local doworld="$2" local exclude="$3" local asvirtual="$4" local nokernel="$5" for deppkg in $(potential_revdepends ${needle} "$exclude"); do rdepend "${deppkg}" | grep '^\('"${needle}"'\|''\([<>]=\?\|[=~]\)'"${needle}-$VER_REGEXP"'\*\?''\) ' \ | if read depatom taint; then echo $'\t'"$(format_cpv "$(pad 35 "$(< /var/db/pkg/${deppkg}/CATEGORY)/$(< /var/db/pkg/${deppkg}/PF)")") $(format_taint ${taint})$(format_depatom ${depatom} ${asvirtual})" else false fi && ((++ndeps)) done echo "${allprofilepackages}" | grep '^\*\('"${needle}"'\|''\([<>]=\?\|[=~]\)'"${needle}-$VER_REGEXP"'\*\?''\)[[:space:]]*$' \ | if read depatom; then echo $'\t'"${YL}$(pad 35 "SYSTEM PROFILE")${NO} $(format_depatom ${depatom} ${asvirtual})" else false fi && ((++ndeps)) [[ "$doworld" ]] && cat ${WORLD_FILE} | grep '^\('"${needle}"'\|''\([<>]=\?\|[=~]\)'"${needle}-$VER_REGEXP"'\*\?''\)[[:space:]]*$' \ | if read depatom; then echo $'\t'"${BL}$(pad 35 "WORLD FILE")${NO} $(format_depatom ${depatom} ${asvirtual})" else false fi && ((++ndeps)) for virtual in $(virtuals_from $needle); do qpkg "$virtual" "$doworld" "$exclude" "yes" "$nokernel" ((ndeps+=$?)) done if [[ -z "$nokernel" ]]; then for mlsr in $(installed_mlsrs $needle); do if is_running_kernel $needle-$mlsr; then echo $'\t'"${RD}RUNNING KERNEL${NO}" ((++ndeps)) fi done fi return $ndeps } function dep_satisfies() { testpkg="$1" depatom="$2" t_cp="${testpkg/%-$VER_SHPAT}" t_mlsr="${testpkg#${t_cp}-}" d_cpv="${depatom/#@(<|<=|=|~|>=|>)}" vs="${depatom%${d_cpv}}" d_cp="${d_cpv/%-$VER_SHPAT?(\*)}" d_mlsr="${d_cpv#${d_cp}-}" # doesn't dereference virtuals [[ "$t_cp" != "$d_cp" ]] && return 2 if [[ "${d_cpv%\*}" != "$d_cpv" ]]; then d_cpv="${d_cpv%\*}" [[ "${testpkg:0:${#d_cpv}}" == "${d_cpv}" ]] \ && return 0 || return 1 fi [[ "$vs" == "" ]] && return 0 comm_ver "${t_mlsr}" "${d_mlsr}"; x=$? [[ ( $x -eq 0 && "$vs" == "=" ) \ || ( $x -eq 0 && "${vs:1}" == "=" ) \ || ( $x -ge 128 && "${vs:0:1}" == "<" ) \ || ( $x -gt 0 && $x -lt 128 && "${vs:0:1}" == ">" ) || ( ( $x -eq 255 || $x -eq 0 || $x -eq 1 ) && "$vs" == "~" ) ]] \ && return 0 return 1 } function resolve_depatom() { depatom="$1" d_cpv="${depatom/#@(<|<=|=|~|>=|>)}" vs="${depatom%${d_cpv}}" d_cp="${d_cpv/%-$VER_SHPAT?(\*)}" d_mlsr="${d_cpv#${d_cp}-}" echo "$vs $d_cpv $d_cp $d_mlsr" >&2 } function accept_for() { cp="$1" mlsr="$2" [[ -f /etc/portage/package.keywords ]] || echo $accept_keywords accept_ks="$accept_keywords" extra_ks=$( grep '^[<>=~]*'$cp /etc/portage/package.keywords 2>/dev/null | \ while read depatom package_keywords; do if dep_satisfies ${cp}-${mlsr} $depatom; then echo $package_keywords fi done ) for keyword in $extra_ks; do accept_ks="${accept_ks} $keyword" if [[ $keyword != "-*" && ${keyword:0:1} != "~" ]]; then accept_ks="${accept_ks//~$keyword}" fi done echo $accept_ks } function is_package_masked() { cp="$1" mlsr="$2" echo "${allprofilepackages}" | \ grep "$cp" | \ while read depatom; do depatom="${depatom#\*}" dep_satisfies ${cp}-${mlsr} "${depatom}" if [[ $? -eq 1 ]]; then echo "yes" break fi done | \ if read; then return 0 else kill_comments_and_blanks /etc/portage/package.unmask 2>/dev/null \ | grep "$cp" \ | while read depatom; do if dep_satisfies ${cp}-${mlsr} "${depatom}"; then echo "yes" break fi done | \ if read; then return 2 else cat $PORTDIR/profiles/package.mask /etc/portage/package.mask 2>/dev/null | sed '/^#/d' | grep "$cp" | \ while read depatom; do if dep_satisfies ${cp}-${mlsr} "${depatom}"; then echo "yes" fi done | \ if read; then return 0 else return 1 fi fi fi } function avail_versions() { cp="$1" allversiontriples="$( for tree in "" $portage_trees; do for depfile in /var/cache/edb/dep${tree%/}/${cp}-$VER_SHPAT; do [[ -f "$depfile" ]] || continue [[ -f ${tree:-$PORTDIR}/$cp/${cp#*/}-${depfile#/var/cache/edb/dep${tree%/}/${cp}-}.ebuild ]] || continue echo ${depfile#/var/cache/edb/dep${tree%/}/${cp}-} sed -n '3p;9p' $depfile done done | sed 'N;N;s/\n/ /g' )" # mlsr slot keywords... echo "${allversiontriples}" | \ while read my_mlsr my_slot my_keywords; do is_package_masked $cp $my_mlsr && continue for keyword in $(accept_for ${cp} ${my_mlsr}); do if has $keyword $my_keywords; then echo $my_mlsr fi done done | \ sort -u } function installed_mlsrs() { cp=$1 for dir in /var/db/pkg/$cp-$VER_SHPAT; do [[ -d "$dir" ]] && echo ${dir#/var/db/pkg/${cp}-} done } function is_running_kernel() { cpv=$1 [[ -d /var/db/pkg/$cpv ]] && has virtual/linux-sources $(provide_for $cpv) && [[ "$(bunzip2 -c "/var/db/pkg/${cpv}/environment.bz2" | sed 's:^KV=::;ta;d;:a;q')" == "$(uname -r)" ]] } function not_in_tree() { cp=$1; mlsr=$2 if best_tree $cp-$mlsr yes >/dev/null; then cat | format_error >&2 <&2 <]=\?\|[=~]\)'"${needle}-$VER_REGEXP"'\*\?''\) ' \ | while read depatom taint; do if dep_satisfies "${ncpv}" "$depatom"; then if [[ ! "$(echo "${cands}" | \ while read candidate; do if dep_satisfies "${needle}-${candidate}" "$depatom"; then echo yes break fi done)" ]]; then echo $'\t'"$(format_cpv "$(pad 35 "$(< /var/db/pkg/${deppkg}/CATEGORY)/$(< /var/db/pkg/${deppkg}/PF)")") $(format_taint ${taint})$(format_depatom ${depatom} ${asvirtual})" break fi fi done done echo "${allprofilepackages}" | grep '^\*\('"${needle}"'\|''\([<>]=\?\|[=~]\)'"${needle}-$VER_REGEXP"'\*\?''\)[[:space:]]*$' \ | while read depatom; do depatom=${depatom#\*} if dep_satisfies "$ncpv" "$depatom"; then if [[ ! "$(echo "${cands}" | \ while read candidate; do if dep_satisfies "${needle}-${candidate}" "$depatom"; then echo yes break fi done)" ]]; then echo $'\t'"${YL}$(pad 35 "SYSTEM PROFILE")${NO} $(format_depatom ${depatom} ${asvirtual})" break fi fi done cat ${WORLD_FILE} | grep '^\('"${needle}"'\|''\([<>]=\?\|[=~]\)'"${needle}-$VER_REGEXP"'\*\?''\)[[:space:]]*$' \ | while read depatom; do if dep_satisfies "$ncpv" "$depatom"; then if [[ ! "$(echo "${cands}" | \ while read candidate; do if dep_satisfies "${needle}-${candidate}" "$depatom"; then echo yes break fi done)" ]]; then echo $'\t'"${BL}$(pad 35 "WORLD FILE")${NO} $(format_depatom ${depatom} ${asvirtual})" break fi fi done [[ "${asvirtual}" ]] || for virtual in $(virtuals_from $cp); do smartdep $cpv $virtual done if [[ -d /var/db/pkg/$cpv ]] && has virtual/linux-sources $(provide_for $cpv) && [[ "$(bunzip2 -c "/var/db/pkg/${cpv}/environment.bz2" | sed 's:^KV=::;ta;d;:a;q')" == "$(uname -r)" ]]; then echo $'\t'"${RD}RUNNING KERNEL${NO}" fi } function pv_to_cpv() { pv="$1" p="${pv/%-$VER_SHPAT}" find $portage_trees -type d -and -name ${p} \ -mindepth 2 -maxdepth 2 \ | sed 's:.*/\([^/]\+/[^/]\+$\):\1'${pv#${p}}':' | sort -u } function redundant_of_all_cp_mlsrs() { cp=$1; shift; mlsrs="$*" if [[ $# -gt 1 ]]; then redundant_mlsrs="" ok_mlsrs="" for mlsr in $mlsrs; do if [[ ! "$(smartdep $cp-$mlsr)" ]]; then redundant_mlsrs="$redundant_mlsrs $mlsr" else ok_mlsrs="$ok_mlsrs $mlsr" fi done [[ ! "${ok_mlsrs}" && "$(smartdep $cp)" ]] \ && redeem=$(highest_mlsr $redundant_mlsrs) \ || redeem="" for mlsr in ${redundant_mlsrs//$redeem}; do echo $cp-$mlsr echo $cp-$mlsr >&2 done else if [[ -z "$(smartdep $cp)" ]]; then echo $cp-$mlsrs echo $cp-$mlsrs >&2 fi fi } function all_installed_cp_mlsrs() { oldcp="" find /var/db/pkg -type d -mindepth 2 -maxdepth 2 \ | cut -c13- \ | sort \ | while read cpv; do cp="${cpv/%-$VER_SHPAT}" if [[ "$oldcp" == "$cp" ]]; then echo -n ${cpv#$cp-} '' else echo -n ${oldcp:+$'\n'}$cp ${cpv#$cp-} '' oldcp=$cp fi done echo } function redundant() { all_installed_cp_mlsrs | \ while read cp mlsrs; do redundant_of_all_cp_mlsrs $cp $mlsrs done } function star_argv_dep() { target=${1%-}; highest_only=$2 if [[ "${target/\/}" == "${target}" ]]; then for ctarget in $(pv_to_cpv $target); do star_argv_dep $ctarget $highest_only done else category=${target%/*} ptarget=${target#*/} for package in $( for tree in $portage_trees; do ls ${tree%/}/$category/*/$ptarget*.ebuild 2>/dev/null | \ sed 's!^'${tree%/}/$category/'\([^/]\+\)/\1-'"$VER_REGEXP"'.ebuild$!\1!;t;d' done | sort -u ); do vtarget=${ptarget/#$package?(-)} cp=$category/$package ( avail_versions $cp; installed_mlsrs $cp) \ | sort -u \ | grep ^$vtarget \ | mlsr_sort \ | if [[ "$highest_only" ]]; then tail -n 1 else cat fi | while read mlsr; do echo $cp-$mlsr: smartdep $cp-$mlsr done done fi } function argv_dep() { pkg=$1; pkg="${pkg#=}" if [[ "${pkg%\*}" != "${pkg}" ]]; then star_argv_dep ${pkg%\*} elif [[ "${pkg/\/}" == "${pkg}" ]]; then for cpkg in $(pv_to_cpv $pkg); do argv_dep $cpkg done elif [[ "$do_list_depends" && "${pkg%-$VER_SHPAT}" == "${pkg}" ]]; then star_argv_dep $pkg "yes" else echo $pkg: smartdep $pkg fi } function upper() { echo "$*" | tr [[:lower:]] [[:upper:]] } # Pretty much a straight port of the function of the same name in emerge. # Only difference is in the order of arguments: call as: # userquery "Make your system ROCK LIKE NINJA?" Yes GR 'Hell Yeah!' CY No RD # See above for colour codes, or add your own. function userquery() { prompt="${BR}$1${NO} " shift if [[ $# -eq 0 ]]; then rprompt="[${GR}Yes${NO}/${RD}No${NO}] " responses=( Yes No ) else rprompt="[" responses=() ((n=$#/2)) for ((i=0; i&2; exit 0" INT while true; do if read -e -p "$rprompt"; then for ((i=0; i<${#responses[@]}; ++i)); do response="${responses[$i]}" [[ "$REPLY" == "${response:0:${#REPLY}}" ]] \ && return $i done for ((i=0; i<${#responses[@]}; ++i)); do response="${responses[$i]}" [[ "$(upper "$REPLY")" == "$(upper "${response:0:${#REPLY}}")" ]] \ && return $i done echo "Sorry, response '$REPLY' not understood." >&2 else # Ctrl-D echo "Interrupted." >&2 exit 0 fi done trap - INT } if [[ "$1" == "--exec" ]]; then shift $* echo $'\n'"Return value: $?" >&2 exit fi if [[ "$do_purge" ]]; then redundant="$( for dbpath in /var/db/pkg/*/*; do cpv=${dbpath#/var/db/pkg/} cp=${cpv/%-$VER_SHPAT} echo $cp $cpv done | \ sort | \ ( oldcp=""; oldcpv="" while read cp cpv; do if [[ "$cp" == "$oldcp" ]]; then echo $oldcpv; echo $cpv while read cp cpv; do [[ "$cp" == "$oldcp" ]] || break echo $cpv done fi oldcp=$cp; oldcpv=$cpv done ) | \ while read cpv; do if [[ ! "$(smartdep $cpv)" ]]; then echo $cpv echo $cpv >&2 fi done)" [[ "${redundant}" ]] && emerge -vC $portage_action_arg $redundant exit 0 fi if [[ "$do_depclean" ]]; then my_redundant="$(redundant)" [[ "${my_redundant}" ]] && emerge -vC $portage_action_arg $my_redundant exit 0 fi if [[ "$*" ]]; then for ((i=$OPTIND; i<=$#; ++i)); do argv_dep ${!i} done exit 0 fi install_new_world() { [[ "$(whoami)" == "root" ]] || echo "Please enter your root password or Ctrl+C, Enter to cancel." while true; do su -c 'cat /tmp/pruneworld-newworld >'"${WORLD_FILE}" case $? in 0) return 0;; 1) continue;; *) return 1;; esac done } if [[ ( -f /tmp/pruneworld-oldworld && ! -w /tmp/pruneworld-oldworld ) || \ ( -f /tmp/pruneworld-newworld && ! -w /tmp/pruneworld-newworld ) || \ ( -f /tmp/pruneworld-mergedworld && ! -w /tmp/pruneworld-mergedworld ) \ ]]; then cat | format_error >&2 <&2 exclude="$exclude $REPLY" fi done | tee /tmp/pruneworld-newworld ) n=$(wc -l &2 [[ $red -eq 0 ]] && exit 0 case $do_action in pretend ) exit 0 ;; ask ) while true; do userquery "Install new world file?" Yes GR No RD Diff BL Merge FC case $? in 0 ) install_new_world && break ;; 1 ) exit 0 ;; 2 ) diff -u /tmp/pruneworld-oldworld /tmp/pruneworld-newworld | ${PAGER:-less} ;; 3 ) sdiff -o /tmp/pruneworld-mergedworld /tmp/pruneworld-oldworld /tmp/pruneworld-newworld [[ $? -eq 2 ]] || cat /tmp/pruneworld-mergedworld >/tmp/pruneworld-newworld ;; esac done ;; force ) install_new_world ;; esac