# -*- shell-script -*- # # Gentoo Linux command completion. # # Copyright 1999-2002 Gentoo Technologies, Inc. # Distributed under the terms of the GNU General Public License, v2 or later # # Author: Geert Bevin # Author: Zach Forrest # # Turn on extended globbing and programmable completion shopt -s extglob progcomp # # emerge completion command # _emerge() { local cur prev grepcmd sedcmd systemactions setsma setbig portagedir origdir local mode words opts curword numwords pkgdbdir origdir="${PWD}" portagedir=/usr/portage pkgdbdir=/var/db/pkg COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" words="${COMP_WORDS[*]}" numwords=${#COMP_WORDS[*]} curword=${COMP_CWORD} mode='ALL' # Determine which mode we're running in. if [ ${curword} -eq 1 ] && [ ${numwords} -eq 2 ]; then # This is set when we nothing but the command name has been given. mode='ALL' elif [[ " ${words} " == *" --unmerge "* ]]; then mode='UNMERGE' elif [[ " ${words} " == *" --safe "* ]]; then mode='SAFE' elif [[ " ${words} " == *" --search "* ]]; then mode='SEARCH' elif [[ " ${words} " == *" --world "* ]]; then mode='WORLD' elif [[ " ${words} " == *" --help "* ]]; then mode='HELP' elif [[ " ${words} " == *" --clean "* ]]; then mode='CLEAN' elif [[ " ${words} " == *" --pretend "* ]]; then mode='PRETEND' else # All except '--clean' mode='NOCLEAN' fi if [[ "${cur}" == -* ]]; then # Process Options case "${mode}" in "ALL") opts='--clean --help --verbose --debug --fetchonly \ --onlydeps --noreplace --usepkg --buildpkg \ --autoclean --pretend --unmerge --safe \ --search --world' ;; "UNMERGE") opts='--clean --verbose --debug --pretend \ --unmerge --safe' ;; "SAFE") opts='--unmerge --verbose --debug' ;; "SEARCH") opts='--verbose --debug' ;; "WORLD") opts='--verbose --debug --pretend --world' ;; "HELP") opts='' ;; "CLEAN") opts='--verbose --debug --clean' ;; "PRETEND") opts='--verbose --debug --fetchonly --onlydeps \ --noreplace --usepkg --buildpkg \ --autoclean --pretend --unmerge --safe \ --world' ;; "NOCLEAN") opts='--help --verbose --debug --fetchonly \ --onlydeps --noreplace --usepkg --buildpkg \ --autoclean --pretend --unmerge --safe \ --search --world' ;; *) # Same as ALL opts='--clean --help --verbose --debug --fetchonly \ --onlydeps --noreplace --usepkg --buildpkg \ --autoclean --pretend --unmerge --safe \ --search --world' ;; esac # Generate the reply. COMPREPLY=($(compgen -W "${opts}" | grep ^$cur)) elif [ "${mode}" == "CLEAN" ]; then # "rsync" is the only option that can follow "--clean" COMPREPLY=($(compgen -W 'rsync')) elif [ "${mode}" == "WORLD" ]; then # "update" is the only option that can follow "--world" #COMPREPLY=($(compgen -W 'update')) COMPREPLY=($(compgen -W 'update' | grep ^${cur})) elif [ "${mode}" == "HELP" ]; then if [[ ! " ${words} " == *" "@(system|rsync)" "* ]]; then COMPREPLY=($(compgen -W 'rsync system' | grep ^${cur})) else COMPREPLY='' fi elif [ "${mode}" == "SEARCH" ]; then # We don't need to interfere here as the search string is a # regular expression. (But wouldn't intelligent, dynamic, # commandline completion of regular expressions be great!) COMPREPLY='' elif [ "${mode}" == "UNMERGE" ]; then # Ignore >, >=, <, <=, and = for the purpose of completion. sedcmd="sed -e s:=:: -e s:>:: -e s:<::" cur=$(echo "${cur}" | ${sedcmd}) # If the current completion (minus conditional characters) # starts with one of the following characters, then complete # on filenames (i.e. an ebuild file) rather than referencing # the package database. if [[ "${cur} " == @('/'|'.'|'~'|'$')* ]]; then # Setting this to nothing with tell bash to use default # completion (i.e. pathname completion). COMPREPLY='' else cd "${pkgdbdir}" grepcmd="grep -E ^$cur.*" if [ "${cur}" ]; then if [ $(echo "${cur}" | grep '/') ]; then setbig=$(compgen -G "${cur}*") COMPREPLY=($(echo "${setbig}" | ${grepcmd})) else setsma=$(compgen -S '/' -G "${cur}*") if [ $(echo "${setsma}" | ${grepcmd} | grep '/' | wc -l) = 1 ]; then setbig=$(compgen -G "*/*") COMPREPLY=($(echo "${setbig}" | ${grepcmd})) else COMPREPLY=($(echo "${setsma}" | ${grepcmd})) fi fi else setsma=$(compgen -S '/' -G "${cur}*") COMPREPLY=($(echo "${setsma}")) fi cd "${origdir}" fi elif [ "${mode}" == "SAFE" ]; then COMPREPLY=($(compgen -W '--unmerge' | grep ^${cur})) elif [[ " ${words} " == *" "@(update|system|rsync)" "* ]]; then # syncing the portage tree is a lonely job # updating should be done by itself (also lonely) COMPREPLY='' elif [[ " ${words} " == *" --usepkg "* ]] && [[ "${cur} " == @('/'|'.'|'~'|'$')* ]]; then # In order to complete on a package file, the name must be # an absolute or realative pathname (even if it is in the # current directory). This allows completion on both package # names in /usr/portage and on arbitrary *.tbz2 packages. # # I'm planning to come up with a more elegant solution, but # that will have to wait for another update. (The only thing # that is really annoying right now is when specifying a # *.tbz2 file in or below the current directory, you have # to prefix the path with "./".) COMPREPLY='' else cd "${portagedir}" grepcmd="grep -E ^${cur}.*" sedcmd="sed -e /CVS/d \ -e /BUGS-TODO/d \ -e /ChangeLog.*/d \ -e /header.txt/d \ -e /skel.build/d \ -e /skel.ebuild/d \ -e /distfiles/d \ -e /eclass/d \ -e /files/d \ -e /incoming/d \ -e /packages/d \ -e /profiles/d \ -e /scripts/d \ -e /virtual-update/d \ -e /current-packages/d" if [ ${COMP_CWORD} -eq 1 ]; then # If emerge hasn't been given any args yet, include "rsync" # among the system actions. The case where the "--clean" flag # has been specified is handled above. systemactions=$'\n'"system"$'\n'"update"$'\n'"rsync" else # Only allow these actions if no packages have been specified. if [[ ! " ${words} " == *" "*[/]*" "* ]]; then systemactions=$'\n'"system"$'\n'"update" else systemactions='' fi fi if [ "${cur}" ]; then if [ $(echo "${cur}" | grep '/') ]; then setbig=$(compgen -G "${cur}*" | ${sedcmd})"${systemactions}" COMPREPLY=($(echo "${setbig}" | $grepcmd)) else setsma=$(compgen -S '/' -G "${cur}*" | ${sedcmd})"${systemactions}" if [ $(echo "${setsma}" | ${grepcmd} | grep '/' | wc -l) = 1 ]; then setbig=$(compgen -G "*/*" | ${sedcmd})"${systemactions}" COMPREPLY=($(echo "${setbig}" | ${grepcmd})) else COMPREPLY=($(echo "${setsma}" | ${grepcmd})) fi fi else setsma=$(compgen -S '/' -G "${cur}*" | ${sedcmd})"${systemactions}" COMPREPLY=($(echo "${setsma}")) fi cd "${origdir}" fi # (Adapted from bash_completion by Ian Macdonald ) # This removes any options from the list of completions that have # already been specified on the command line. COMPREPLY=($(echo "${COMP_WORDS[@]}" | \ (while read -d ' ' i; do [ "${i}" == "" ] && continue # flatten array with spaces on either side, # otherwise we cannot grep on word boundaries of # first and last word COMPREPLY=" ${COMPREPLY[@]} " # remove word from list of completions COMPREPLY=(${COMPREPLY/ ${i%% *} / }) done echo ${COMPREPLY[@]}))) return 0 } complete -o default -F _emerge emerge # # ebuild completion command # _ebuild() { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} if [ $COMP_CWORD -eq 1 ]; then #COMPREPLY=( $( compgen -o filenames -X '!*.ebuild' $cur ) ) COMPREPLY=( $( compgen -o filenames ) ) elif [ $COMP_CWORD -eq 2 ]; then COMPREPLY=( $( compgen -W 'clean \ compile \ digest \ fetch \ install \ merge \ package \ qmerge \ remerge \ rpm \ unmerge \ unpack' $cur ) ) fi return 0 } complete -o default -F _ebuild ebuild # # rc-update completion command # _rc-update() { local cur prev initdir runlvdir origdir origdir=${PWD} initdir=/etc/init.d runlvdir=/etc/runlevels COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} if [ $COMP_CWORD -eq 1 ]; then COMPREPLY=( $( compgen -W 'add del' ${cur} ) ) elif [ $COMP_CWORD -eq 2 ]; then cd ${initdir} COMPREPLY=( $( compgen -G "${cur}*" ) ) cd ${origdir} elif [ $COMP_CWORD -eq 3 ]; then cd ${runlvdir} COMPREPLY=( $( compgen -G "${cur}*" ) ) cd ${origdir} fi return 0 } complete -F _rc-update rc-update # # This function loosely emulates emerge, but, as the name implies, # it _unmerges_. It is useful for unmerging multiple, existing packages # with a lot less typing. It looks in /var/db/pkg. # epurge() { local pkg pkgdb pkgfile usage pretend red yellow off local pkgnover pkgcount depend red="\033[31;01m" # Red yellow="\033[33;01m" # Yellow off="\033[0m" # Default pkgdb="/var/db/pkg" usage="Usage: ${FUNCNAME} [ --pretend ] cat/pkg [ [ cat2/pkg2 ] ... ]" count=0 # We need at least one package if [ -z "${1}" ]; then echo "${usage}" return 1 fi if [ "${1}" = "--pretend" ]; then # We need at least one package if [ -z "${2}" ]; then echo "${usage}" return 1 else shift # move "--pretend" out of the way pretend="TRUE" echo echo "These are the packages that I would unmerge, in order." fi fi # The following may seem a little overzealous. After all, why not just check # for the existence of the files during the main loop? My rationale is this: # because unmerging has the potential to do serious damage (e.g. unmerging your # only glibc entry), any problems should be addressed _before_ any packages are # unmerged. This will provide one extra sanity check for the user; if there # is one mistake, there may be others. Also, because this function isn't the # most sophisticated piece of code in the world, if a package was specified # more than once on the command line, the main loop can now assume (with a # reasonable degree of certainty) that any missing ebuild files are from # packages that have already been unmerged (i.e. if the file doesn't exist, # it must have been unmerged with a previous call to ebuild). # Enough talk.... for pkg in $* do pkgfile="${pkgdb}/${pkg}/$( basename ${pkg} ).ebuild" if [ ! -f "${pkgfile}" ]; then echo echo -e "${red}!!!${off} Cannot find database entry for $pkg:" echo -e "${red}!!!${off} $pkgfile not found" echo return 1 fi done # Make some room. echo for pkg in $* do if [ "${pretend}" = "TRUE" ]; then echo -e "[ebuild ${red}UNMERGE${off}] $pkg" # Calculate dependencies # # Note: This doesn't (yet) account for version numbers # in calculating dependencies. So, if a package requires # a specific version of the one being unmerged, and this # is that version, then you may end up with a broken # package/dependency. Also, dependency checking is only # performed when using "--pretend". When actually # unmerging packages, it is assumed that you know what # you are doing. # # TODO: make version aware # TODO: check for PROVIDES (i.e. virtual dependencies) pkgnover="${pkg%%-[0-9]*}" pkgcount=$( ls "${pkgdb}/${pkg%%/*}" | grep -c ${pkgnover##*/} ) # If there exists another installed version of the package, # then assume any dependencies are taken care of (see note # above). Otherwise, check for packages dependent on this # one. if [ ${pkgcount} -eq 1 ]; then for depend in $( egrep -l ${pkgnover} $( find ${pkgdb} -name RDEPEND ) \ | sed -e "s|/RDEPEND||" -e "s|${pkgdb}/||" ) do echo -e "${yellow}!!! WARNING:${off} $depend depends on $pkgnover" done fi else pkgfile=${pkgdb}/${pkg}/$( basename ${pkg} ).ebuild if [ -f "${pkgfile}" ]; then echo ">>> Unmerging ${pkg}...." echo ">>> ebuild ${pkgfile} unmerge" ebuild ${pkgfile} unmerge echo ">>> ${pkg} unmerged" else echo echo "!!! Ignoring ${pkg} (already unmerged)" # most likely, anyway echo fi fi done echo return 0 } # # epurge completion command # _epurge() { local cur origdir pkgdb grepcmd setsma setbig origdir="${PWD}" pkgdb=/var/db/pkg COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then COMPREPLY=( $( compgen -W '--pretend' | grep ^$cur ) ) else cd ${pkgdb} grepcmd="grep -E ^${cur}.*" setsma=$( compgen -S '/' -G "*" ) setbig=$( compgen -G "*/*" ) if [ ${cur} ]; then if [ `echo ${cur} | grep '/'` ]; then COMPREPLY=( $( echo "${setbig}" | ${grepcmd} ) ) else if [ `echo "${setsma}" | ${grepcmd} | grep '/' | wc -l` = 1 ]; then COMPREPLY=( $( echo "${setbig}" | ${grepcmd} ) ) else COMPREPLY=( $( echo "${setsma}" | ${grepcmd} ) ) fi fi else COMPREPLY=( $( echo "${setsma}" ) ) fi cd ${origdir} fi return 0 } complete -F _epurge epurge