diff --git a/contrib/bash_completion b/contrib/bash_completion index 62e5bc9..218321a 100644 --- a/contrib/bash_completion +++ b/contrib/bash_completion @@ -1,365 +1,234 @@ -# vim: set ft=sh ts=2 sw=2 et: -# file: /etc/bash_completion.d/pacman - -# Bash completion for pacman -# Original: Manolis Tzanidakis +# pacman/makepkg completion by Andres Perera # -# Distributed under the terms of the GNU General Public License, v2 or later. +# Distributed under the terms of the GNU General Public License v2 or +# later. # - -## initial functions - -rem_selected () -{ - # (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=($(/bin/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%% *} / }) +# Local variables: common core cur glob list long m o prev query r +# remove s short sync upgrade w +# +# vim: ft=sh sts=2 sw=2 et: + +# Removes packages/files already present in the line +_arch_rem_selected() { + local w r + + for (( w=0; w<${#COMP_WORDS[@]}-1; w++)); do + # Don't touch operands + [[ ${COMP_WORDS[w]} == -* ]] && continue + for r in "${!COMPREPLY[@]}"; do + # Only deal with full matches + if [[ ${COMP_WORDS[w]} == ${COMPREPLY[r]} ]]; then + unset 'COMPREPLY[r]'; break + fi done - /bin/echo ${COMPREPLY[@]}))) - return 0 -} - -_available_repos () -{ - COMPREPLY=( $( compgen -W "$(/bin/grep '\[' /etc/pacman.conf | /bin/grep -v -e 'options' -e '^#' | tr -d '[]' )" -- $cur ) ) -} - -_installed_pkgs () -{ - local installed_pkgs - installed_pkgs=$( /bin/ls /var/lib/pacman/local/ ) - COMPREPLY=( $( compgen -W "$( for i in $installed_pkgs; do /bin/echo ${i%-*-*}; done )" -- $cur ) ) + done } -_available_pkgs () -{ - #find balks easilly on a find /foo/*/* type dir, especially one like - # /var/lib/pacman/*/* - # This little change-up removes the find *and* only uses enabled repos - local available_pkgs - local enabled_repos - enabled_repos=$( /bin/grep '\[' /etc/pacman.conf | /bin/grep -v -e 'options' -e '^#' | tr -d '[]' ) - available_pkgs=$( for r in $enabled_repos; do /bin/echo /var/lib/pacman/sync/$r/*; done ) - COMPREPLY=( $( compgen -W "$( for i in $available_pkgs; do j=${i##*/}; echo ${j%-*-*}; done )" -- $cur ) ) -} +# makepkg purge functions +_makepkg_deselect() { + local o -_installed_groups () -{ - local installed_groups - installed_groups=$( /bin/find /var/lib/pacman/local -name desc -exec /bin/sed -ne '/%GROUPS%/,/^$/{//d; p}' {} \; | /bin/sort -u ) - COMPREPLY=( $( compgen -W "$( for i in $installed_groups; do /bin/echo ${i%-*-*}; done )" -- $cur ) ) + if [[ $1 == l ]]; then + for o in "${!long[@]}"; do + if [[ ${COMP_WORDS[w]} == ${long[o]} ]]; then + unset 'long[o]'; break + fi + done + else + for o in "${!short[@]}"; do + if [[ ${COMP_WORDS[w]} == -*([^- ])${short[o]}* ]]; then + unset 'short[o]' + fi + done + fi } -_available_groups () -{ - #find balks easilly on a find /foo/*/* type dir, especially one like - # /var/lib/pacman/*/* - # This little change-up removes the find *and* only uses enabled repos - local available_groups - local enabled_repos - enabled_repos=$( /bin/grep '\[' /etc/pacman.conf | /bin/grep -v -e 'options' -e '^#' | tr -d '[]' ) - available_groups=$( for r in $enabled_repos; do /bin/sed '/%GROUPS%/,/^$/{//d; p}' /var/lib/pacman/sync/$r/*/desc | /bin/sort -u; done ) - COMPREPLY=( $( compgen -W "$( for i in $available_groups; do /bin/echo ${i%-*-*}; done )" -- $cur ) ) +_makepkg_count_words() { + local w + + for (( w=0; w<${#COMP_WORDS[@]}; w++)); do + [[ ${COMP_WORDS[w]} == @(-*([^- ])[hC]*|--@(help|cleancache)) ]] && return 0 + _makepkg_deselect "$1" + done } -## makepkg completion - -_makepkg () -{ - local cur prev +# makepkg main +_makepkg() { COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} - prev=${COMP_WORDS[COMP_CWORD-1]} - case "$prev" in - -p) - _filedir - return 0 - ;; - --help|--cleancache) - COMPREPLY='' - return 0 - ;; - esac - - if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '\ - -A --ignorearch \ - -b --builddeps \ - -c --clean \ - -C --cleancache \ - -d --nodeps \ - -e --noextract \ - -f --force \ - -g --geninteg \ - -h --help \ - -i --install \ - -L --log \ - -m --nocolor \ - -o --nobuild \ - -p \ - -r --rmdeps \ - -s --syncdeps \ - --asroot \ - --source \ - --noconfirm \ - --noprogressbar' -- $cur ) ) + local prev cur short long + prev=${COMP_WORDS[COMP_CWORD-1]} + cur=`_get_cword` + + short=( A C L R c d e f g h i m o p r s ) + + long=( --allsource --asroot --clean --cleancache --config --force + --geninteg --help --holdver --ignorearch --install --log + --nobuild --nocolor --noconfirm --nodeps --noextract + --noprogressbar --repackage --rmdeps --skipinteg --source + --syncdeps ) + + if (( COMP_CWORD == 1 )) && [[ $cur != -[^-]* ]]; then + # First word check + COMPREPLY=($(compgen -W "$(echo "${short[@]/#/-}" "${long[@]}")" -- "$cur")) + elif [[ $prev == @(--config|-*([^- ])p*) ]]; then + # Complete filenames for selected options + _arch_rem_selected + return 0 + else + # Generate the list depending on current word + case $cur in + -+($(IFS='|'; echo "${short[*]}"))) + _makepkg_count_words s + COMPREPLY=($(compgen -W "$(echo "${short[@]/#/$cur}")" -- "$cur")) + ;; + -*) + # If $cur isn't a combination option, fall back to displaying + # long options only. These are the prime target for completion + _makepkg_count_words l + COMPREPLY=($(compgen -W "$(echo "${long[@]}")" -- "$cur")) + ;; + esac fi - - rem_selected } -complete -o default -F _makepkg makepkg -## pacman completion - -_instring () -{ - str="${1}" - shift 1 - for c in "${@}"; do - if [ $(/bin/expr index "${str}" "${c}") -gt 0 ]; then - return 0 - fi - done - return 1 +# pacman packages +_pacman_pkg() { + COMPREPLY=($(compgen -W "$(if [[ $2 ]]; then + command pacman -"$1" |\ + command sed 's, .*,,' |\ + command sort -u + else + command pacman -"$1" + fi)" -- "$cur" )) } -_pacman () -{ - local a arg toparse op mod cur +# pacman main +_pacman() { COMPREPLY=() - # This argument parsing is done so we can check for flag existance later - # right now it's a tad crappy, but does the job - for (( i=1; i < ${#COMP_WORDS[@]}-1; i++ )); do - a=${COMP_WORDS[i]} - arg="${a:0:2}" - toparse="${a:2}" + local prev cur query remove sync upgrade core common list w m o s=() + prev=${COMP_WORDS[COMP_CWORD-1]} + cur=`_get_cword` - case "${arg}" in - -@(U|R|S|Q|h|V)) - op="${arg/-}" - mod="${mod}${a:2}" - ;; - --) - arg="${a:2}" - case "${arg}" in - remove) op="R" ;; - upgrade) op="U" ;; - query) op="Q" ;; - sync) op="S" ;; - help) op="h" ;; - version) op="V" ;; - verbose) mod="${mod}v" ;; - root) mod="${mod}r" ;; - dbpath) mod="${mod}b" ;; - nodeps) mod="${mod}d" ;; - force) mod="${mod}f" ;; - groups) mod="${mod}g" ;; - info) mod="${mod}i" ;; - list) mod="${mod}l" ;; - print-uris) mod="${mod}p" ;; - search) mod="${mod}s" ;; - sysupgrade) mod="${mod}u" ;; - upgrades) mod="${mod}u" ;; - downloadonly) mod="${mod}w" ;; - refresh) mod="${mod}y" ;; - changelog) mod="${mod}c" ;; - deps) mod="${mod}d" ;; - explicit) mod="${mod}e" ;; - unrequired) mod="${mod}t" ;; - foreign) mod="${mod}m" ;; - owns) mod="${mod}o" ;; - file) mod="${mod}p" ;; - search) mod="${mod}s" ;; - upgrades) mod="${mod}u" ;; - cascade) mod="${mod}c" ;; - check) mod="${mod}k" ;; - dbonly) mod="${mod}k" ;; - nosave) mod="${mod}n" ;; - recursive) mod="${mod}s" ;; - unneeded) mod="${mod}u" ;; - esac ;; - *) toparse="${a}" ;; - esac + # These are split in a subshell with IFS=' '. No point in listing + # them by \n since compgen -W can't handle spaces or new lines in + # operands + query=( '--changelog --check --deps --explicit --file --foreign --groups --info + --list --owns --quiet --search --unrequired --upgrades' 'c e g i k l m o p q s t u' ) - arglen=$(( ${#toparse}-1 )) - for c in $(/bin/seq 0 "${arglen}"); do - arg=${toparse:$c:1} - [ "${arg}" != "-" ] && mod="${mod}${arg}" - done - done + remove=( '--cascade --dbonly --nodeps --nosave --recursive --unneeded' 'c k n s u' ) - cur=${COMP_WORDS[COMP_CWORD]} + sync=( '--asdeps --asexplicit --clean --downloadonly --force --groups --ignore --ignoregroup + --info --list --needed --nodeps --print-uris --quiet --refresh --search --sysupgrade' + 'c f g i l p q s u w y' ) - if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '\ - -h --help \ - -Q --query \ - -R --remove \ - -S --sync \ - -U --upgrade \ - -V --version \ - ' -- $cur ) ) - rem_selected - return 0 - fi + upgrade=( '--asdeps --asexplicit --force --nodeps' f ) - if [[ "$cur" == -* ]]; then - case "${op}" in - U) - COMPREPLY=( $( compgen -W '\ - --asdeps \ - --asexplicit \ - -d --nodeps \ - -f --force \ - -h --help \ - --config \ - --logfile \ - --noconfirm \ - --noprogressbar \ - --noscriptlet \ - -v --verbose \ - -r --root \ - -b --dbpath \ - --cachedir \ - ' -- $cur ) ) - return 0 - ;; - R) - COMPREPLY=( $( compgen -W '\ - -c --cascade \ - -d --nodeps \ - -h --help \ - -k --dbonly \ - -n --nosave \ - -s --recursive \ - -u --unneeded \ - --config \ - --logfile \ - --noconfirm \ - --noprogressbar \ - --noscriptlet \ - -v --verbose \ - -r --root \ - -b --dbpath \ - --cachedir \ - ' -- $cur ) ) - return 0 - ;; - S) - COMPREPLY=( $( compgen -W '\ - --asdeps \ - --asexplicit \ - -c --clean \ - -d --nodeps \ - -f --force \ - -g --groups \ - -h --help \ - -i --info \ - -l --list \ - -p --print-uris \ - -s --search \ - -u --sysupgrade \ - -w --downloadonly \ - -y --refresh \ - --needed \ - --ignore \ - --ignoregroup \ - --config \ - --logfile \ - --noconfirm \ - --noprogressbar \ - --noscriptlet \ - -v --verbose \ - -r --root \ - -b --dbpath \ - --cachedir \ - ' -- $cur ) ) - return 0 - ;; - Q) - COMPREPLY=( $( compgen -W '\ - -c --changelog \ - -d --deps \ - -e --explicit \ - -g --groups \ - -h --help \ - -i --info \ - -k --check \ - -l --list \ - -m --foreign \ - -o --owns \ - -p --file \ - -s --search \ - -t --unrequired \ - -u --upgrades \ - --config \ - --logfile \ - --noconfirm \ - --noprogressbar \ - --noscriptlet \ - -v --verbose \ - -r --root \ - -b --dbpath \ - --cachedir \ - ' -- $cur ) ) - return 0 - ;; - esac - rem_selected + common=( '--cachedir --config --dbpath --debug --help --logfile --noconfirm + --noprogressbar --noscriptlet --root --verbose' 'b d h r v' ) + + core=( --query --remove --sync --upgrade --version -Q -R -S -U -V ) + + if (( COMP_CWORD == 1 )) && [[ $cur != -[^-]* ]]; then + COMPREPLY=($(compgen -W "$(echo "${core[@]}") '--help' '-h'" -- "$cur")) + return 0 + elif [[ $prev == @(-*([^- ])[br]*|--@(config|logfile|root|dbpath|cachedir)) ]]; then + _arch_rem_selected + return 0 else - case "${op}" in - U) - COMPREPLY=( $( compgen -d -- "$cur" ) \ - $( compgen -f -X '!*.pkg.tar.gz' -- "$cur" ) ) + # Main option check + for m in 'Q query' 'R remove' 'S sync' 'U upgrade' 'null'; do + if [[ $m == null ]]; then + # Go back to core if null + COMPREPLY=($(compgen -W "$(echo "${core[@]}")" -- "$cur")) return 0 - ;; - h|V) - COMPREPLY='' - return 0 - ;; - Q) - if _instring $mod g; then - _installed_groups - elif _instring $mod o; then - COMPREPLY=( $( compgen -d -- "$cur" ) \ - $( compgen -f -- "$cur" ) ) - elif _instring $mod p; then - COMPREPLY=( $( compgen -d -- "$cur" ) \ - $( compgen -f -X '!*.pkg.tar.gz' -- "$cur" ) ) - elif _instring $mod u; then - COMPREPLY='' + fi + # Break on match + [[ $COMP_LINE == @(* -*([^- ])${m% *}*|* --${m#* } *) ]] && break + done + fi + + # See if $cur is an operand before calling _pacman_pkg + case $cur in + + # Check if it's a combination option first + -"${m% *}"*) + # Generate the list from the option arrays. eval is being using on + # possible matches between query, remove, sync, upgrade and common + # arrays; completely sanitized input + eval "list=($(command sed 's,.*,\${&[1]},' <<<"${m#* }"; IFS=' '; echo ${common[1]}))" + # Parse previous arguments + for (( w=0; w<${#COMP_WORDS[@]}; w++)); do + if [[ ${COMP_WORDS[w]} == -*([^-])[hV]* ]]; then return 0 - else - _installed_pkgs fi - return 0 + for o in "${!list[@]}"; do + if [[ ${COMP_WORDS[w]} == -*([^-])*${list[o]}* ]]; then + unset 'list[o]' + fi + done + done + # Allow special double letters + case ${m% *} in + Q) [[ $cur == *([^i])i*([^i]) ]] && s+=(i) ;; + R) [[ $cur == *([^s])s*([^s]) ]] && s+=(s) ;; + S) [[ $cur == *([^c])c*([^c]) ]] && s+=(c) + [[ $cur == *([^u])u*([^u]) ]] && s+=(u) + [[ $cur == *([^y])y*([^y]) ]] && s+=(y) ;; + esac + list=("${list[@]/#/$cur}" "${s[@]/#/$cur}") + ;; + + # Use long array instead + -*) + eval "list=($(command sed 's,.*,\${&[0]},' <<<"${m#* }"; IFS=' '; echo ${common[0]}))" + for (( w=0; w<${#COMP_WORDS[@]}-1; w++)); do + if [[ ${COMP_WORDS[w]} == --@(help|version) ]]; then + return 0 + fi + for o in "${!list[@]}"; do + if [[ ${COMP_WORDS[w]} == ${list[o]} ]]; then + unset 'list[o]'; break + fi + done + done + ;; + + # The word isn't an operand; use pacman or _filedir + *) + case ${m% *} in + # The weak extglobs are due to a previous match already + # confirming validity + Q) + case $COMP_LINE in + @(* -*([^- ])g*|* --groups *)) _pacman_pkg Qgq sort ;; + @(* -*([^- ])o*|* --owns *)) ;; + @(* -*([^- ])p*|* --file *)) _filedir 'pkg.tar.gz' ;; + @(* -*([^- ])u*|* --upgrades *)) _pacman_pkg Quq ;; + *) _pacman_pkg Qq ;; + esac ;; - R) - _installed_pkgs - return 0 + R) + _pacman_pkg Qq ;; - S) - if _instring $mod l; then - _available_repos - else - _available_pkgs - fi - return 0 + S) + case $COMP_LINE in + @(* -*([^- ])l*|* --list *)) _pacman_pkg Sl sort ;; + @(* -*([^- ])g*|* --groups *)) _pacman_pkg Sg ;; + *) _pacman_pkg Slq ;; + esac ;; - esac - fi - - rem_selected + U) + _filedir 'pkg.tar.gz' + ;; + esac + _arch_rem_selected + return 0 + ;; + esac + COMPREPLY=($(compgen -W "$(echo "${list[@]}")" -- "$cur")) } -complete -o filenames -F _pacman pacman + +complete -F _makepkg -o default -o filenames -o plusdirs makepkg +complete -F _pacman -o default -o filenames -o plusdirs pacman