[pacman-dev] [PATCH] [bash_completion] Undeclared local vars/filenames with spaces/other
Andres Perera
andres87p at gmail.com
Wed Jan 20 02:27:31 EST 2010
Uh, I broke the spaces with git's 72 textwidth.
Same patch attached...
-------------- next part --------------
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 <mtzanidakis at freemail.gr>
+# pacman/makepkg completion by Andres Perera <andres87p gmail>
#
-# 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 <ian at caliban.org>)
- # 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
More information about the pacman-dev
mailing list