summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--Completion/Base/.distfiles7
-rw-r--r--Completion/Base/_argument_sets56
-rw-r--r--Completion/Base/_arguments152
-rw-r--r--Completion/Base/_describe6
-rw-r--r--Completion/Builtins/_bindkey2
-rw-r--r--Completion/Builtins/_compdef18
-rw-r--r--Completion/Builtins/_emulate2
-rw-r--r--Completion/Builtins/_zpty2
-rw-r--r--Completion/Core/_tags2
-rw-r--r--Doc/Zsh/compsys.yo66
-rw-r--r--Etc/completion-style-guide30
-rw-r--r--Src/Zle/computil.c288
13 files changed, 473 insertions, 169 deletions
diff --git a/ChangeLog b/ChangeLog
index 1d7a6b222..af223d374 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2000-04-25  Sven Wischnowsky  <wischnow@informatik.hu-berlin.de>
+
+	* 10908: Completion/Base/.distfiles, Completion/Base/_argument_sets,
+ 	Completion/Base/_arguments, Completion/Base/_describe,
+ 	Completion/Builtins/_bindkey, Completion/Builtins/_compdef,
+ 	Completion/Builtins/_emulate, Completion/Builtins/_zpty,
+ 	Completion/Core/_tags, Doc/Zsh/compsys.yo,
+ 	Etc/completion-style-guide, Src/Zle/computil.c: make _arguments
+ 	use more than one action when appropriate; add _argument_sets to
+ 	complete different sets of arguments and options for the same command
+	
 2000-04-24  Bart Schaefer  <schaefer@zsh.org>
 
 	* 10900: Src/Makefile.in: Replace a dependency on Makefile with a
diff --git a/Completion/Base/.distfiles b/Completion/Base/.distfiles
index 7e7635fa6..377a56c82 100644
--- a/Completion/Base/.distfiles
+++ b/Completion/Base/.distfiles
@@ -1,6 +1,7 @@
 DISTFILES_SRC='
     .distfiles 
-    _brace_parameter _command_names _condition _default _equal
-    _long_options _match_pattern _match_pattern.orig _match_test _parameter
-    _precommand _redirect _subscript _tilde _vars 
+    _arg_compile _argument_sets _arguments _brace_parameter _combination
+    _command_names _condition _default _describe _equal _first _jobs _math
+    _parameter _precommand _redirect _regex_arguments _subscript _tilde
+    _value _values
 '
diff --git a/Completion/Base/_argument_sets b/Completion/Base/_argument_sets
new file mode 100644
index 000000000..ad59effdc
--- /dev/null
+++ b/Completion/Base/_argument_sets
@@ -0,0 +1,56 @@
+#autoload
+
+local all ret=1 end xor has_args had_args ostate ocontext oopt_args r
+local opre="$PREFIX" oipre="$IPREFIX" ocur="$CURRENT"
+local osuf="$SUFFIX" oisuf="$ISUFFIX" owords
+
+owords="$words[@]"
+
+end=$argv[(i)-]
+[[ end -gt $# ]] && return 1
+
+all=( "${(@)argv[1,end]}" )
+
+shift end
+
+xor=()
+ostate=()
+ocontext=()
+
+while true; do
+  end=$argv[(i)-]
+
+  _arguments -M xor "$1" "$all[@]" "${(@)argv[2,end-1]}"
+  r=$?
+
+  oopt_args=( "$oopt_args[@]" "${(kv)opt_args}" )
+  if [[ r -eq 300 ]]; then
+    ret=300
+    ostate=( "$ostate[@]" "$state[@]" )
+    ocontext=( "$ocontext[@]" "$context[@]" )
+    PREFIX="$opre"   SUFFIX="$osuf"
+    IPREFIX="$oipre" ISUFFIX="$oisuf"
+    CURRENT="$ocur"  words=( "$owords[@]" )
+  elif [[ "$r$ret" = 01 ]]; then
+    ret=0
+  fi
+  
+  [[ end -gt $# ]] && break
+
+  shift end
+done
+
+opt_args=( "$oopt_args[@]" )
+
+if [[ ret -eq 300 ]]; then
+  state=( "$ostate[@]" )
+  context=( "$ocontext[@]" )
+elif [[ -z "$has_args" ]]; then
+  if [[ -n "$had_args" ]]; then
+    _message "no more arguments"
+  else
+    _message "no arguments"
+  fi
+fi
+
+return ret
diff --git a/Completion/Base/_arguments b/Completion/Base/_arguments
index f633b7cf5..9308bce71 100644
--- a/Completion/Base/_arguments
+++ b/Completion/Base/_arguments
@@ -4,7 +4,7 @@
 # descriptions given as arguments to this function.
 
 local long cmd="$words[1]" descr mesg subopts opt usecc autod
-local oldcontext="$curcontext" hasopts
+local oldcontext="$curcontext" hasopts multi ismulti
 
 long=$argv[(I)--]
 if (( long )); then
@@ -148,36 +148,37 @@ if (( long )); then
   set -- "$tmpargv[@]" "${(@P)name}"
 fi
 
+multi=(-i)
 subopts=()
-while [[ "$1" = -(O*|C) ]]; do
+while [[ "$1" = -(O*|C|M*) ]]; do
   case "$1" in
   -C) usecc=yes; shift ;;
   -O) subopts=( "${(@P)2}" ); shift 2 ;;
-  *)  subopts=( "${(@P)1[3,-1]}" ); shift ;;
+  -O*)  subopts=( "${(@P)1[3,-1]}" ); shift ;;
+  -M) ismulti=yes multi=(-I "$2" "$3"); shift 3 ;;
+  -M*) ismulti=yes multi=(-I "${1[3,-1]}" "$2"); shift 2 ;;
   esac
 done
 
 zstyle -s ":completion:${curcontext}:options" auto-description autod
 
-if (( $# )) && comparguments -i "$autod" "$@"; then
+if (( $# )) && comparguments "$multi[@]" "$autod" "$@"; then
   local nm="$compstate[nmatches]" action noargs aret expl local
   local next direct odirect equal single match matched ws tmp1 tmp2 tmp3
-  local opts subc prefix suffix
+  local opts subc tc prefix suffix descrs actions subcs
   local origpre="$PREFIX" origipre="$IPREFIX"
 
-  if comparguments -D descr action; then
-    comparguments -C subc
-    curcontext="${oldcontext%:*}:$subc"
-
+  if comparguments -D descrs actions subcs; then
     if comparguments -O next direct odirect equal; then
       opts=yes
-      _tags arguments options
+      _tags "$subcs[@]" options
     else
-      _tags arguments
+      _tags "$subcs[@]"
     fi
   else
     if comparguments -a; then
       noargs='no more arguments'
+      had_args=yes
     else
       noargs='no arguments'
     fi
@@ -187,83 +188,100 @@ if (( $# )) && comparguments -i "$autod" "$@"; then
     _tags options
   fi
 
+  context=()
+  state=()
+
   while true; do
     while _tags; do
-      if [[ -n "$matched" ]] || _requested arguments; then
-        _description arguments expl "$descr"
+      while (( $#descrs )); do
 
-        if [[ "$action" = \=\ * ]]; then
-          action="$action[3,-1]"
-          words=( "$subc" "$words[@]" )
-	  (( CURRENT++ ))
-        fi
+	action="$actions[1]"
+	descr="$descrs[1]"
+	subc="$subcs[1]"
 
-        if [[ "$action" = -\>* ]]; then
-          comparguments -W line opt_args
-          state="${${action[3,-1]##[ 	]#}%%[ 	]#}"
-	  if [[ -n "$usecc" ]]; then
-	    curcontext="${oldcontext%:*}:$subc"
-	  else
-	    context="$subc"
-	  fi
-          compstate[restore]=''
-          aret=yes
-        else
-          if [[ -z "$local" ]]; then
-            local line
-            typeset -A opt_args
-            local=yes
+        if [[ -n "$matched" ]] || _requested "$subc"; then
+
+          curcontext="${oldcontext%:*}:$subc"
+
+          _description "$subc" expl "$descr"
+
+          if [[ "$action" = \=\ * ]]; then
+            action="$action[3,-1]"
+            words=( "$subc" "$words[@]" )
+	    (( CURRENT++ ))
           fi
 
-          comparguments -W line opt_args
+          if [[ "$action" = -\>* ]]; then
+            comparguments -W line opt_args
+            state=( "$state[@]" "${${action[3,-1]##[ 	]#}%%[ 	]#}" )
+	    if [[ -n "$usecc" ]]; then
+	      curcontext="${oldcontext%:*}:$subc"
+	    else
+	      context=( "$context[@]" "$subc" )
+	    fi
+            compstate[restore]=''
+            aret=yes
+          else
+            if [[ -z "$local" ]]; then
+              local line
+              typeset -A opt_args
+              local=yes
+            fi
 
-          if [[ "$action" = \ # ]]; then
+            comparguments -W line opt_args
 
-            # An empty action means that we should just display a message.
+            if [[ "$action" = \ # ]]; then
 
-            [[ -n "$matched" ]] && compadd -n -Q -S '' -s "$SUFFIX" - "$PREFIX"
-            mesg="$descr"
+              # An empty action means that we should just display a message.
 
-          elif [[ "$action" = \(\(*\)\) ]]; then
+              [[ -n "$matched" ]] &&
+                  compadd -n -Q -S '' -s "$SUFFIX" - "$PREFIX"
+              mesg="$descr"
 
-            # ((...)) contains literal strings with descriptions.
+            elif [[ "$action" = \(\(*\)\) ]]; then
 
-            eval ws\=\( "${action[3,-3]}" \)
+              # ((...)) contains literal strings with descriptions.
 
-            _describe "$descr" ws -M "$match" "$subopts[@]"
+              eval ws\=\( "${action[3,-3]}" \)
 
-          elif [[ "$action" = \(*\) ]]; then
+              _describe -t "$subc" "$descr" ws -M "$match" "$subopts[@]"
 
-            # Anything inside `(...)' is added directly.
+            elif [[ "$action" = \(*\) ]]; then
 
-            _all_labels arguments expl "$descr" \
-                compadd "$subopts[@]" - ${=action[2,-2]}
-          elif [[ "$action" = \{*\} ]]; then
+              # Anything inside `(...)' is added directly.
 
-            # A string in braces is evaluated.
+              _all_labels "$subc" expl "$descr" \
+                  compadd "$subopts[@]" - ${=action[2,-2]}
+            elif [[ "$action" = \{*\} ]]; then
 
-            while _next_label arguments expl "$descr"; do
-              eval "$action[2,-2]"
-            done
-          elif [[ "$action" = \ * ]]; then
+              # A string in braces is evaluated.
 
-            # If the action starts with a space, we just call it.
+              while _next_label "$subc" expl "$descr"; do
+                eval "$action[2,-2]"
+              done
+            elif [[ "$action" = \ * ]]; then
 
-	    eval "action=( $action )"
-            while _next_label arguments expl "$descr"; do
-              "$action[@]"
-            done
-          else
+              # If the action starts with a space, we just call it.
+
+	      eval "action=( $action )"
+              while _next_label "$subc" expl "$descr"; do
+                "$action[@]"
+              done
+            else
 
-            # Otherwise we call it with the description-arguments.
+              # Otherwise we call it with the description-arguments.
 
-            eval "action=( $action )"
-            while _next_label arguments expl "$descr"; do
-              "$action[1]" "$subopts[@]" "$expl[@]" "${(@)action[2,-1]}"
-	    done
+              eval "action=( $action )"
+              while _next_label "$subc" expl "$descr"; do
+                "$action[1]" "$subopts[@]" "$expl[@]" "${(@)action[2,-1]}"
+	      done
+            fi
           fi
         fi
-      fi
+        shift 1 descrs
+        shift 1 actions
+        shift 1 subcs
+      done
 
       if [[ -z "$matched$hasopts" ]] && _requested options &&
           { ! zstyle -T ":completion:${curcontext}:options" prefix-needed ||
@@ -344,7 +362,11 @@ if (( $# )) && comparguments -i "$autod" "$@"; then
   [[ -n "$aret" ]] && return 300
 
   [[ -n "$mesg" ]] && _message "$mesg"
-  [[ -n "$noargs" ]] && _message "$noargs"
+  if [[ -n "$noargs" ]]; then
+    [[ -z "$ismulti" ]] && _message "$noargs"
+  else
+    has_args=yes
+  fi
 
   # Set the return value.
 
diff --git a/Completion/Base/_describe b/Completion/Base/_describe
index ca2d3e4cf..5aeeadf10 100644
--- a/Completion/Base/_describe
+++ b/Completion/Base/_describe
@@ -10,6 +10,12 @@ local _type=values _descr
 if [[ "$1" = -o ]]; then
   _type=options
   shift
+elif [[ "$1" = -t ]]; then
+  _type="$2"
+  shift 2
+elif [[ "$1" = -t* ]]; then
+  _type="${1[3,-1]}"
+  shift
 fi
 
 # Do the tests. `showd' is set if the descriptions should be shown.
diff --git a/Completion/Builtins/_bindkey b/Completion/Builtins/_bindkey
index 6f6677b59..d1e1f8ab1 100644
--- a/Completion/Builtins/_bindkey
+++ b/Completion/Builtins/_bindkey
@@ -24,7 +24,7 @@ _arguments -C -s \
   '(-l -L -d -D -A -N -m -s *)-r[unbind specified in-strings]:*:in-string' \
   '(-l -L -d -D -A -N -m -r *)-s[bind each in-string to each out-string]:*:key string' \
   '(-e -v -a -M -l -L -d -D -A -N -m)-R[interpret in-strings as ranges]' \
-  '(-l -L -d -A -N -m -r -s)*::widgets:->widget'
+  '(-l -L -d -A -N -m -r -s)*::widgets:->widget' && return 0
 
 case $state in
   keymap)
diff --git a/Completion/Builtins/_compdef b/Completion/Builtins/_compdef
index 6287810e5..649daa6dc 100644
--- a/Completion/Builtins/_compdef
+++ b/Completion/Builtins/_compdef
@@ -8,11 +8,19 @@ _arguments -C -s \
   '(-a -n -p -P -k -K)-d[delete]:*:completed command:->ccom' \
   '(-n -d -P -k -K)-p[completion for command matching pattern]:completion function:->cfun:pattern' \
   '(-n -d -p -k -K)-P[as -p for commands without own completion]:completion function:->cfun:pattern' \
-  '(-d -p -P -K)-k[define widget and key binding]:completion function:->cfun:widget name::style:->style:*:key' \
-  '(-d -p -P -k)-K[define multiple widgets based on function]:completion function:->cfun:widget name::style:->style:*:key' \
-  '1:completion function:->cfun' \
-  '2:commands:_command_names'
-  
+  '(-d -p -P -K)-k[define widget and key binding]:completion function:->cfun:style:->style:*:key' \
+  '(-d -p -P -k)-K[define multiple widgets based on function]:*::: :->multi' \
+  ':completion function:->cfun' \
+  '*:commands: _command_names' && return 0
+
+if [[ $state = multi ]]; then
+  case $(( CURRENT % 3 )) in
+  0) _message key
+     return 1;;
+  1) state=cfun;;
+  2) state=style;;
+  esac
+fi
 
 case $state in
   ccom)
diff --git a/Completion/Builtins/_emulate b/Completion/Builtins/_emulate
index 82096a77e..1c1f63cde 100644
--- a/Completion/Builtins/_emulate
+++ b/Completion/Builtins/_emulate
@@ -3,4 +3,4 @@
 _arguments -C -s \
   '-L[set local_options and local_traps as well]' \
   '-R[reset all options instead of only those needed for script portability]' \
-  '1::shell to emulate:(zsh sh ksh csh)'
+  '::shell to emulate:(zsh sh ksh csh)'
diff --git a/Completion/Builtins/_zpty b/Completion/Builtins/_zpty
index ac631baf4..e766e509e 100644
--- a/Completion/Builtins/_zpty
+++ b/Completion/Builtins/_zpty
@@ -9,7 +9,7 @@ _arguments -C -s \
   '(-e -b -d -r -L)-w[send string to command]:name:->name:*:strings to write' \
   '(-e -b -d -w -L *)-r[read string from command]:name:->name:param:_parameters' \
   '(-e -b -d -w -r)-L[list defined commands as calls]' \
-  '(-r)*::args:_normal'
+  '(-r)*::args:_normal' && return 0
 
 if [[ $state = name ]]; then
   list=( ${${(f)"$(zpty)"}#*\) } )
diff --git a/Completion/Core/_tags b/Completion/Core/_tags
index 496f5b7e0..c98990cec 100644
--- a/Completion/Core/_tags
+++ b/Completion/Core/_tags
@@ -53,7 +53,7 @@ if (( $# )); then
     "$_sort_tags" "$@"
   else
     zstyle -a ":completion:${curcontext}:" tag-order order ||
-        order=('arguments values' options)
+        order=('(|*-)argument-* (|*-)option-* values' options)
 
     for tag in $order; do
       case $tag in
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 332dc2702..8267819e3 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -331,7 +331,7 @@ enditemize()
 
 As an example, the context name
 
-example(tt(:completion::complete:dvips:-o-1:files))
+example(tt(:completion::complete:dvips:option-o-1:files))
 
 says that normal completion was attempted on an argument of the tt(dvips)
 command (more precisely: completion was attempted on the first argument
@@ -1501,7 +1501,7 @@ This style is used when completing arguments of the Debian `tt(dpkg)'
 program.  It contains an override for the default package set
 for a given context.  For example,
 
-example(zstyle ':completion:*:complete:dpkg:--status-1:' packageset avail)
+example(zstyle ':completion:*:complete:dpkg:option--status-1:' packageset avail)
 
 causes available packages, rather than only installed packages,
 to be completed for `dpkg --status'.
@@ -2879,11 +2879,11 @@ exclusive. Such a list is given in parentheses at the beginning, as in
 `tt((-two -three 1)-one:...)' or `tt((-foo):...)'. In the first
 example, the options `tt(-two)' and `tt(-three)' and the first
 argument will not be offered as possible completions if the option
-`tt(-one)' is on the line. Also, the list may contain a single star as
-one of its elements to specify that the description for the rest
-arguments should not be used and it may contain a colon to specify
-that the descriptions for all normal (non-option-) arguments should
-not be used.
+`tt(-one)' is on the line before the cursor. Also, the list may
+contain a single star as one of its elements to specify that the
+description for the rest arguments should not be used and it may
+contain a colon to specify that the descriptions for all normal
+(non-option-) arguments should not be used.
 
 In each of the cases above, the var(action) says how the possible
 completions should be generated. In cases where only one of a fixed
@@ -2896,9 +2896,10 @@ matches will be listed together with their descriptions if the
 tt(description) style for the tt(values) tag is set.
 
 An var(action) of the form `tt(->)var(string)' is used by functions
-that implement a state machine. In this case, the `var(string)' (with
-all leading and trailing spaces and tabs removed) will be stored in
-the global parameter tt(state) and the function returns with a return
+that implement a state machine. In this case, the `var(string)'s (with
+all leading and trailing spaces and tabs removed) of all actions that
+have to be used will be stored in
+the global array tt(state) and the function returns with a return
 value of 300 (to make it distinguishable from other return values)
 after setting the global `tt(context)', `tt(line)' and `tt(opt_args)'
 parameters as described below and without resetting any changes made
@@ -2965,13 +2966,15 @@ and their arguments. These are stored in the associative array
 `tt(opt_args)', using the option names as keys and their arguments as
 the values. For options that have more than one argument these are
 given as one string, separated by colons. All colons in the original
-arguments are preceded with backslashes. The parameter `tt(context)'
-will be set to the automatically created context name. This is either
-a string of the form `var(-opt)tt(-)var(n)' for the var(n)'th argument 
+arguments are preceded with backslashes.
+
+The parameter `tt(context)'
+will be set to the automatically created context names. This are either
+strings of the form `tt(option)var(-opt)tt(-)var(n)' for the var(n)'th argument 
 of the option var(-opt), or a string of the form `tt(argument-)var(n)' 
 for the var(n)'th argument (for rest arguments the var(n) is the
 string `tt(rest)'). For example, when completing the argument of the tt(-o)
-option, the name is `tt(-o-1)' and for the second normal (non-option-)
+option, the name is `tt(option-o-1)' and for the second normal (non-option-)
 argument it is `tt(argument-2)'.
 
 Also, during the evaluation of the var(action), the context name in
@@ -3091,6 +3094,41 @@ arguments. The first one describes the first argument as a
 be completed. The last description says that all other arguments are
 `var(page numbers)' but does not give possible completions.
 )
+findex(_argument_sets)
+item(tt(_argument_sets) var(sets) ...)(
+This is like tt(_arguments) but allows to specify multiple sets of
+options and arguments. The arguments are sets of specifications for
+tt(_arguments) separated by single hyphens. The specifications before
+the first hyphen are shared by all sets given after the first
+hyphen. The first word in every other set gives the name of the
+set. This name may appear in exclusion lists in the specifications,
+either alone or before (with a `tt(-)' between the name and the rest)
+one of the possible values described for tt(_arguments) above.
+
+For example:
+
+example(_argument_sets \
+    -a \
+  - set1 \
+    -c \
+  - set2 \
+    -d \
+    ':arg:(x2 y2)')
+
+This defines two sets. When the command line contains the option
+`tt(-c)', the `tt(-d)' option and the argument will not be considered
+possible completions. When it contains `tt(-d)' or an argument, the
+option `tt(-c)' will not be completed any more, but if `tt(-a)' is
+given, both sets will still be considered valid, because it appears
+before the first hyphen, so both sets contain this option.
+
+Don't expect too much with complicated options that get their
+arguments in the same string and `tt(->)var(state)' actions or with
+the tt(-C) option that is given to tt(_arguments), otherwise most
+things should work. Note that the contexts reported in the tt(context) 
+array and the options in the tt(opt_args) association are prefixed
+with the set names and a hyphen.
+)
 findex(_values)
 item(tt(_values) var(specs) ...)(
 This is used to complete values (strings) and their arguments or
diff --git a/Etc/completion-style-guide b/Etc/completion-style-guide
index 71b5c9a5c..a626c79fe 100644
--- a/Etc/completion-style-guide
+++ b/Etc/completion-style-guide
@@ -27,7 +27,7 @@ by giving it to functions like `_tags' via the `-C' options, as in:
 
   local context ...
   ...
-  _arguments ... '-foo:foo:->foo'
+  _arguments ... '-foo:foo:->foo' && return 0
   ...
   if [[ "$state" = foo ]]; then
     _tags -C "$context" ...
@@ -47,7 +47,7 @@ reported back to functions you call. E.g.:
 
   local curcontext="$curcontext" ...
   ...
-  _arguments -C ... 'foo:foo:->foo'
+  _arguments -C ... 'foo:foo:->foo' && return 0
   ...
   if [[ "$state" = foo ]]; then
     _tags ...
@@ -60,6 +60,32 @@ value changed by `_arguments' and `_values' is only used in your
 function (and make sure to initialise it to its old value as in the
 example).
 
+All this only works if the specifications given to `_arguments' define 
+options and arguments that are completely separate. If there is more
+than one `->state' action and more than one of them might be needed
+for the same word, you'll have to use a loop:
+
+  local state context line i expl ret=1
+  ...
+  _arguments \
+      '::arg1:->arg1' \
+      '*:args:->rest' && return 0
+
+  while (( $#state )); do
+    case "$state[1]" in
+    arg1) _wanted -C "$context[1]" foo expl 'foo' compadd - foo1 foo2 && ret=0;;
+    rest) _wanted -C "$context[1]" bar expl 'bar' compadd - bar1 bar2 && ret=0;;
+    esac
+    shift 1 state
+    shift 1 context
+  done
+
+  return ret
+
+As you can see, `state' and `context' are really arrays. In this
+example, completion for the first argument has to complete both `foo's 
+and `bar's.
+
 Then, before adding the matches, see if matches of that type are
 requested by the user in the current context. If you will add only one 
 type of matches, this is very simple. You can use the function
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 709e8f1ab..29a5fcb21 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -304,6 +304,7 @@ struct cadef {
     char *match;		/* -M spec to use */
     int argsactive;		/* if arguments are still allowed */
 				/* used while parsing a command line */
+    char *set;			/* set name, shared */
 };
 
 /* Description for an option. */
@@ -317,6 +318,7 @@ struct caopt {
     Caarg args;			/* option arguments */
     int active;			/* still allowed on command line */
     int num;			/* it's the num'th option */
+    char *set;			/* set name, shared */
 };
 
 #define CAO_NEXT    1
@@ -335,7 +337,10 @@ struct caarg {
     char *end;			/* end-pattern for ::<pat>:... */
     char *opt;			/* option name if for an option */
     int num;			/* it's the num'th argument */
+    int min;			/* it's also this argument, using opt. args */
+    int direct;			/* number was given directly */
     int active;			/* still allowed on command line */
+    char *set;			/* set name, shared */
 };
 
 #define CAA_NORMAL 1
@@ -393,6 +398,7 @@ freecadef(Cadef d)
 	Caopt p, n;
 
 	zsfree(d->match);
+	zsfree(d->set);
 	if (d->defs)
 	    freearray(d->defs);
 
@@ -454,7 +460,8 @@ bslashcolon(char *s)
 /* Parse an argument definition. */
 
 static Caarg
-parse_caarg(int mult, int type, int num, char *oname, char **def)
+parse_caarg(int mult, int type, int num, int opt, char *oname, char **def,
+	    char *set)
 {
     Caarg ret = (Caarg) zalloc(sizeof(*ret));
     char *p = *def, *d, sav;
@@ -463,8 +470,11 @@ parse_caarg(int mult, int type, int num, char *oname, char **def)
     ret->descr = ret->action = ret->end = NULL;
     ret->xor = NULL;
     ret->num = num;
+    ret->min = num - opt;
     ret->type = type;
     ret->opt = ztrdup(oname);
+    ret->direct = 0;
+    ret->set = set;
 
     /* Get the description. */
 
@@ -498,16 +508,22 @@ parse_caarg(int mult, int type, int num, char *oname, char **def)
 /* Parse an array of definitions. */
 
 static Cadef
-parse_cadef(char *nam, char **args)
+parse_cadef(char *nam, char **args, int multi)
 {
     Cadef ret;
     Caopt *optp;
+    Caarg argp;
     char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor;
-    char *adpre, *adsuf;
+    char *adpre, *adsuf, *set = NULL, *doset = NULL;
     int single = 0, anum = 1, xnum, nopts, ndopts, nodopts;
 
     nopts = ndopts = nodopts = 0;
 
+    if (multi) {
+	if (!args[1])
+	    return NULL;
+	set = tricat(*args++, "-", "");
+    }
     /* First string is the auto-description definition. */
 
     for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++);
@@ -551,6 +567,7 @@ parse_cadef(char *nam, char **args)
     ret->defs = zarrdup(oargs);
     ret->ndefs = arrlen(oargs);
     ret->lastt = time(0);
+    ret->set = set;
     if (single) {
 	ret->single = (Caopt *) zalloc(256 * sizeof(Caopt));
 	memset(ret->single, 0, 256 * sizeof(Caopt));
@@ -561,6 +578,10 @@ parse_cadef(char *nam, char **args)
     /* Get the definitions. */
 
     for (optp = &(ret->opts); *args; args++) {
+        if (args[0][0] == '-' && !args[0][1]) {
+	    doset = set;
+	    continue;
+	}
 	p = dupstring(*args);
 	xnum = 0;
 	if (*p == '(') {
@@ -689,7 +710,7 @@ parse_cadef(char *nam, char **args)
 		/* There's at least one argument. */
 
 		Caarg *oargp = &oargs;
-		int atype, rest, oanum = 1;
+		int atype, rest, oanum = 1, onum = 0;
 		char *end;
 
 		/* Loop over the arguments. */
@@ -736,7 +757,10 @@ parse_cadef(char *nam, char **args)
 
 		    /* And the definition. */
 
-		    *oargp = parse_caarg(!rest, atype, oanum++, name, &p);
+		    *oargp = parse_caarg(!rest, atype, oanum++, onum,
+					 name, &p, doset);
+		    if (atype == CAA_OPT)
+			onum++;
 		    if (end)
 			(*oargp)->end = ztrdup(end);
 		    oargp = &((*oargp)->next);
@@ -751,6 +775,7 @@ parse_cadef(char *nam, char **args)
 	    optp = &((*optp)->next);
 
 	    opt->next = NULL;
+	    opt->set = doset;
 	    opt->name = ztrdup(rembslashcolon(name));
 	    if (descr)
 		opt->descr = ztrdup(descr);
@@ -810,15 +835,15 @@ parse_cadef(char *nam, char **args)
 		} else
 		    type = CAA_RARGS;
 	    }
-	    ret->rest = parse_caarg(0, type, -1, NULL, &p);
+	    ret->rest = parse_caarg(0, type, -1, 0, NULL, &p, doset);
 	    ret->rest->xor = xor;
 	} else {
 	    /* It's a normal argument definition. */
 
-	    int type = CAA_NORMAL;
+	    int type = CAA_NORMAL, direct;
 	    Caarg arg, tmp, pre;
 
-	    if (idigit(*p)) {
+	    if ((direct = idigit(*p))) {
 		/* Argment number is given. */
 		int num = 0;
 
@@ -840,8 +865,9 @@ parse_cadef(char *nam, char **args)
 		type = CAA_OPT;
 		p++;
 	    }
-	    arg = parse_caarg(0, type, anum - 1, NULL, &p);
+	    arg = parse_caarg(0, type, anum - 1, 0, NULL, &p, doset);
 	    arg->xor = xor;
+	    arg->direct = direct;
 
 	    /* Sort the new definition into the existing list. */
 
@@ -865,7 +891,13 @@ parse_cadef(char *nam, char **args)
     ret->nopts = nopts;
     ret->ndopts = ndopts;
     ret->nodopts = nodopts;
-    
+
+    for (argp = ret->args, xnum = 0; argp; argp = argp->next) {
+	if (!argp->direct)
+	    argp->min = argp->num - xnum;
+	if (argp->type == CAA_OPT)
+	    xnum++;
+    }
     return ret;
 }
 
@@ -873,7 +905,7 @@ parse_cadef(char *nam, char **args)
  * are newly built. */
 
 static Cadef
-get_cadef(char *nam, char **args)
+get_cadef(char *nam, char **args, int multi)
 {
     Cadef *p, *min, new;
     int i, na = arrlen(args);
@@ -887,7 +919,7 @@ get_cadef(char *nam, char **args)
 	    min = p;
     if (i)
 	min = p;
-    if ((new = parse_cadef(nam, args))) {
+    if ((new = parse_cadef(nam, args, multi))) {
 	freecadef(*min);
 	*min = new;
     }
@@ -974,10 +1006,10 @@ ca_get_arg(Cadef d, int n)
     if (d->argsactive) {
 	Caarg a = d->args;
 
-	while (a && a->num < n)
+	while (a && (n < a->min || n > a->num))
 	    a = a->next;
 
-	if (a && a->num == n && a->active)
+	if (a && a->min <= n && a->num >= n && a->active)
 	    return a;
 
 	return (d->rest && d->rest->active ? d->rest : NULL);
@@ -987,20 +1019,32 @@ ca_get_arg(Cadef d, int n)
 
 /* Use a xor list, marking options as inactive. */
 
-static void
-ca_inactive(Cadef d, char **xor)
+static LinkList ca_xor;
+
+static int
+ca_inactive(Cadef d, char **xor, int cur)
 {
-    if (xor) {
+    if (xor && cur <= compcurrent) {
 	Caopt opt;
-
-	for (; *xor; xor++) {
-	    if (xor[0][0] == ':' && !xor[0][1])
+	char *x;
+	int sl = (d->set ? strlen(d->set) : -1);
+
+	for (; (x = *xor); xor++) {
+	    if (ca_xor)
+		addlinknode(ca_xor, x);
+	    if (sl > 0) {
+		if (strpfx(d->set, x))
+		    x += sl;
+		else if (!strncmp(d->set, x, sl - 1))
+		    return 1;
+	    }
+	    if (x[0] == ':' && !x[1])
 		d->argsactive = 0;
-	    else if (xor[0][0] == '*' && !xor[0][1]) {
+	    else if (x[0] == '*' && !x[1]) {
 		if (d->rest)
 		    d->rest->active = 0;
-	    } else if (xor[0][0] >= '0' && xor[0][0] <= '9') {
-		int n = atoi(xor[0]);
+	    } else if (x[0] >= '0' && x[0] <= '9') {
+		int n = atoi(x);
 		Caarg a = d->args;
 
 		while (a && a->num < n)
@@ -1008,10 +1052,11 @@ ca_inactive(Cadef d, char **xor)
 
 		if (a && a->num == n)
 		    a->active = 0;
-	    } else if ((opt = ca_get_opt(d, *xor, 1, NULL)))
+	    } else if ((opt = ca_get_opt(d, x, 1, NULL)))
 		opt->active = 0;
 	}
     }
+    return 0;
 }
 
 /* State when parsing a command line. */
@@ -1022,7 +1067,7 @@ struct castate {
     Caarg def, ddef;
     Caopt curopt;
     int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos;
-    int inopt, inrest, inarg, nth, doff, singles;
+    int inopt, inrest, inarg, nth, doff, singles, oopt;
     LinkList args;
     LinkList *oargs;
 };
@@ -1030,10 +1075,10 @@ struct castate {
 static struct castate ca_laststate;
 static int ca_parsed = 0, ca_alloced = 0;
 
-/* Pars a command line. */
+/* Parse a command line. */
 
-static void
-ca_parse_line(Cadef d)
+static int
+ca_parse_line(Cadef d, int multi)
 {
     Caarg adef, ddef;
     Caopt ptr, wasopt;
@@ -1073,7 +1118,7 @@ ca_parse_line(Cadef d)
     state.curopt = NULL;
     state.argbeg = state.optbeg = state.nargbeg = state.restbeg =
 	state.nth = state.inopt = state.inarg = state.opt = state.arg = 1;
-    state.inrest = state.doff = state.singles = state.doff = 0;
+    state.inrest = state.doff = state.singles = state.doff = state.oopt = 0;
     state.curpos = compcurrent;
     state.args = znewlinklist();
     state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
@@ -1086,7 +1131,7 @@ ca_parse_line(Cadef d)
     if (!compwords[1]) {
 	ca_laststate.opt = ca_laststate.arg = 0;
 
-	return;
+	return 0;
     }
     /* Loop over the words from the line. */
 
@@ -1095,7 +1140,8 @@ ca_parse_line(Cadef d)
 	ddef = adef = NULL;
 	doff = state.singles = 0;
 
-	ca_inactive(d, argxor);
+	if (ca_inactive(d, argxor, cur))
+	    return 1;
 
 	/* We've a definition for an argument, skip to the next. */
 
@@ -1104,7 +1150,8 @@ ca_parse_line(Cadef d)
 	    if (state.curopt)
 		zaddlinknode(state.oargs[state.curopt->num], ztrdup(line));
 
-	    state.opt = (state.def->type == CAA_OPT);
+	    if ((state.opt = (state.def->type == CAA_OPT)) && state.def->opt)
+		state.oopt++;
 
 	    if (state.def->type == CAA_REST || state.def->type == CAA_RARGS ||
 		state.def->type == CAA_RREST) {
@@ -1145,7 +1192,8 @@ ca_parse_line(Cadef d)
 
 	    state.oargs[state.curopt->num] = znewlinklist();
 
-	    ca_inactive(d, state.curopt->xor);
+	    if (ca_inactive(d, state.curopt->xor, cur))
+		return 1;
 
 	    /* Collect the argument strings. Maybe. */
 
@@ -1184,7 +1232,8 @@ ca_parse_line(Cadef d)
 		if ((tmpopt = d->single[STOUC(*p)])) {
 		    state.oargs[tmpopt->num] = znewlinklist();
 
-		    ca_inactive(d, tmpopt->xor);
+		    if (ca_inactive(d, tmpopt->xor, cur))
+			return 1;
 		}
 	    }
 	    if (state.def &&
@@ -1203,12 +1252,16 @@ ca_parse_line(Cadef d)
 		state.opt = 0;
 	    else
 		state.curopt = NULL;
-	} else if (state.arg) {
+	} else if (multi && (*line == '-' || *line == '+') && cur != compcurrent)
+	    return 1;
+	else if (state.arg) {
 	    /* Otherwise it's a normal argument. */
 	    if (state.inopt) {
 		state.inopt = 0;
 		state.nargbeg = cur - 1;
 	    }
+	    if (!d->args && !d->rest)
+		return 1;
 	    if ((adef = state.def = ca_get_arg(d, state.nth)) &&
 		(state.def->type == CAA_RREST ||
 		 state.def->type == CAA_RARGS)) {
@@ -1291,6 +1344,7 @@ ca_parse_line(Cadef d)
 	    }
 	}
     }
+    return 0;
 }
 
 /* Build a colon-list from a list. */
@@ -1327,6 +1381,88 @@ ca_colonlist(LinkList l)
 	return ztrdup("");
 }
 
+static void
+ca_set_data(char *opt, Caarg arg, char **args, int single)
+{
+    LinkList descr, act, subc;
+    char nbuf[40], *buf;
+    int restr = 0, onum, miss = 0, rest, oopt = 1, lopt = 0, addopt;
+
+    descr = newlinklist();
+    act = newlinklist();
+    subc = newlinklist();
+
+ rec:
+
+    addopt = (opt ? 0 : ca_laststate.oopt);
+
+    for (; arg && (arg->num < 0 ||
+		   (arg->min <= ca_laststate.nth + addopt &&
+		    arg->num >= ca_laststate.nth));) {
+	if ((lopt = arg->type == CAA_OPT) && !opt && oopt > 0)
+	    oopt = 0;
+
+	addlinknode(descr, arg->descr);
+	addlinknode(act, arg->action);
+
+	if (!restr) {
+	    if ((restr = (arg->type == CAA_RARGS)))
+		restrict_range(ca_laststate.optbeg, arrlen(compwords) - 1);
+	    else if ((restr = (arg->type == CAA_RREST)))
+		restrict_range(ca_laststate.argbeg, arrlen(compwords) - 1);
+	}
+	if (arg->opt) {
+	    buf = (char *) zhalloc((arg->set ? strlen(arg->set) : 0) +
+				   strlen(arg->opt) + 40);
+	    if (arg->num > 0)
+		sprintf(buf, "%soption%s-%d",
+			(arg->set ? arg->set : ""), arg->opt, arg->num);
+	    else
+		sprintf(buf, "%soption%s-rest",
+			(arg->set ? arg->set : ""), arg->opt);
+	} else if (arg->num > 0) {
+	    sprintf(nbuf, "argument-%d", arg->num);
+	    buf = (arg->set ? dyncat(arg->set, nbuf) : dupstring(nbuf));
+	} else
+	    buf = (arg->set ? dyncat(arg->set, "argument-rest") :
+		   dupstring("argument-rest"));
+
+	addlinknode(subc, buf);
+
+	if (single)
+	    break;
+
+	if (!opt && arg->num >= 0 && !arg->next && miss)
+	    arg = ca_laststate.d->rest;
+	else {
+	    onum = arg->num;
+	    rest = (onum != arg->min && onum == ca_laststate.nth);
+	    if ((arg = arg->next)) {
+		if (arg->num != onum + 1)
+		    miss = 1;
+	    } else if (rest || (oopt > 0 && !opt)) {
+		arg = ca_laststate.d->rest;
+		oopt = -1;
+	    }
+	}
+    }
+    if (!single && opt && lopt) {
+	opt = NULL;
+	arg = ca_get_arg(ca_laststate.d, ca_laststate.nth);
+
+	goto rec;
+    }
+    if (!opt && oopt > 0) {
+	oopt = -1;
+	arg = ca_laststate.d->rest;
+
+	goto rec;
+    }
+    set_list_array(args[0], descr);
+    set_list_array(args[1], act);
+    set_list_array(args[2], subc);
+}
+
 static int
 bin_comparguments(char *nam, char **args, char *ops, int func)
 {
@@ -1340,14 +1476,14 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 	zwarnnam(nam, "invalid argument: %s", args[0], 0);
 	return 1;
     }
-    if (args[0][1] != 'i' && !ca_parsed) {
+    if (args[0][1] != 'i' && args[0][1] != 'I' && !ca_parsed) {
 	zwarnnam(nam, "no parsed state", NULL, 0);
 	return 1;
     }
     switch (args[0][1]) {
-    case 'i': min = 2; max = -1; break;
-    case 'D': min = 2; max =  2; break;
-    case 'C': min = 1; max =  1; break;
+    case 'i':
+    case 'I': min = 2; max = -1; break;
+    case 'D': min = 3; max =  3; break;
     case 'O': min = 4; max =  4; break;
     case 'L': min = 3; max =  4; break;
     case 's': min = 1; max =  1; break;
@@ -1368,17 +1504,43 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
     }
     switch (args[0][1]) {
     case 'i':
+    case 'I':
 	if (compcurrent > 1 && compwords[0]) {
-	    Cadef def = get_cadef(nam, args + 1);
+	    Cadef def;
 	    int cap = ca_parsed;
+	    LinkList cax = ca_xor;
 
 	    ca_parsed = 0;
 
-	    if (!def)
-		return 1;
+	    if (args[0][1] == 'I') {
+		char **xor;
+
+		if (!(def = get_cadef(nam, args + 2, 1)))
+		    return 1;
 
-	    ca_parsed = cap;
-	    ca_parse_line(def);
+		ca_parsed = cap;
+		ca_xor = newlinklist();
+		if ((xor = getaparam(args[1]))) {
+		    if (arrcontains(xor, args[2], 0) ||
+			ca_inactive(def, xor, compcurrent)) {
+			ca_xor = cax;
+			return 1;
+		    }
+		}
+		if (ca_parse_line(def, 1)) {
+		    ca_xor = cax;
+		    return 1;
+		}
+		set_list_array(args[1], ca_xor);
+	    } else {
+		if (!(def = get_cadef(nam, args + 1, 0)))
+		    return 1;
+
+		ca_parsed = cap;
+		ca_xor = NULL;
+		ca_parse_line(def, 0);
+	    }
+	    ca_xor = cax;
 	    ca_parsed = 1;
 
 	    return 0;
@@ -1390,35 +1552,11 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 	    Caarg arg = ca_laststate.def;
 
 	    if (arg) {
-		setsparam(args[1], ztrdup(arg->descr));
-		setsparam(args[2], ztrdup(arg->action));
-
 		if (ca_laststate.doff > 0)
 		    ignore_prefix(ca_laststate.doff);
-		if (arg->type == CAA_RARGS)
-		    restrict_range(ca_laststate.optbeg,
-				   arrlen(compwords) - 1);
-		else if (arg->type == CAA_RREST)
-		    restrict_range(ca_laststate.argbeg,
-				   arrlen(compwords) - 1);
-		return 0;
-	    }
-	    return 1;
-	}
-    case 'C':
-	{
-	    Caarg arg = ca_laststate.def;
 
-	    if (arg) {
-		char buf[20];
+		ca_set_data(arg->opt, arg, args + 1, (ca_laststate.doff > 0));
 
-		if (arg->num > 0)
-		    sprintf(buf, "%d", arg->num);
-		else
-		    strcpy(buf, "rest");
-
-		setsparam(args[1], (arg->opt ? tricat(arg->opt, "-", buf) :
-				    tricat("argument-", buf, "")));
 		return 0;
 	    }
 	    return 1;
@@ -1474,11 +1612,7 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 	    Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL);
 
 	    if (opt && opt->args) {
-		setsparam(args[2], ztrdup(opt->args->descr));
-		setsparam(args[3], ztrdup(opt->args->action));
-
-		if (args[4])
-		    setsparam(args[4], tricat(opt->name, "-1", ""));
+		ca_set_data(opt->name, opt->args, args + 2, 1);
 
 		return 0;
 	    }
@@ -1528,7 +1662,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 	    for (o = ca_laststate.d->opts, a = ca_laststate.oargs; o;
 		 o = o->next, a++) {
 		if (*a) {
-		    *p++ = ztrdup(o->name);
+		    *p++ = (o->set ? tricat(o->set, o->name, "") :
+			    ztrdup(o->name));
 		    *p++ = ca_colonlist(*a);
 		}
 	    }
@@ -1740,7 +1875,7 @@ parse_cvdef(char *nam, char **args)
 		vtype = CVV_OPT;
 	    } else
 		vtype = CVV_ARG;
-	    arg = parse_caarg(0, 0, 0, name, &p);
+	    arg = parse_caarg(0, 0, 0, 0, name, &p, NULL);
 	} else {
 	    vtype = CVV_NOARG;
 	    arg = NULL;
@@ -2243,6 +2378,7 @@ settags(int level, char **tags)
 
 /* Check if an array contains a string. */
 
+/**/
 static int
 arrcontains(char **a, char *s, int colon)
 {