about summary refs log tree commit diff
path: root/Completion
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-09-06 09:04:32 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-09-06 09:04:32 +0000
commitead1fff7dbd518efdd7799030ea773cd0d5eee31 (patch)
tree86432efcfb0d5b12b22cdbefd54f64797572fbed /Completion
parente2409e0649ac61e938624ba349988f58f873bf54 (diff)
downloadzsh-ead1fff7dbd518efdd7799030ea773cd0d5eee31.tar.gz
zsh-ead1fff7dbd518efdd7799030ea773cd0d5eee31.tar.xz
zsh-ead1fff7dbd518efdd7799030ea773cd0d5eee31.zip
zsh-workers/7650
Diffstat (limited to 'Completion')
-rw-r--r--Completion/Base/_arguments285
-rw-r--r--Completion/Core/_display76
-rw-r--r--Completion/Core/compinit28
-rw-r--r--Completion/Linux/_rpm215
-rw-r--r--Completion/User/_urls16
-rw-r--r--Completion/X/_x_color24
6 files changed, 540 insertions, 104 deletions
diff --git a/Completion/Base/_arguments b/Completion/Base/_arguments
index 5925cbc42..c8dceed26 100644
--- a/Completion/Base/_arguments
+++ b/Completion/Base/_arguments
@@ -5,22 +5,22 @@
 
 setopt localoptions extendedglob
 
-local args rest ws cur nth def nm expl descr action opt arg tmp
-local single uns ret=1 soptseq soptseq1 sopts prefix line
-local beg optbeg argbeg nargbeg inopt inrest fromrest
+local args rest ws cur nth def nm expl descr action opt arg tmp xor
+local single uns ret=1 soptseq soptseq1 sopts prefix _line odescr
+local beg optbeg argbeg nargbeg inopt inrest fromrest cmd="$words[1]"
+local matched curopt
 
 # Associative arrays used to collect information about the options.
 
-typeset -A opts dopts odopts
-typeset -A oneshot
+typeset -A opts dopts odopts xors _options
 
 # Fill the cache if we were called with different arguments.
 
 if [[ "$*" != "$_args_cache_descr" ]]; then
   _args_cache_descr="$*"
 
-  unset _args_cache_{opts,dopts,odopts,oneshot}
-  typeset -gA _args_cache_{opts,dopts,odopts,oneshot}
+  unset _args_cache_{opts,dopts,odopts,odescr,xors}
+  typeset -gA _args_cache_{opts,dopts,odopts,xors}
 
   unset _args_cache_{long,longcmd,single,rest,args,sopts,soptseq,soptseq1}
 
@@ -182,10 +182,29 @@ if [[ "$*" != "$_args_cache_descr" ]]; then
 
   # Now parse the arguments...
 
+  odescr=()
   args=()
   nth=1
   while (( $# )); do
 
+    descr=''
+    xor=''
+
+    # Get the names of other values that are mutually exclusive with
+    # this one.
+
+    if [[ "$1" = \(*\)* ]]; then
+      xor="${${1[2,-1]}%%\)*}"
+      1="${1#*\)}"
+    fi
+
+    # Get a description, if any.
+
+    if [[ "$1" = *\[*\](|:*) ]]; then
+      descr="${${1#*\[}%%\]*}"
+      1="${1/\[$descr\]}"
+    fi
+
     # Description for both the `-foo' and `+foo' form?
 
     if [[ "$1" = (\*|)(-+|+-)* ]]; then
@@ -196,12 +215,10 @@ if [[ "$*" != "$_args_cache_descr" ]]; then
       if [[ "$1" = \** ]]; then
         tmp="${1[4,-1]%%:*}"
         [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]"
-        unset "_args_cache_oneshot[-$tmp]" "_args_cache_oneshot[+$tmp]"
       else
         tmp="${1[3,-1]%%:*}"
         [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]"
-        _args_cache_oneshot[-$tmp]=yes
-        _args_cache_oneshot[+$tmp]=yes
+        xor="$xor -$tmp +$tmp"
       fi
 
       # If the option name ends in a `-', the first argument comes
@@ -222,6 +239,12 @@ if [[ "$*" != "$_args_cache_descr" ]]; then
         _args_cache_opts[-$tmp]=''
         _args_cache_opts[+$tmp]=''
       fi
+
+      _args_cache_odescr=( "$_args_cache_odescr[@]" {-,+}"${tmp}:$descr" )
+      if [[ -n "$xor" ]]; then
+        _args_cache_xors[-$tmp]="${${xor##[ 	]#}%%[ 	]#}"
+        _args_cache_xors[+$tmp]="${${xor##[ 	]#}%%[ 	]#}"
+      fi
     elif [[ "$1" = (\*|)[-+]* ]]; then
 
       # With a `*' at the beginning, the option may appear more than
@@ -230,11 +253,10 @@ if [[ "$*" != "$_args_cache_descr" ]]; then
       if [[ "$1" = \** ]]; then
         tmp="${1[2,-1]%%:*}"
         [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]"
-        unset "_args_cache_oneshot[$tmp]"
       else
         tmp="${1%%:*}"
         [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]"
-        _args_cache_oneshot[$tmp]=yes
+	xor="$xor ${tmp%\=}"
       fi
 
       # If the option name ends in a `-', the first argument comes
@@ -251,6 +273,9 @@ if [[ "$*" != "$_args_cache_descr" ]]; then
       else
         _args_cache_opts[$tmp]=''
       fi
+      _args_cache_odescr=( "$_args_cache_odescr[@]" "${tmp%[-+=]}:$descr" )
+      [[ -n "$xor" ]] && 
+          _args_cache_xors[${tmp%\=}]="${${xor##[ 	]#}%%[ 	]#}"
     elif [[ "$1" = \*::* ]]; then
 
       # This is `*:...', describing `all other arguments', with argument 
@@ -306,7 +331,8 @@ rest="$_args_cache_rest"
 opts=( "${(@kv)_args_cache_opts}" )
 dopts=( "${(@kv)_args_cache_dopts}" )
 odopts=( "${(@kv)_args_cache_odopts}" )
-oneshot=( "${(@kv)_args_cache_oneshot}" )
+odescr=( "$_args_cache_odescr[@]" )
+xors=( "${(@kv)_args_cache_xors}" )
 single="$_args_cache_single"
 
 # Parse the command line...
@@ -314,7 +340,7 @@ single="$_args_cache_single"
 ws=( "${(@)words[2,-1]}" )
 cur=$(( CURRENT-2 ))
 nth=1
-line=( "$words[1]" )
+_line=( "$words[1]" )
 beg=2
 argbeg=1
 optbeg=1
@@ -324,6 +350,14 @@ nargbeg=1
 
 while [[ cur -gt 0 ]]; do
 
+  if [[ -n "$def" && -n "$curopt" ]]; then
+    if [[ -n "$_options[$curopt]" ]]; then
+      _options[$curopt]="$_options[$curopt]:${ws[1]//:/\\:}"
+    else
+      _options[$curopt]="${ws[1]//:/\\:}"
+    fi
+  fi
+
   # `def' holds the description for the option we are currently after.
   # Check if the next argument for the option is optional.
 
@@ -339,6 +373,7 @@ while [[ cur -gt 0 ]]; do
 
   if [[ "$def" = \**[^\\]:* && "$ws[1]" = ${~${(M)def#*[^\\]:}[2,-2]} ]]; then
     def=''
+    curopt=''
     shift 1 ws
     (( cur-- ))
     (( beg++ ))
@@ -353,6 +388,7 @@ while [[ cur -gt 0 ]]; do
       argbeg="$beg"
     else
       def=''
+      curopt=''
     fi
   elif [[ -z "$def" ]]; then
 
@@ -360,6 +396,7 @@ while [[ cur -gt 0 ]]; do
 
     opt=yes
     arg=yes
+    curopt=''
   fi
 
   if [[ -n "$opt" ]]; then
@@ -374,16 +411,23 @@ while [[ cur -gt 0 ]]; do
       # associative array so that we don't offer them again.
 
       def="$opts[$ws[1]]"
+      curopt="$ws[1]"
+      _options[$curopt]=''
       optbeg="$beg"
       argbeg="$beg"
       inopt=yes
-      [[ -n "$oneshot[$ws[1]]" ]] && unset "opts[$ws[1]]"
+      if [[ -n "$xors[$ws[1]]" ]]; then
+        odescr=( "${(@)odescr:#(${(j:|:)~${=xors[$ws[1]]}}):*}" )
+        unset {{,d,od}opts,xors}\[${^=xors[$ws[1]]}\]
+      fi
     else
       uns=''
       if [[ -n "$sopts" && "$ws[1]" = [-+]${~soptseq}[$sopts] ]]; then
 	tmp="${ws[1][1]}${ws[1][-1]}"
 	if (( $+opts[$tmp] )); then
 	  def="$opts[$tmp]"
+          curopt="$tmp"
+          _options[$curopt]=''
 	  optbeg="$beg"
 	  argbeg="$beg"
           inopt=yes
@@ -425,10 +469,15 @@ while [[ cur -gt 0 ]]; do
 
 	  opt=''
 	  def="$dopts[$tmp[1]]"
+          curopt="$tmp[1]"
+	  _options[$curopt]="${ws[1]#${tmp[1]}}"
 	  optbeg="$beg"
 	  argbeg="$beg"
 	  inopt=yes
-	  [[ -n "$oneshot[$tmp[1]]" ]] && unset "dopts[$tmp[1]]"
+	  if [[ -n "$xors[$tmp[1]]" ]]; then
+            odescr=( "${(@)odescr:#(${(j:|:)~${=xors[$tmp[1]]}}):*}" )
+            unset {{,d,od}opts,xors}\[${^=xors[$tmp[1]]}\]
+          fi
 	  if [[ "$def" = [^*]*[^\\]:*[^\\]:* ]]; then
             def="${def#?*[^\\]:*[^\\]:}"
           else
@@ -453,11 +502,28 @@ while [[ cur -gt 0 ]]; do
 	if (( $#tmp )); then
 	  opt=''
 	  def="$odopts[$tmp[1]]"
-          [[ -z "$def" ]] && def="$odopts[$tmp[1]=]"
+	  curopt="$tmp[1]"
+          if [[ -z "$def" ]]; then
+	    def="$odopts[$tmp[1]=]"
+	    if [[ "$ws[1]" = ${tmp[1]}?* ]]; then
+	      _options[$curopt]="${ws[1]#${tmp[1]}=}"
+            else
+	      _options[$curopt]=''
+	    fi
+          else
+	    if [[ "$ws[1]" = ${tmp[1]}?* ]]; then
+	      _options[$curopt]="${ws[1]#${tmp[1]}}"
+            else
+	      _options[$curopt]=''
+	    fi
+	  fi
 	  optbeg="$beg"
 	  argbeg="$beg"
 	  inopt=yes
-	  [[ -n "$oneshot[$tmp[1]]" ]] && unset "odopts[$tmp[1]]"
+	  if [[ -n "$xors[$tmp[1]]" ]]; then
+            odescr=( "${(@)odescr:#(${(j:|:)~${=xors[$tmp[1]]}}):*}" )
+            unset {{,d,od}opts,xors}\[${^=xors[$tmp[1]]}\]
+          fi
 
 	  # For options whose first argument *may* come after the
 	  # option, we skip over the first description only if there
@@ -481,7 +547,7 @@ while [[ cur -gt 0 ]]; do
           uns="${ws[1][2,-1]}"
 
       if [[ -n "$uns" ]]; then
-	uns="${(j::)${(@k)oneshot[(I)${ws[1][1]}[$uns]]#[-+]}}"
+        uns="${(@j::)${(v)=xors[(I)${ws[1][1]}[$uns]]}#[-+]}"
 	tmp=(
 	  "opts[${(@)^opts[(I)${ws[1][1]}[$uns]]}]"
 	  "dopts[${(@)^dopts[(I)${ws[1][1]}[$uns]]}]"
@@ -496,7 +562,7 @@ while [[ cur -gt 0 ]]; do
 
       if [[ -n "$opt" && -n "$arg" ]]; then
         def=''
-	line=( "$line[@]" "$ws[1]" )
+	_line=( "$_line[@]" "$ws[1]" )
 	[[ -n "$inopt" ]] && nargbeg=$(( beg - 1 ))
 	inopt=''
         if [[ -z "$args[nth]" && "$rest" = \*::* ]]; then
@@ -593,7 +659,7 @@ if [[ -z "$def" || "$def" = :* ]]; then
       uns="${PREFIX[2,-1]}"
 
   if [[ -n "$uns" ]]; then
-    uns="${(j::)${(@k)oneshot[(I)${ws[1][1]}[$uns]]#[-+]}}"
+    uns="${(@j::)${(v)=xors[(I)${ws[1][1]}[$uns]]}#[-+]}"
     tmp=(
       "opts[${(@)^opts[(I)${pre[1]}[$uns]]}]"
       "dopts[${(@)^dopts[(I)${pre[1]}[$uns]]}]"
@@ -657,67 +723,134 @@ while true; do
 
     _description expl "$descr"
 
-    if [[ "$action" = \ # ]]; then
+    if [[ "$action" = -\>* ]]; then
+      line=( "$_line[@]" )
+      options=( "${(@kv)_options}" )
+      state="${${action[3,-1]##[ 	]#}%%[ 	]#}"
+      compstate[restore]=''
+      return 1
+    else
+      if [[ "${(t)line}" != *local* ]]; then
+        local line
+	typeset -A options
+      fi
 
-      # An empty action means that we should just display a message.
+      line=( "$_line[@]" )
+      options=( "${(@kv)_options}" )
 
-      _message "$descr"
-      break
+      if [[ "$action" = \ # ]]; then
 
-    elif [[ "$action" = \(\(*\)\) ]]; then
+        # An empty action means that we should just display a message.
 
-      # ((...)) contains literal strings with descriptions.
+        [[ -n "$matched" ]] && compadd -n -Q -S '' -s "$SUFFIX" - "$PREFIX"
+        _message "$descr"
+        break
 
-      eval ws\=\( "${action[3,-3]}" \)
+      elif [[ "$action" = \(\(*\)\) ]]; then
 
-      compadd -D ws - "${(@)ws%%:*}"
+        # ((...)) contains literal strings with descriptions.
 
-      if (( $#ws )); then
-        beg=1
-        for nth in "$ws[@]"; do
-          tmp="${#nth%%:*}"
-	  [[ tmp -gt beg ]] && beg="$tmp"
-        done
-        tmp=''
-        for nth in "$ws[@]"; do
-          tmp="$tmp
-${(r:beg:: :)nth%%:*} -- ${nth#*:}"
-        done
-        tmp="$tmp[2,-1]"
-        compadd "$expl[@]" -y tmp - "${(@)ws%%:*}"
-      else
-        _message "$descr"
-      fi
-    elif [[ "$action" = \(*\) ]]; then
+        eval ws\=\( "${action[3,-3]}" \)
 
-      # Anything inside `(...)' is added directly.
+	if _display tmp ws -M 'r:|[_-]=* r:|=*'; then
+          compadd "$expl[@]" -y tmp - "${(@)ws%%:*}"
+        else
+          [[ -n "$matched" ]] && compadd -Q -S -s "$SUFFIX" - "$PREFIX"
+          _message "$descr"
+        fi
+      elif [[ "$action" = \(*\) ]]; then
 
-      compadd "$expl[@]" - ${=action[2,-2]}
-    elif [[ "$action" = \{*\} ]]; then
+        # Anything inside `(...)' is added directly.
 
-      # A string in braces is evaluated.
+        compadd "$expl[@]" - ${=action[2,-2]}
+      elif [[ "$action" = \{*\} ]]; then
 
-      eval "$action[2,-2]"
+        # A string in braces is evaluated.
 
-    elif [[ "$action" = \ * ]]; then
+        eval "$action[2,-2]"
 
-      # If the action starts with a space, we just call it.
+      elif [[ "$action" = \ * ]]; then
 
-      ${(e)=~action}
-    else
+        # If the action starts with a space, we just call it.
+
+        ${(e)=~action}
+      else
+
+        # Otherwise we call it with the description-arguments built above.
+
+        action=( $=action )
+        ${(e)action[1]} "$expl[@]" ${(e)~action[2,-1]}
+      fi
+    fi
+  fi
 
-      # Otherwise we call it with the description-arguments built above.
+  # Probably add the option names.
+
+  if [[ -n "$opt" &&
+        ( nm -eq compstate[nmatches] ||
+          -z "$compconfig[option_prefix]" || 
+          "$compconfig[option_prefix]" = *\!${cmd}* ||
+          "$PREFIX" = [-+]* ) ]]; then
+    _description expl option
+    if [[ -n "$sopts" && -n "$PREFIX" &&
+      "$PREFIX" = [-+]${~soptseq}[$sopts] ]]; then
+      if [[ "$PREFIX" = [-+]${~soptseq1} ]]; then
+        if [[ -n "$compconfig[describe_options]" &&
+              "$compconfig[describe_options]" != *\!${cmd}* ]]; then
+          _display tmp odescr
+        else
+          tmp="( ${(j: :)${(@)${(@M)${=:-${(k)opts} ${(k)dopts} ${(k)odopts}}:#[-+]?(|=)}#?}%=} )"
+        fi
+        compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' -y tmp - \
+                "${PREFIX}${(@k)^opts[(I)${PREFIX[1]}?]#?}" \
+	        "${PREFIX}${(@k)^dopts[(I)${PREFIX[1]}?]#?}" \
+	        "${PREFIX}${(@)^${(@k)odopts[(I)${PREFIX[1]}?(|=)]#?}%=}" && 
+            ret=0
+      else
+        # The last option takes an argument in the next word.
 
-      action=( $=action )
-      ${(e)action[1]} "$expl[@]" ${(e)~action[2,-1]}
+        compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' - "${PREFIX}" && ret=0
+      fi
+    else
+      tmp=''
+      if [[ -n "$compconfig[describe_options]" &&
+            "$compconfig[describe_options]" != *\!${cmd}* ]]; then
+        if _display tmp odescr; then
+          if (( $#dopts )); then
+            compadd -n "$expl[@]" -QS '' -M 'r:|[_-]=* r:|=*' -y tmp - \
+                    "${(@k)dopts}" && ret=0
+            compadd -n -J option -Q -M 'r:|[_-]=* r:|=*' - \
+                    "${(@k)opts}" "${(@k)odopts[(I)*[^=]]}" && ret=0
+            compadd -n -J option -QqS= -M 'r:|[_-]=* r:|=*' - \
+                    "${(@k)odopts[(I)*=]%=}" && ret=0
+          elif (( ${(@k)#odopts[(I)*=]} )); then
+            compadd -n "$expl[@]" -QqS= -M 'r:|[_-]=* r:|=*' -y tmp - \
+                    "${(@k)odopts[(I)*=]%=}" && ret=0
+            compadd -n -J option -Q -M 'r:|[_-]=* r:|=*' - \
+                    "${(@k)opts}" "${(@k)odopts[(I)*[^=]]}" && ret=0
+          else
+            compadd -n "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' -y tmp - \
+                    "${(@k)opts}" "${(@k)odopts[(I)*[^=]]}" && ret=0
+          fi
+        fi
+      fi
+      if [[ -z "$tmp" ]]; then
+        compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' - \
+                "${(@k)opts}" "${(@k)odopts[(I)*[^=]]}" && ret=0
+        compadd "$expl[@]" -QqS= -M 'r:|[_-]=* r:|=*' - \
+                "${(@k)odopts[(I)*=]%=}" && ret=0
+        compadd "$expl[@]" -QS '' -M 'r:|[_-]=* r:|=*' - \
+                "${(@k)dopts}" && ret=0
+      fi
     fi
   fi
 
-  if [[ nm -eq compstate[nmatches] && $#_args_cache_long -ne 0 &&
-        "$PREFIX" = --*=* ]]; then
+  if [[ nm -eq compstate[nmatches] && 
+        ( -z "$single" ||
+          ( $#_args_cache_long -ne 0 && "$PREFIX" = --*=* ) ) ]]; then
     local suffix
 
-    tmp=( "${(@Mk)odopts:#--[^:]#\=}" )
+    tmp=( "${(@Mk)odopts:#[^:]#\=}" )
     prefix="${PREFIX#*\=}"
     suffix="$SUFFIX"
     PREFIX="${PREFIX%%\=*}"
@@ -729,40 +862,14 @@ ${(r:beg:: :)nth%%:*} -- ${nth#*:}"
       PREFIX="$prefix"
       SUFFIX="$suffix"
       IPREFIX="$tmp[1]"
+      matched=yes
       continue
     fi
   fi
+
   break
 done
 
-# Probably add the option names.
-
-if [[ -n "$opt" &&
-      ( nm -eq compstate[nmatches] ||
-        -z "$compconfig[option_prefix]" || "$PREFIX" = [-+]* ) ]]; then
-  _description expl option
-  if [[ -n "$sopts" && -n "$PREFIX" &&
-    "$PREFIX" = [-+]${~soptseq}[$sopts] ]]; then
-    if [[ "$PREFIX" = [-+]${~soptseq1} ]]; then
-      compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' \
-              -y "( ${(j: :)${(@)${(@M)${=:-${(k)opts} ${(k)dopts} ${(k)odopts}}:#[-+]?(|=)}#?}%=} )" - \
-              "${PREFIX}${(@k)^opts[(I)${PREFIX[1]}?]#?}" \
-	      "${PREFIX}${(@k)^dopts[(I)${PREFIX[1]}?]#?}" \
-	      "${PREFIX}${(@)^${(@k)odopts[(I)${PREFIX[1]}?(|=)]#?}%=}" && ret=0
-    else
-      # The last option takes an argument in the next word.
-
-      compadd "$expl[@]" -Q  -M 'r:|[_-]=* r:|=*' - "${PREFIX}" && ret=0
-    fi
-  else
-    compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' - \
-            "${(@k)opts}" "${(@k)odopts[(I)*[^=]]}" && ret=0
-    compadd "$expl[@]" -Q -M 'r:|[_-]=* r:|=*' -qS= - \
-            "${(@k)odopts[(I)*=]%=}" && ret=0
-    compadd "$expl[@]" -QS '' -M 'r:|[_-]=* r:|=*' - "${(@k)dopts}" && ret=0
-  fi
-fi
-
 # Set the return value.
 
 [[ nm -ne "$compstate[nmatches]" ]]
diff --git a/Completion/Core/_display b/Completion/Core/_display
new file mode 100644
index 000000000..5bddeaac1
--- /dev/null
+++ b/Completion/Core/_display
@@ -0,0 +1,76 @@
+#autoload
+
+# This builds a display-list for the `-y' option of `compadd' and
+# `compgen' out of the arguments it gets. The first argument is
+# taken as the name of a parameter and the string built is stored
+# into it.
+# The second argument is the name of an array whose elements each
+# contains a string to complete, optionally followed by a colon
+# and a description. The display list created will contain one
+# line per string with the description after it, all nicely
+# aligned. Strings without descriptions are put at the end in a
+# column-oriented fashion.
+# All arguments after the second one are given as arguments to
+# `compadd'.
+# This function will also do the matching required to find out
+# which strings will be included in the list. All elements that
+# don't match will be removed from the array. This means that the
+# special parameters `PREFI' and `SUFFIX' have to be set up 
+# correctly before this function is called.
+
+local _param="$1" _arr _len _i _tmp _simple
+
+# Remove all descriptions not matched by the string on the line.
+
+if [[ "${2[1]}" = \( ]]; then
+  _arr=( ${(o)=2[2,-2]} )
+else
+  _arr=( "${(@Po)2}" )
+fi
+
+compadd -D _arr "${(@)argv[3,-1]}" - "${(@)_arr%%:*}"
+
+[[ "${2[1]}" != \( ]] && eval "${2}=( \"\$_arr[@]\" )"
+
+if (( $#_arr )); then
+
+  # There are strings left, first get the length of the longest of
+  # them (to be able to align them) and collect all strings without
+  # descriptions.
+
+  _simple=()
+  _len=1
+  for _i in "$_arr[@]";  do
+    _tmp="${#_i%%:*}"
+    if [[ "$_i" = *:?* ]]; then
+      [[ _tmp -gt _len ]] && _len="$_tmp"
+    else
+      _simple=( "$_simple[@]" "${_i%:}" )
+    fi
+  done
+
+  # Now we build the list in `_tmp', adding one line per string.
+
+  _tmp=''
+  for _i in "$_arr[@]"; do
+    [[ "$_i" = *:?* ]] && _tmp="$_tmp
+${(r:_len:: :)_i%%:*} -- ${_i#*:}"
+  done
+
+  # If there were strings without descriptions, we just add them by
+  # calling `print -c'.
+
+  (( $#_simple )) && _tmp="${_tmp}
+$(print -c - $_simple)"
+
+  eval "${_param}=${(q)_tmp[2,-1]}"
+
+  return 0
+else
+
+  # None of the strings matches what's on the line, signal this by
+  # setting the parameter to an empty string and by the return value.
+
+  eval "${_param}=''"
+  return 1
+fi
diff --git a/Completion/Core/compinit b/Completion/Core/compinit
index 04438327c..209ecc6e8 100644
--- a/Completion/Core/compinit
+++ b/Completion/Core/compinit
@@ -320,6 +320,34 @@ compconf() {
   fi
 }
 
+# Utility function to call a function if it exists.
+#
+# Usage: call <return> <name> [ <args> ... ]
+#
+# If a function named <name> is defined (or defined to be autoloaded),
+# it is called. If <return> is given not the string `-' or empty, it is
+# taken as the name of a parameter and the return status of the function
+# called is stored in this parameter. All other arguments are given
+# to the function called.
+# The return value of this function is zero if the function was
+# called and non-zero otherwise.
+
+funcall() {
+  local _name _ret
+
+  [[ "$1" != (|-) ]] && _name="$1"
+
+  shift
+
+  if builtin functions "$1"; then
+    "$@"
+    _ret="$?"
+    [[ -n "$_name" ]] && eval "${_name}=${_ret}"
+    return 0
+  fi
+  return 1
+}
+
 # Now we automatically make the definition files autoloaded.
 
 typeset -U _i_files
diff --git a/Completion/Linux/_rpm b/Completion/Linux/_rpm
new file mode 100644
index 000000000..7fdc80ab0
--- /dev/null
+++ b/Completion/Linux/_rpm
@@ -0,0 +1,215 @@
+#compdef rpm
+
+# This uses `_arguments' in a state-machine kind of way. These states
+# have names and before executing the default action for such a state
+# we try to call a function with the name `_rpm_<state>'. If such a
+# function exists, we return with it's return status immediatly. This
+# allows users to override the default completions by simply defining
+# these functions.
+# The states (and possible values for the `<state>' above) are:
+#
+#  query
+#    complete for `rpm -q' query
+#  verify
+#    complete for `rpm --verify'
+#  install
+#    complete for `rpm -i' or `rpm --install'
+#  upgrade
+#    complete for `rpm -U' or `rpm --upgrade'
+#  uninstall
+#    complete for `rpm -e' or `rpm --erase'
+#  build_b
+#    complete for `rpm -bx' (the stage `x' is already completed)
+#  build_t
+#    complete for `rpm -tx' (the stage `x' is already completed)
+#  sigcheck
+#    complete for `rpm --sigcheck'
+#  rebuild
+#    complete for `rpm --rebuild'
+#  package
+#    complete a RPM package name
+#  package_file
+#    complete a RPM package file name
+#  package_or_file
+#    the previous two together
+#  tags
+#    complete a tag name
+#  capability
+#    complete a capability
+#  relocate
+#    complete a `old=new' pair of paths
+
+local ret=1 tmp expl
+
+# Used by `_arguments', made local here.
+
+local state lstate line
+tyeset -A options
+
+state=''
+
+# Do simple completions or get the first state.
+
+_arguments \
+  '--rcfile:resource file:_files' \
+  '--ftpproxy:FTP proxy server:_hosts' \
+  '--ftpport:FTP port number:' \
+  '-q:*:query:->query' \
+  -{V,v,vv,y,-{setperms,setugids,querytags,initdb,showrc}} \
+  '-pipe:*:pipe command:_command_names -e' \
+  '--verify:*:verify:->verify' \
+  -{i,-install}':*:install:->install' \
+  -{U,-upgrade}':*:upgrade:->upgrade' \
+  -{e,-erase}':*:uninstall:->uninstall' \
+  -'b+:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages)):*:build:->build_b' \
+  -'t+:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages)):*:build:->build_t' \
+  --{rebuild,rmsource,recompile,resign,addsign}':*:RPM package:->package' \
+  -{K,-checksig}':*:sigcheck:->sigcheck' \
+  '--rebuilddb:*:rebuild:->rebuild' && ret=0
+
+# As long as we have a state name...
+
+while [[ -n "$state" ]]; do
+
+  # First try to call a user-defined function.
+
+  funcall ret _rpm_$state && return ret
+
+  # Copy the state and reset `state', to simplify the test above.
+
+  lstate="$state"
+  state=''
+  tmp=()
+
+  # Dispatch...
+
+  case "$lstate" in
+  query)
+    _arguments \
+      -{v,vv} \
+      '--rcfile:resource file:_files' \
+      '--ftpproxy:FTP proxy server:_hosts' \
+      '--ftpport:FTP port number:' \
+      '--root:RPM root directory:_files -/' \
+      '--dbpath:RPM database path:_files -/' \
+      '--queryformat:RPM query format:->tags' \
+      '-f:file:_files' \
+      '-p:RPM package file:->package_file' \
+      '--triggeredby:RPM package:->package' \
+      '--whatprovides:RPM capability:->capability' \
+      '--whatrequires:RPM capability:->capability' \
+      '*:RPM package:->package_or_file' && ret=0
+    ;;
+  verify)
+    _arguments \
+      -{v,vv} \
+      '--rcfile:resource file:_files' \
+      '--ftpproxy:FTP proxy server:_hosts' \
+      '--ftpport:FTP port number:' \
+      --no{deps,md5,files} \
+      '--root:RPM root directory:_files -/' \
+      '--dbpath:RPM database path:_files -/' \
+      '*:RPM package:->package' && ret=0
+    ;;
+  upgrade)
+    tmp=( --oldpackage )
+    ;&
+  install)
+    _arguments "$tmp[@]" \
+      -{v,vv} \
+      '--rcfile:resource file:_files' \
+      '--ftpproxy:FTP proxy server:_hosts' \
+      '--ftpport:FTP port number:' \
+      -{-{badreloc,excludedocs,force,hash,allfiles,ignorearch,ignoreos,includedocs,justdb,nodeps,noorder,noscripts,notriggers,percent,replacefiles,replacepkgs,test},h} \
+      '--relocate:relocate:->relocate' \
+      '--prefix:package prefix directory:_files -/' \
+      '--root:RPM root directory:_files -/' \
+      '--dbpath:RPM database path:_files -/' \
+      '*:pkg file:->package_file' && ret=0
+    ;;
+  uninstall)
+    _arguments \
+      -{v,vv} \
+      '--rcfile:resource file:_files' \
+      '--ftpproxy:FTP proxy server:_hosts' \
+      '--ftpport:FTP port number:' \
+      --{allmatches,justdb,nodeps,noorder,noscripts,notriggers} \
+      '--root:RPM root directory:_files -/' \
+      '--dbpath:RPM database path:_files -/' \
+      '*:RPM package:->package' && ret=0
+    ;;
+  build_b)
+    tmp=( '*:RPM package:->package' )
+    ;&
+  build_t)
+    (( $#tmp )) || tmp=( '*:tar file:_files -g \*.\(\#i\)tar\(.\*\|\)' )
+
+    _arguments \
+      -{v,vv} \
+      '--rcfile:resource file:_files' \
+      '--ftpproxy:FTP proxy server:_hosts' \
+      '--ftpport:FTP port number:' \
+      --{short-circuit,clean,rmsource,sign,test} \
+      '--buildroot:build root directory:_files -/' \
+      '--buildarch:architecture for which to build:' \
+      '--buildos:ositecture for which to build:' \
+      '--timecheck:time check (seconds):' "$tmp[1]" && ret=0
+    ;;
+  sigcheck)
+    _arguments \
+      -{v,vv} \
+      '--rcfile:resource file:_files' \
+      '--ftpproxy:FTP proxy server:_hosts' \
+      '--ftpport:FTP port number:' \
+      --no{pgp,md5} \
+      '*:RPM package file:->package_or_file' && ret=0
+    ;;
+  rebuild)
+    _arguments \
+      -{v,vv} \
+      '--rcfile:resource file:_files' \
+      '--ftpproxy:FTP proxy server:_hosts' \
+      '--ftpport:FTP port number:' \
+      '--root:RPM root directory:_files -/' \
+      '--dbpath:RPM database path:_files -/' \
+      '*:RPM source package file:->package_file' && ret=0
+    ;;
+  package_or_file)
+    state=package_file
+    ;&
+  package)
+    _description expl 'RPM package'
+    compadd "$expl[@]" -M 'r:|-=* r:|=*' - $(rpm -qa) && ret=0
+    ;;
+  package_file)
+    if compset -P ftp:; then
+      _hosts -S/ && ret=0
+    else
+      _files -g '*.(#i)rpm' && ret=0
+    fi
+    ;;
+  tags)
+    if compset -P '*\{'; then
+      _description expl 'RPM tag'
+      compadd "$expl[@]" -M 'm:{a-z}={A-Z}' -S '}' - \
+              "${(@)${(@f)$(rpm --querytags)}#RPMTAG_}" && ret=0
+    else
+      _message 'RPM format'
+    fi
+    ;;
+  capability)
+    _message 'RPM capability'
+    ;;
+  relocate)
+    if compset -P '*\='; then
+      _description expl 'new path'
+    else
+      _description expl 'old path'
+    fi
+
+    _files "$expl[@]" -/ && ret=0
+    ;;
+  esac
+done
+
+return ret
diff --git a/Completion/User/_urls b/Completion/User/_urls
index db32aea82..ef10bb37d 100644
--- a/Completion/User/_urls
+++ b/Completion/User/_urls
@@ -6,7 +6,7 @@
 #
 # Configuration key used:
 #
-#  urls_dir
+#  urls_path
 #    The path to a directory containing a URL database, such as:
 #
 #      % cd ~/.zsh/urls
@@ -32,8 +32,8 @@ if [[ "$1" = -f ]]; then
   _files "$@" && return
 fi
 
-if [[ -z "$compconfig[urls_dir]" ]]; then
-  compconfig[urls_dir]=${ZDOTDIR:-$HOME}/.zsh/urls
+if [[ -z "$compconfig[urls_path]" ]]; then
+  compconfig[urls_path]=${ZDOTDIR:-$HOME}/.zsh/urls
 fi
 
 ipre="$IPREFIX"
@@ -52,12 +52,12 @@ case "$scheme" in
 esac
 
 if [[ "$scheme" = bookmark &&
-      -f "$compconfig[urls_dir]/$scheme/$PREFIX$SUFFIX" &&
-      -s "$compconfig[urls_dir]/$scheme/$PREFIX$SUFFIX" ]]; then
-  compadd "$@" -QU -- "$ipre$(<"$compconfig[urls_dir]/$scheme/$PREFIX$SUFFIX")"
+      -f "$compconfig[urls_path]/$scheme/$PREFIX$SUFFIX" &&
+      -s "$compconfig[urls_path]/$scheme/$PREFIX$SUFFIX" ]]; then
+  compadd "$@" -QU -- "$ipre$(<"$compconfig[urls_path]/$scheme/$PREFIX$SUFFIX")"
 else
-  dirs=($compconfig[urls_dir]/$scheme/$PREFIX*$SUFFIX(/:t))
-  files=($compconfig[urls_dir]/$scheme/$PREFIX*$SUFFIX(.:t))
+  dirs=($compconfig[urls_path]/$scheme/$PREFIX*$SUFFIX(/:t))
+  files=($compconfig[urls_path]/$scheme/$PREFIX*$SUFFIX(.:t))
   compset -P '*/'
   compadd "$@" -Q -S '/' - $dirs
   if [[ "$scheme" = bookmark ]]; then
diff --git a/Completion/X/_x_color b/Completion/X/_x_color
index 346142eea..405347ef9 100644
--- a/Completion/X/_x_color
+++ b/Completion/X/_x_color
@@ -1,5 +1,13 @@
 #autoload
 
+# This tries to automatically find the rgb.txt color database. If this
+# in an unusual place on your system or you want a personal database,
+# you can use the configuration key:
+#
+#  colors_path
+#    Path to a file containing the names of colors you want to
+#    complete. In the form of a X11 rgb.txt file.
+
 local expl
 
 if (( ! $+_color_cache )); then
@@ -7,16 +15,18 @@ if (( ! $+_color_cache )); then
 
   # Cache of color names doesn't exist yet, create it.
 
-  file=( /usr/{lib,{{X11R6,openwin},local{,/X11{,R6}}}/lib}/X11/rgb.txt(N) )
-
-  if (( $#file )); then
-    _color_cache=( "${(@)${(@f)$(< $file[1])}[2,-1]##*		}" )
+  if [[ -n "$compconfig[colors_path]" ]]; then
+    _color_cache=( "${(@)${(@f)$(< $compconfig[colors_path])}[2,-1]##*		}" )
   else
+    file=( /usr/{lib,{{X11R6,openwin},local{,/X11{,R6}}}/lib}/X11/rgb.txt(N) )
 
-    # Stupid default value.
-
-    _color_cache=(white black gray red blue green)
+    (( $#file )) &&
+        _color_cache=( "${(@)${(@f)$(< $file[1])}[2,-1]##*		}" )
   fi
+
+  # Stupid default value.
+
+  (( $#_color_cache )) || _color_cache=(white black gray red blue green)
 fi
 
 _description expl 'color specification'