From 5eab784997de0e1448e81eeeff6bc7d5e81f1a1a Mon Sep 17 00:00:00 2001
From: Andres P <stderr@mail.com>
Date: Tue, 18 May 2010 17:56:47 -0430
Subject: [PATCH] [bash_completion] many improvements

0003:

* completions regarding database entries shouldn't trigger filename
  completion.

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.

Signed-off-by: Andres P <stderr@mail.com>
---
 contrib/bash_completion |  434 ++++++++++-------------------------------------
 1 files changed, 86 insertions(+), 348 deletions(-)

diff --git a/contrib/bash_completion b/contrib/bash_completion
index f0b5f9c..2713ba4 100644
--- a/contrib/bash_completion
+++ b/contrib/bash_completion
@@ -1,370 +1,108 @@
-# 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
-  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
+_pacman_file() {
+  compopt -o filenames; _filedir 'pkg.tar.*'
+}
 
-    arglen=$(( ${#toparse}-1 ))
-    for c in $(\seq 0 "${arglen}"); do
-      arg=${toparse:$c:1}
-      [ "${arg}" != "-" ] && mod="${mod}${arg}"
-    done
+_pacman() {
+  local common core cur database prev query remove sync upgrade o
+  COMPREPLY=()
+  cur=$(_get_cword)
+  prev=${COMP_WORDS[COMP_CWORD-1]}
+  database=('asdeps asexplicit')
+  query=('changelog check deps explicit file foreign groups info list owns
+          search unrequired upgrades' 'c e g i k l m o p 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 refresh search sysupgrade'
+        'c f g i l p s u w y')
+  upgrade=('asdeps asexplicit force nodeps print' 'f p')
+  common=('arch cachedir config dbpath debug help logfile noconfirm
+           noprogressbar noscriptlet quiet root verbose' 'b d h q 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|help|logfile|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'   && _pacman_file; }           ||
+          _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)
+          _pacman_file;;
+      esac
   fi
-
-  rem_selected
+  true
 }
-complete -o filenames -F _pacman pacman
+
+complete -F _makepkg -o default makepkg
+complete -F _pacman  -o default pacman
+
+# ex:et ts=2 sw=2 ft=sh
-- 
1.7.1

