From 2cc8a0f6b0700714d214b0d345019849ec8e8653 Mon Sep 17 00:00:00 2001
From: Andres P <stderr@mail.com>
Date: Sun, 9 May 2010 18:50:07 -0430
Subject: [PATCH] [bash_completion] many improvements

0002:

* add -D --database options and --print (shining)
* fix dirs showing up when they shouldn't in completions

This is now down to 106 lines. The original one (master) is 365 lines
long, yet this one retains all functionality.

0001:

Fix:
\3 Undeclared local vars with common enough names to warrant breakage

Performance issues with _pacman trying to replicate /usr/bin/pacman
with find and other slow tools.

Performance issues with expanding an array (with sometimes hundreds of
items) over three times.

Expanding said array to remove already completed entries had the side
effect of braking filenames with spaces and or \n.

The full description of fixes are already posted at:
http://bugs.archlinux.org/task/16630#comment55779
along with time diffs.
---
 contrib/bash_completion |  431 +++++++++--------------------------------------
 1 files changed, 83 insertions(+), 348 deletions(-)

diff --git a/contrib/bash_completion b/contrib/bash_completion
index f0b5f9c..842a38f 100644
--- a/contrib/bash_completion
+++ b/contrib/bash_completion
@@ -1,370 +1,105 @@
-# vim: set ft=sh ts=2 sw=2 et:
-# file: /etc/bash_completion.d/pacman
-
-# Bash completion for pacman
-# Original: Manolis Tzanidakis <mtzanidakis@freemail.gr>
-#
-# 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@caliban.org>)
-  # 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%% *} / })
+# This file is in the public domain.
+
+_arch_compgen() {
+  local i r
+  COMPREPLY=($(compgen -W '$*' -- "$cur"))
+  for ((i=1; i < ${#COMP_WORDS[@]}-1; i++)); do
+    for r in ${!COMPREPLY[@]}; do
+      if [[ ${COMP_WORDS[i]} = ${COMPREPLY[r]} ]]; then
+        unset 'COMPREPLY[r]'; break
+      fi
     done
-    \echo ${COMPREPLY[@]})))
-  return 0
-}
-
-_available_repos ()
-{
-  COMPREPLY=( $( compgen -W "$(\grep '\[' /etc/pacman.conf | \grep -v -e 'options' -e '^#' | \tr -d '[]' )" -- $cur ) )
-}
-
-_installed_pkgs ()
-{
-  local installed_pkgs
-  installed_pkgs=$( \ls /var/lib/pacman/local/ )
-  COMPREPLY=( $( compgen -W "$( for i in $installed_pkgs; do \echo ${i%-*-*}; done )" -- $cur ) )
-}
-
-_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=$( \grep '\[' /etc/pacman.conf | \grep -v -e 'options' -e '^#' | \tr -d '[]' )
-  available_pkgs=$( for r in $enabled_repos; do \echo /var/lib/pacman/sync/$r/*; done )
-  COMPREPLY=( $( compgen -W "$( for i in $available_pkgs; do j=${i##*/}; echo ${j%-*-*}; done )" -- $cur ) )
+  done
 }
 
-_installed_groups ()
-{
-  local installed_groups
-  installed_groups=$( \find /var/lib/pacman/local -name desc -exec \sed -ne '/%GROUPS%/,/^$/{//d; p}' {} \; | \sort -u )
-  COMPREPLY=( $( compgen -W "$( for i in $installed_groups; do \echo ${i%-*-*}; done )" -- $cur ) )
+_arch_ptr2comp() {
+  local list= x y
+  for x; do
+    for y in '0 --' '1 -'; do
+      eval 'set -- ${'$x'[${y% *}]}'
+      list+=\ ${@/#/${y#* }}
+    done
+  done
+  _arch_compgen $list
 }
 
-_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=$( \grep '\[' /etc/pacman.conf | \grep -v -e 'options' -e '^#' | tr -d '[]' )
-  available_groups=$( for r in $enabled_repos; do \sed '/%GROUPS%/,/^$/{//d; p}' /var/lib/pacman/sync/$r/*/desc | \sort -u; done )
-  COMPREPLY=( $( compgen -W "$( for i in $available_groups; do \echo ${i%-*-*}; done )" -- $cur ) )
+_arch_incomp() {
+  local r="\s-(-${1#* }\s|\w*${1% *})"; [[ $COMP_LINE =~ $r ]]
 }
 
-## makepkg completion
-
-_makepkg ()
-{
-  local cur prev
+_makepkg() {
+  local cur opts prev
   COMPREPLY=()
-  cur=${COMP_WORDS[COMP_CWORD]}
+  cur=$(_get_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 ) )
+  if [[ $cur = -* && ! $prev =~ ^-(-(cleancache|config|help)|\w*[Chp]) ]]; then
+    opts=('allsource asroot clean cleancache config force geninteg help
+          holdver ignorearch install log nobuild nocolor noconfirm nodeps
+          noextract noprogressbar pkg repackage rmdeps skipinteg source
+          syncdeps' 'A C L R c d e f g h i m o p r s')
+    _arch_ptr2comp opts
   fi
-
-  rem_selected
+  true
 }
-complete -o default -F _makepkg makepkg
-
-## pacman completion
 
-_instring ()
-{
-  str="${1}"
-  shift 1
-  for c in "${@}"; do
-    if [ $(\expr index "${str}" "${c}") -gt 0 ]; then
-      return 0
+_pacman_pkg() {
+  _arch_compgen "$(
+    if [[ $2 ]]; then
+      \pacman -$1 | \cut -d' ' -f1 | \sort -u
+    else
+      \pacman -$1
     fi
-  done
-  return 1
+  )"
 }
 
-_pacman ()
-{
-  local a arg toparse op mod cur
+_pacman() {
+  local common core cur prev query remove sync upgrade o
   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}"
-
-    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) 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
-
-    arglen=$(( ${#toparse}-1 ))
-    for c in $(\seq 0 "${arglen}"); do
-      arg=${toparse:$c:1}
-      [ "${arg}" != "-" ] && mod="${mod}${arg}"
-    done
+  cur=$(_get_cword)
+  prev=${COMP_WORDS[COMP_CWORD-1]}
+  database=('asdeps asexplicit')
+  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')
+  remove=('cascade dbonly nodeps nosave print recursive unneeded' 'c k n p s u')
+  sync=('asdeps asexplicit clean downloadonly force groups ignore
+         ignoregroup info list needed nodeps print quiet refresh search
+         sysupgrade' 'c f g i l p q s u w y')
+  upgrade=('asdeps asexplicit force nodeps print' 'f p')
+  common=('arch cachedir config dbpath debug help logfile noconfirm
+           noprogressbar noscriptlet root verbose' 'b d h r v')
+  core=('database help query remove sync upgrade version' 'D Q R S U V h')
+
+  for o in 'D database' 'Q query' 'R remove' 'S sync' 'U upgrade'; do
+    _arch_incomp "$o" && break
   done
 
-  cur=${COMP_WORDS[COMP_CWORD]}
-
-  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
-
-  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 \
-          -p --print \
-          --print-format \
-          --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 \
-          -p --print \
-          --print-format \
-          --cachedir \
-          ' -- $cur ) )
-        return 0
-        ;;
-      S)
-        COMPREPLY=( $( compgen -W '\
-          --asdeps \
-          --asexplicit \
-          -c --clean \
-          -d --nodeps \
-          -f --force \
-          -g --groups \
-          -h --help \
-          -i --info \
-          -l --list \
-          -s --search \
-          -u --sysupgrade \
-          -w --downloadonly \
-          -y --refresh \
-          --needed \
-          --ignore \
-          --ignoregroup \
-          --config \
-          --logfile \
-          --noconfirm \
-          --noprogressbar \
-          --noscriptlet \
-          -v --verbose \
-          -r --root \
-          -b --dbpath \
-          -p --print \
-          --print-format \
-          --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
-  else
-    case "${op}" in
-      U)
-        COMPREPLY=( $( compgen -d -- "$cur" ) \
-                    $( compgen -f -X '!*.pkg.tar.*' -- "$cur" ) )
-        return 0
-        ;;
-      h|V)
-        COMPREPLY=''
-        return 0
-        ;;
+  if [[ $? != 0 ]]; then
+    _arch_ptr2comp core
+  elif ! [[ $prev =~ ^-\w*[Vbhr] ||
+    $prev = --@(cachedir|config|dbpath|file|help|logfile|owns|root|version) ]]
+  then
+    [[ $cur = -* ]] && _arch_ptr2comp ${o#* } common ||
+      case ${o% *} in
+      D|R)
+          _pacman_pkg Qq;;
       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.*' -- "$cur" ) )
-        elif _instring $mod u; then
-          COMPREPLY=''
-          return 0
-        else
-          _installed_pkgs
-        fi
-        return 0
-        ;;
-      R)
-        _installed_pkgs
-        return 0
-        ;;
+        { _arch_incomp 'g groups' && _pacman_pkg Qg sort; } ||
+        { _arch_incomp 'p file' && _filedir 'pkg.tar.*'; } ||
+          _arch_incomp 'o owns' || _arch_incomp 'u upgrades' ||
+          _pacman_pkg Qq;;
       S)
-        if _instring $mod l; then
-          _available_repos
-        else
-          _available_pkgs
-        fi
-        return 0
-        ;;
-    esac
+        { _arch_incomp 'g groups' && _pacman_pkg Sg; } ||
+        { _arch_incomp 'l list' && _pacman_pkg Sl sort; } ||
+          _pacman_pkg Slq;;
+      U)
+          _filedir 'pkg.tar.*';;
+      esac
   fi
-
-  rem_selected
+  true
 }
-complete -o filenames -F _pacman pacman
+
+complete -F _makepkg -o default makepkg
+complete -F _pacman  -o default -o filenames pacman
+
+# ex:et ts=2 sw=2 ft=sh
-- 
1.7.1

