mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 18:10:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			908 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			908 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| #autoload
 | |
| 
 | |
| # Complete the arguments of the current command according to the
 | |
| # descriptions given as arguments to this function.
 | |
| 
 | |
| setopt localoptions extendedglob
 | |
| 
 | |
| local args rest ws cur nth def nm expl descr action opt arg tmp xor
 | |
| local single uns ret=1 aret soptseq soptseq1 sopts prefix _line odescr
 | |
| local beg optbeg argbeg nargbeg inopt inrest fromrest cmd="$words[1]"
 | |
| local matched curopt noargs i tmp1 tmp2 tmp3 suffix match
 | |
| 
 | |
| # Associative arrays used to collect information about the options.
 | |
| 
 | |
| 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,odescr,xors}
 | |
|   typeset -gA _args_cache_{opts,dopts,odopts,xors}
 | |
| 
 | |
|   unset _args_cache_{long,longcmd,single,match,rest,args,sopts,soptseq,soptseq1}
 | |
| 
 | |
|   # Default match spec.
 | |
| 
 | |
|   _args_cache_match='r:|[_-]=* r:|=*'
 | |
| 
 | |
|   # See if we are using single-letter options or have a match spec.
 | |
| 
 | |
|   while [[ "$1" = -(s|M*) ]]; do
 | |
|     if [[ "$1" = -s ]]; then
 | |
|       shift
 | |
|       _args_cache_single=yes
 | |
|     elif [[ "$1" = -M?* ]]; then
 | |
|       _args_cache_match="${1[3,-1]}"
 | |
|       shift
 | |
|     else
 | |
|       _args_cache_match="$2"
 | |
|       shift 2
 | |
|     fi
 | |
|   done
 | |
| 
 | |
|   # See if we support long options, too.
 | |
| 
 | |
|   nth=$argv[(I)--]
 | |
|   if (( nth )); then
 | |
|     local tmpargv
 | |
| 
 | |
|     if [[ nth -eq 1 ]]; then
 | |
|       tmpargv=()
 | |
|     else
 | |
|       tmpargv=( "${(@)argv[1,nth-1]}" )
 | |
|     fi
 | |
| 
 | |
|     tmp=${~words[1]}
 | |
|     if [[ "$tmp" != /* ]]; then
 | |
|       tmp="$PWD/$tmp"
 | |
|     fi
 | |
| 
 | |
|     if [[ "$tmp" != "$_args_cache_longcmd" ]]; then
 | |
|       local iopts pattern tmpo
 | |
|       typeset -U lopts
 | |
| 
 | |
|       _args_cache_longcmd="$tmp"
 | |
| 
 | |
|       # We have to build the long-option cache anew, get the `-i' and
 | |
|       # `-s' options.
 | |
| 
 | |
|       set -- "${(@)argv[nth+1,-1]}"
 | |
| 
 | |
|       iopts=()
 | |
|       sopts=()
 | |
|       while [[ "$1" = -[is]* ]]; do
 | |
|         if [[ "$1" = -??* ]]; then
 | |
| 	  tmp="${1[3,-1]}"
 | |
| 	  cur=1
 | |
|         else
 | |
|           tmp="$2"
 | |
| 	  cur=2
 | |
|         fi
 | |
| 	if [[ "$tmp[1]" = '(' ]]; then
 | |
| 	  tmp=( ${=tmp[2,-2]} )
 | |
|         else
 | |
| 	  tmp=( "${(@P)tmp}" )
 | |
|         fi
 | |
| 	if [[ "$1" = -i* ]]; then
 | |
|           iopts=( "$iopts[@]" "$tmp[@]" )
 | |
|         else
 | |
|           sopts=( "$sopts[@]" "$tmp[@]" )
 | |
|         fi
 | |
| 	shift cur
 | |
|       done
 | |
| 
 | |
|       # Now get the long option names by calling the command with `--help'.
 | |
|       # The parameter expansion trickery first gets the lines as separate
 | |
|       # array elements. Then we select all lines whose first non-blank
 | |
|       # character is a hyphen. Since some commands document more than one
 | |
|       # option per line, separated by commas, we convert commas int
 | |
|       # newlines and then split the result again at newlines after joining 
 | |
|       # the old array elements with newlines between them. Then we select
 | |
|       # those elements that start with two hyphens, remove anything up to
 | |
|       # those hyphens and anything from the space or comma after the
 | |
|       # option up to the end. 
 | |
| 
 | |
|       lopts=("--${(@)^${(@)${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$(${~words[1]} --help 2>&1)//\[--/
 | |
| --}:#[ 	]#-*}//,/
 | |
| }}:#[ 	]#--*}#*--}%%[, ]*}:#}")
 | |
| 
 | |
|       # Now remove all ignored options ...
 | |
| 
 | |
|       while (( $#iopts )); do
 | |
|         lopts=( ${lopts:#$~iopts[1]} )
 | |
|         shift iopts
 | |
|       done
 | |
| 
 | |
|       # ... and add "same" options
 | |
| 
 | |
|       while (( $#sopts )); do
 | |
|         lopts=( $lopts ${lopts/$sopts[1]/$sopts[2]} )
 | |
|         shift 2 sopts
 | |
|       done
 | |
| 
 | |
|       # Then we walk through the descriptions plus a few builtin ones.
 | |
| 
 | |
|       set -- "$@" '*=FILE*:file:_files' \
 | |
|              '*=(DIR|PATH)*:directory:_files -/' '*: :'
 | |
| 
 | |
|       while (( $# )); do
 | |
| 
 | |
|         # First, we get the pattern and the action to use and take them
 | |
|         # from the positional parameters.
 | |
| 
 | |
|         pattern="${${${(M)1#*[^\\]:}[1,-2]}//\\\\:/:}"
 | |
|         descr="${1#${pattern}}"
 | |
|         shift
 | |
| 
 | |
|         # We get all options matching the pattern and take them from the
 | |
|         # list we have built. If no option matches the pattern, we
 | |
|         # continue with the next.
 | |
| 
 | |
|         tmp=("${(@M)lopts:##$~pattern}")
 | |
|         lopts=("${(@)lopts:##$~pattern}")
 | |
| 
 | |
|         (( $#tmp )) || continue
 | |
| 
 | |
|         opt=''
 | |
| 
 | |
|         # If there are option strings with a `[=', we take these get an
 | |
|         # optional argument.
 | |
| 
 | |
|         tmpo=("${(@M)tmp:#*\[\=*}")
 | |
|         if (( $#tmpo )); then
 | |
|           tmp=("${(@)tmp:#*\[\=*}")
 | |
|           tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
 | |
| 
 | |
| 	  if [[ "$descr" = ::* ]]; then
 | |
| 	    _args_cache_long=( "$_args_cache_long[@]"
 | |
| 	                       "${(@)^tmpo}=${descr}" )
 | |
|           else
 | |
| 	    _args_cache_long=( "$_args_cache_long[@]"
 | |
| 	                       "${(@)^tmpo}=:${descr}" )
 | |
|           fi
 | |
|         fi
 | |
| 
 | |
| 	# Descriptions with `=': mandatory argument.
 | |
| 
 | |
|         tmpo=("${(@M)tmp:#*\=*}")
 | |
|         if (( $#tmpo )); then
 | |
|           tmp=("${(@)tmp:#*\=*}")
 | |
|           tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
 | |
| 
 | |
| 	  if [[ "$descr" = ::* ]]; then
 | |
| 	    _args_cache_long=( "$_args_cache_long[@]"
 | |
| 	                       "${(@)^tmpo}=${descr[2,-1]}" )
 | |
|           else
 | |
| 	    _args_cache_long=( "$_args_cache_long[@]"
 | |
| 	                       "${(@)^tmpo}=${descr}" )
 | |
|           fi
 | |
|         fi
 | |
| 
 | |
| 	# Everything else is just added as a option without arguments.
 | |
| 
 | |
|         if (( $#tmp )); then
 | |
|           tmp=("${(@)tmp//[^a-zA-Z0-9-]}")
 | |
| 	  _args_cache_long=( "$_args_cache_long[@]" "$tmp[@]" )
 | |
| 	fi
 | |
|       done
 | |
|     fi
 | |
|     _args_cache_long=( "${(@)_args_cache_long:# #}" )
 | |
| 
 | |
|     set -- "$tmpargv[@]" "$_args_cache_long[@]"
 | |
|   fi
 | |
| 
 | |
|   # 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\]}"
 | |
|     elif [[ -n "$compconfig[autodescribe_options]" &&
 | |
|             "$1" = [-+][^:]##:[^:]#[^\\]:[^:]# ]]; then
 | |
|       descr="${${${${(M)${1#*:}#*[^\\]:}[1,-2]}## #}%% #}"
 | |
|       [[ -n "$descr" ]] && descr="${compconfig[autodescribe_options]//\\%d/$descr}"
 | |
|     fi
 | |
| 
 | |
|     # Description for both the `-foo' and `+foo' form?
 | |
| 
 | |
|     if [[ "$1" = (\*|)(-+|+-)[^:]* ]]; then
 | |
| 
 | |
|       # With a `*' at the beginning, the option may appear more than
 | |
|       # once.
 | |
| 
 | |
|       if [[ "$1" = \** ]]; then
 | |
|         tmp="${1[4,-1]%%:*}"
 | |
|         [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]"
 | |
|       else
 | |
|         tmp="${1[3,-1]%%:*}"
 | |
|         [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]"
 | |
|         xor="$xor -$tmp +$tmp"
 | |
|       fi
 | |
| 
 | |
|       # If the option name ends in a `-', the first argument comes
 | |
|       # directly after the option, if it ends in a `+', the first
 | |
|       # argument *may* come directly after the option, otherwise it
 | |
|       # is in the next word.
 | |
| 
 | |
|       if [[ "$1" = [^:]##-:* ]]; then
 | |
|         _args_cache_dopts[-$tmp]="${1#*:}"
 | |
|         _args_cache_dopts[+$tmp]="${1#*:}"
 | |
|       elif [[ "$1" = [^:]##[+=]:* ]]; then
 | |
|         _args_cache_odopts[-$tmp]="${1#*:}"
 | |
|         _args_cache_odopts[+$tmp]="${1#*:}"
 | |
|       elif [[ "$1" = *:* ]]; then
 | |
|         _args_cache_opts[-$tmp]="${1#*:}"
 | |
|         _args_cache_opts[+$tmp]="${1#*:}"
 | |
|       else
 | |
|         _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
 | |
|       # once.
 | |
| 
 | |
|       if [[ "$1" = \** ]]; then
 | |
|         tmp="${1[2,-1]%%:*}"
 | |
|         [[ "$tmp" = *[-+] ]] && tmp="$tmp[1,-2]"
 | |
|       else
 | |
|         tmp="${1%%:*}"
 | |
|         [[ "$tmp" = [-+]?*[-+] ]] && tmp="$tmp[1,-2]"
 | |
| 	xor="$xor ${tmp%\=}"
 | |
|       fi
 | |
| 
 | |
|       # If the option name ends in a `-', the first argument comes
 | |
|       # directly after the option, if it ends in a `+', the first
 | |
|       # argument *may* come directly after the option, otherwise it
 | |
|       # is in the next word.
 | |
| 
 | |
|       if [[ "$1" = [^:]##-:* ]]; then
 | |
|         _args_cache_dopts[$tmp]="${1#*:}"
 | |
|       elif [[ "$1" = [^:]##[+=]:* ]]; then
 | |
|         _args_cache_odopts[$tmp]="${1#*:}"
 | |
|       elif [[ "$1" = *:* ]]; then
 | |
|         _args_cache_opts[$tmp]="${1#*:}"
 | |
|       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 
 | |
|       # range restriction.
 | |
| 
 | |
|       if [[ "$1" = \*:::* ]]; then
 | |
|         _args_cache_rest="*${1[3,-1]}"
 | |
|       else
 | |
|         _args_cache_rest="$1"
 | |
|       fi
 | |
|     elif [[ "$1" = \*:* ]]; then
 | |
| 
 | |
|       # This is `*:...', describing `all other arguments'.
 | |
| 
 | |
|       _args_cache_rest="${1[3,-1]}"
 | |
|     elif [[ "$1" = :* ]]; then
 | |
| 
 | |
|       # This is `:...', describing `the next argument'.
 | |
| 
 | |
|       _args_cache_args[nth++]="${1#*:}"
 | |
|     else
 | |
| 
 | |
|       # And this is `n:...', describing the `n'th argument.
 | |
| 
 | |
|       _args_cache_args[${1%%:*}]="${1#*:}"
 | |
|       nth=$(( ${1%%:*} + 1 ))
 | |
|     fi
 | |
|     shift
 | |
|   done
 | |
| 
 | |
|   if [[ -n "$_args_cache_single" ]]; then
 | |
|     _args_cache_soptseq="${(@j::)${(@M)${(@k)_args_cache_opts[(R)]}:#[-+]?}#[-+]}"
 | |
|     if [[ -n "$_args_cache_soptseq" ]]; then
 | |
|       _args_cache_soptseq="[$_args_cache_soptseq]#"
 | |
|       _args_cache_soptseq1="$_args_cache_soptseq#"
 | |
|     else
 | |
|       _args_cache_soptseq=''
 | |
|       _args_cache_soptseq1=''
 | |
|     fi
 | |
|     _args_cache_sopts="${(@j::)${(@)${(@M)${=:-${(k)_args_cache_opts} ${(k)_args_cache_dopts} ${(k)_args_cache_odopts}}:#[-+]?(|=)}#?}%\=}"
 | |
|   else
 | |
|     _args_cache_soptseq=''
 | |
|     _args_cache_soptseq1=''
 | |
|     _args_cache_sopts=''
 | |
|   fi
 | |
| fi
 | |
| 
 | |
| soptseq="$_args_cache_soptseq"
 | |
| soptseq1="$_args_cache_soptseq1"
 | |
| sopts="$_args_cache_sopts"
 | |
| args=( "$_args_cache_args[@]" )
 | |
| rest="$_args_cache_rest"
 | |
| opts=( "${(@kv)_args_cache_opts}" )
 | |
| dopts=( "${(@kv)_args_cache_dopts}" )
 | |
| odopts=( "${(@kv)_args_cache_odopts}" )
 | |
| odescr=( "$_args_cache_odescr[@]" )
 | |
| xors=( "${(@kv)_args_cache_xors}" )
 | |
| single="$_args_cache_single"
 | |
| match="$_args_cache_match"
 | |
| 
 | |
| # Parse the command line...
 | |
| 
 | |
| ws=( "${(@)words[2,-1]}" )
 | |
| cur=$(( CURRENT-2 ))
 | |
| nth=1
 | |
| _line=( "$words[1]" )
 | |
| beg=2
 | |
| argbeg=1
 | |
| optbeg=1
 | |
| nargbeg=1
 | |
| 
 | |
| # ...until the current word is reached.
 | |
| 
 | |
| 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.
 | |
| 
 | |
|   if [[ "$def" = :* ]]; then
 | |
|     opt=yes
 | |
|   else
 | |
|     opt=''
 | |
|   fi
 | |
|   arg=''
 | |
| 
 | |
|   # See if we are after an option getting n arguments ended by something
 | |
|   # that matches the current word.
 | |
| 
 | |
|   if [[ "$def" = \**[^\\]:* && "$ws[1]" = ${~${(M)def#*[^\\]:}[2,-2]} ]]; then
 | |
|     def=''
 | |
|     curopt=''
 | |
|     shift 1 ws
 | |
|     (( cur-- ))
 | |
|     (( beg++ ))
 | |
|     continue
 | |
|   fi
 | |
| 
 | |
|   # Remove one description/action pair from `def' if that isn't empty.
 | |
| 
 | |
|   if [[ -n "$def" && "$def" != \** ]]; then
 | |
|     if [[ "$def" = ?*[^\\]:*[^\\]:* ]]; then
 | |
|       def="${def#?*[^\\]:*[^\\]:}"
 | |
|       argbeg="$beg"
 | |
|     else
 | |
|       def=''
 | |
|       curopt=''
 | |
|     fi
 | |
|   elif [[ -z "$def" ]]; then
 | |
| 
 | |
|     # Make sure we test for options below and handle normal arguments.
 | |
| 
 | |
|     opt=yes
 | |
|     arg=yes
 | |
|     curopt=''
 | |
|   fi
 | |
| 
 | |
|   if [[ -n "$opt" ]]; then
 | |
| 
 | |
|     # `opt' was set above if we have to test if the word is an option.
 | |
|     # We first test for the simple options -- those without arguments or
 | |
|     # those whose arguments have to be given as separate words.
 | |
| 
 | |
|     if (( $+opts[$ws[1]] )); then
 | |
| 
 | |
|       # Options that may only be given once are removed from the
 | |
|       # 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
 | |
|       if [[ -n "$xors[$ws[1]]" ]]; then
 | |
| 	if [[ "$xors[$ws[1]]" = (*\ |):(\ *|) ]]; then
 | |
|           args=()
 | |
| 	  rest=''
 | |
|         fi
 | |
|         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"
 | |
| 	  for i in ${(s::)ws[1][2,-1]}; do
 | |
|             _options[${ws[1][1]}$i]=''
 | |
| 	  done
 | |
| 	  optbeg="$beg"
 | |
| 	  argbeg="$beg"
 | |
|           inopt=yes
 | |
| 	  uns="${ws[1][2,-1]}"
 | |
| 	  opt=''
 | |
| 	fi
 | |
|       fi
 | |
| 
 | |
|       # If the word is none of the simple options, test for those
 | |
|       # whose first argument has to or may come directly after the
 | |
|       # option. This is done in two loops looking very much alike.
 | |
| 
 | |
|       if [[ -n "$opt" && $#dopts -ne 0 ]]; then
 | |
| 
 | |
| 	# First we get the option names.
 | |
| 
 | |
| 	tmp=( "${(@k)dopts}" )
 | |
| 
 | |
| 	# Then we loop over them and see if the current word begins
 | |
| 	# with one of the option names.
 | |
| 
 | |
| 	while (( $#tmp )); do
 | |
|           if [[ -n "$sopts" && $tmp[1] = [-+]? ]]; then
 | |
| 	    if [[ "$ws[1]" = ${tmp[1][1]}${~soptseq}${tmp[1][2]}* ]]; then
 | |
| 	      uns="${ws[1][2,-1]%%${tmp[1][2]}*}${tmp[1][2]}"
 | |
| 	      break;
 | |
| 	    fi
 | |
| 	  elif [[ "$ws[1]" = ${tmp[1]}* ]]; then
 | |
|             break
 | |
| 	  fi
 | |
| 	  shift 1 tmp
 | |
| 	done
 | |
| 
 | |
| 	if (( $#tmp )); then
 | |
| 
 | |
| 	  # It does. So use the description for it, but only from
 | |
| 	  # the second argument on, because we are searching the
 | |
| 	  # description for the next command line argument.
 | |
| 
 | |
| 	  opt=''
 | |
| 	  def="$dopts[$tmp[1]]"
 | |
|           curopt="$tmp[1]"
 | |
| 	  _options[$curopt]="${ws[1]#${tmp[1]}}"
 | |
| 	  optbeg="$beg"
 | |
| 	  argbeg="$beg"
 | |
| 	  inopt=yes
 | |
| 	  if [[ -n "$xors[$tmp[1]]" ]]; then
 | |
| 	    if [[ "$xors[$ws[1]]" = (*\ |):(\ *|) ]]; then
 | |
|               args=()
 | |
| 	      rest=''
 | |
|             fi
 | |
|             odescr=( "${(@)odescr:#(${(j:|:)~${=xors[$tmp[1]]}}):*}" )
 | |
|             unset {{,d,od}opts,xors}\[${^=xors[$tmp[1]]}\]
 | |
|           fi
 | |
| 	  if [[ "$def" = [^*]*[^\\]:*[^\\]:* ]]; then
 | |
|             def="${def#?*[^\\]:*[^\\]:}"
 | |
|           elif [[ "$def" != \** ]]; then
 | |
|             def=''
 | |
| 	  fi
 | |
|         fi
 | |
|       fi
 | |
|       if [[ -n "$opt" && $#odopts -ne 0 ]]; then
 | |
| 	tmp=( "${(@k)odopts%\=}" )
 | |
| 	while (( $#tmp )); do
 | |
|           if [[ -n "$sopts" && $tmp[1] = [-+]? ]]; then
 | |
| 	    if [[ "$ws[1]" = ${tmp[1][1]}${~soptseq}${tmp[1][2]}* ]]; then
 | |
| 	      uns="${ws[1][2,-1]%%${tmp[1][2]}*}${tmp[1][2]}"
 | |
| 	      break;
 | |
| 	    fi
 | |
| 	  elif [[ "$ws[1]" = ${tmp[1]}* ]]; then
 | |
| 	    break
 | |
| 	  fi
 | |
| 	  shift 1 tmp
 | |
| 	done
 | |
| 
 | |
| 	if (( $#tmp )); then
 | |
| 	  opt=''
 | |
| 	  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
 | |
| 	  if [[ -n "$xors[$tmp[1]]" ]]; then
 | |
| 	    if [[ "$xors[$ws[1]]" = (*\ |):(\ *|) ]]; then
 | |
|               args=()
 | |
| 	      rest=''
 | |
|             fi
 | |
|             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
 | |
| 	  # is something after the option name on the line.
 | |
| 
 | |
| 	  if [[ ( -z "$sopts" && ( "$def" = :* || "$ws[1]" != "$tmp[1]" ) ) ||
 | |
|                 ( -n "$sopts" && ( ( $tmp[1] = [-+]? && ( "$def" = :* || "$ws[1]" != "${tmp[1][1]}"${~soptseq}"${tmp[1][2]}" ) ) ||
 | |
| 		  		   ( $tmp[1] != [-+]? && ( "$def" = :* || "$ws[1]" != "$tmp[1]" ) ) ) ) ]]; then
 | |
| 	    if [[ "$def" = [^*]*[^\\]:*[^\\]:* ]]; then
 | |
|               def="${def#?*[^\\]:*[^\\]:}"
 | |
| 	      optbeg="$beg"
 | |
| 	      argbeg="$beg"
 | |
|             elif [[ "$def" != \** ]]; then
 | |
|               def=''
 | |
|             fi
 | |
| 	  fi
 | |
|         fi
 | |
|       fi
 | |
| 
 | |
|       [[ -n "$sopts" && -n "$opt" && "$ws[1]" = [-+]${~soptseq} ]] && \
 | |
|           uns="${ws[1][2,-1]}"
 | |
| 
 | |
|       if [[ -n "$uns" ]]; then
 | |
|         uns="${(@j::)${(M@)${(v)=xors[(I)${ws[1][1]}[$uns]]}:#??}#[-+]}"
 | |
| 	if  [[ -n "$uns" ]]; then
 | |
| 	  tmp=(
 | |
| 	    "opts[${(@)^opts[(I)${ws[1][1]}[$uns]]}]"
 | |
| 	    "dopts[${(@)^dopts[(I)${ws[1][1]}[$uns]]}]"
 | |
| 	    "odopts[${(@)^odopts[(I)${ws[1][1]}[$uns]]}]"
 | |
| 	    "xors[${(@)^xors[(I)${ws[1][1]}[$uns]]}]"
 | |
| 	  )
 | |
| 	  odescr=( "${(@)odescr:#${ws[1][1]}[$uns]:*}" )
 | |
| 	  (( $#tmp )) && unset "$tmp[@]"
 | |
|         fi
 | |
|       fi
 | |
| 
 | |
|       # If we didn't find a matching option description and we were
 | |
|       # told to use normal argument descriptions, just increase
 | |
|       # our counter `nth'.
 | |
| 
 | |
|       if [[ -n "$opt" && -n "$arg" ]]; then
 | |
|         def=''
 | |
| 	_line=( "$_line[@]" "$ws[1]" )
 | |
| 	[[ -n "$inopt" ]] && nargbeg=$(( beg - 1 ))
 | |
| 	inopt=''
 | |
|         if [[ -z "$args[nth]" && "$rest" = \*::* ]]; then
 | |
| 	  inrest=yes
 | |
| 	  break
 | |
|         fi
 | |
| 	(( nth++ ))
 | |
|       fi
 | |
|     fi
 | |
|   fi
 | |
| 
 | |
|   shift 1 ws
 | |
|   (( cur-- ))
 | |
|   (( beg++ ))
 | |
| done
 | |
| 
 | |
| [[ -n "$inopt" ]] && nargbeg=$(( beg - 1 ))
 | |
| 
 | |
| # Now generate the matches.
 | |
| 
 | |
| nm="$compstate[nmatches]"
 | |
| 
 | |
| if [[ -z "$def" || "$def" = :* ]]; then
 | |
|   local pre="$PREFIX"
 | |
| 
 | |
|   uns=''
 | |
| 
 | |
|   # We either don't have a description for an argument of an option
 | |
|   # or we have a description for a optional argument.
 | |
| 
 | |
|   opt=yes
 | |
| 
 | |
|   if [[ -z "$def" ]]; then
 | |
| 
 | |
|     # If we have none at all, use the one for this argument position.
 | |
| 
 | |
|     def="$args[nth]"
 | |
|     if [[ -z "$def" ]]; then
 | |
|       def="$rest"
 | |
|       optbeg="$nargbeg"
 | |
|       argbeg="$nargbeg"
 | |
|       fromrest=yes
 | |
|       [[ -n "$inrest" ]] && opt=''
 | |
|     fi
 | |
|     if [[ -z "$def" ]]; then
 | |
|       if [[ -z "$args$rest" ]]; then
 | |
|         noargs='no arguments'
 | |
|       else
 | |
|         noargs='no more arguments'
 | |
|       fi
 | |
|     fi
 | |
|   fi
 | |
| 
 | |
|   # In any case, we have to complete option names here, but we may
 | |
|   # be in a string that starts with an option name and continues with
 | |
|   # the first argument, test that (again, two loops).
 | |
| 
 | |
|   if [[ -n "$opt" && $#dopts -ne 0 ]]; then
 | |
| 
 | |
|     # Get the option names.
 | |
| 
 | |
|     tmp=( "${(@k)dopts}" )
 | |
|     prefix="$PREFIX"
 | |
|     while (( $#tmp )); do
 | |
|       if [[ -n "$sopts" && $tmp[1] = [-+]? ]] && compset -P "${tmp[1][1]}${~soptseq}${tmp[1][2]}"; then
 | |
|         def="$dopts[$tmp[1]]"
 | |
| 	opt=''
 | |
| 	uns="${prefix[2,-1]%%${tmp[1][2]}*}${tmp[1][2]}"
 | |
| 	for i in ${(s::)prefix[2,-1]%%${tmp[1][2]}*} ${tmp[1][2]}; do
 | |
| 	  _options[${prefix[1]}$i]=''
 | |
| 	done
 | |
| 	noargs=''
 | |
| 	break
 | |
|       elif compset -P "$tmp[1]"; then
 | |
| 
 | |
| 	# The current string starts with the option name, so ignore
 | |
| 	# that and complete the rest of the string.
 | |
| 
 | |
| 	def="$dopts[$tmp[1]]"
 | |
| 	opt=''
 | |
| 	noargs=''
 | |
| 	break
 | |
|       fi
 | |
|       shift 1 tmp
 | |
|     done
 | |
|   fi
 | |
|   if [[ -n "$opt" && $#odopts -ne 0 ]]; then
 | |
|     tmp=( "${(@k)odopts}" )
 | |
|     prefix="$PREFIX"
 | |
|     while (( $#tmp )); do
 | |
|       if [[ -n "$sopts" && $tmp[1] = [-+]?(|=) ]] && compset -P "${tmp[1][1]}${~soptseq}${tmp[1][2]}${tmp[1][3]}"; then
 | |
|         def="$odopts[$tmp[1]]"
 | |
| 	opt=''
 | |
| 	uns="${prefix[2,-1]%%${tmp[1][2]}*}${tmp[1][2]}"
 | |
| 	for i in ${(s::)prefix[2,-1]%%${tmp[1][2]}*} ${tmp[1][2]}; do
 | |
| 	  _options[${prefix[1]}$i]=''
 | |
| 	done
 | |
| 	noargs=''
 | |
| 	break
 | |
|       elif compset -P "$tmp[1]"; then
 | |
| 	def="$odopts[$tmp[1]]"
 | |
| 	opt=''
 | |
| 	noargs=''
 | |
| 	break
 | |
|       fi
 | |
|       shift 1 tmp
 | |
|     done
 | |
|   fi
 | |
| 
 | |
|   [[ -n "$sopts" && -n "$opt" && "$PREFIX" = [-+]${~soptseq}[$sopts] ]] &&
 | |
|       uns="${PREFIX[2,-1]}"
 | |
| 
 | |
|   if [[ -n "$uns" ]]; then
 | |
|     uns="${(@j::)${(M@)${(v)=xors[(I)${ws[1][1]}[$uns]]}:#??}#[-+]}"
 | |
|     if [[ -n "$uns" ]]; then
 | |
|       tmp=(
 | |
|         "opts[${(@)^opts[(I)${pre[1]}[$uns]]}]"
 | |
|         "dopts[${(@)^dopts[(I)${pre[1]}[$uns]]}]"
 | |
|         "odopts[${(@)^odopts[(I)${pre[1]}[$uns](|=)]}]"
 | |
|         "xors[${(@)^xors[(I)${pre[1]}[$uns]]}]"
 | |
|       )
 | |
|       odescr=( "${(@)odescr:#${pre[1]}[$uns]:*}" )
 | |
|       (( $#tmp )) && unset "$tmp[@]"
 | |
|     fi
 | |
|   fi
 | |
| 
 | |
|   # If we aren't in an argument directly after a option name, all option
 | |
|   # names are possible matches.
 | |
| 
 | |
|   [[ -z "$opt" || ( "$def" = \** &&
 | |
|                     ( -z "$fromrest" || CURRENT -ne argbeg+1 ) ) ]] && opt=''
 | |
| else
 | |
|   opt=''
 | |
| fi
 | |
| 
 | |
| # Now add the matches from the description, if any.
 | |
| 
 | |
| while true; do
 | |
| 
 | |
|   if [[ -n "$def" ]]; then
 | |
| 
 | |
|     # Ignore the leading colon or `*...' describing optional arguments.
 | |
| 
 | |
|     if [[ "$def" = :* ]]; then
 | |
|       def="$def[2,-1]"
 | |
|     elif [[ "$def" = \** ]]; then
 | |
|       tmp="${${(M)def#*[^\\]:}[2,-2]}"
 | |
|       def="${def#*[^\\]:}"
 | |
| 
 | |
|       if [[ "$def" = :* ]]; then
 | |
|         if [[ "$def" = ::* ]]; then
 | |
|           def="$def[3,-1]"
 | |
| 	  beg=$argbeg
 | |
|         else
 | |
| 	  def="$def[2,-1]"
 | |
| 	  beg=$optbeg
 | |
|         fi
 | |
| 
 | |
|         [[ beg -ge $#words ]] && beg=$(( $#words - 1 ))
 | |
| 
 | |
|         shift beg words
 | |
|         (( CURRENT -= beg ))
 | |
| 
 | |
|         if [[ -n "$tmp" ]]; then
 | |
|           tmp="$words[(ib:CURRENT:)${~tmp}]"
 | |
| 	  [[ tmp -le $#words ]] && words=( "${(@)words[1,tmp-1]}" )
 | |
|         fi
 | |
|       fi
 | |
|     fi
 | |
| 
 | |
|     # Get the description and the action.
 | |
| 
 | |
|     descr="${${${(M)def#*[^\\]:}[1,-2]}//\\\\:/:}"
 | |
|     if [[ "$def" = *[^\\]:*[^\\]:* ]]; then
 | |
|       action="${${${(M)${def#*[^\\]:}#*[^\\]:}[1,-2]}//\\\\:/:}"
 | |
|     else
 | |
|       action="${${def#*[^\\]:}//\\\\:/:}"
 | |
|     fi
 | |
| 
 | |
|     _description expl "$descr"
 | |
| 
 | |
|     if [[ "$action" = -\>* ]]; then
 | |
|       line=( "$_line[@]" )
 | |
|       options=( "${(@kv)_options}" )
 | |
|       state="${${action[3,-1]##[ 	]#}%%[ 	]#}"
 | |
|       compstate[restore]=''
 | |
|       aret=yes
 | |
|     else
 | |
|       if [[ "${(t)line}" != *local* ]]; then
 | |
|         local line
 | |
| 	typeset -A options
 | |
|       fi
 | |
| 
 | |
|       line=( "$_line[@]" )
 | |
|       options=( "${(@kv)_options}" )
 | |
| 
 | |
|       if [[ "$action" = \ # ]]; then
 | |
| 
 | |
|         # An empty action means that we should just display a message.
 | |
| 
 | |
|         [[ -n "$matched" ]] && compadd -n -Q -S '' -s "$SUFFIX" - "$PREFIX"
 | |
|         _message "$descr"
 | |
|         break
 | |
| 
 | |
|       elif [[ "$action" = \(\(*\)\) ]]; then
 | |
| 
 | |
|         # ((...)) contains literal strings with descriptions.
 | |
| 
 | |
|         eval ws\=\( "${action[3,-3]}" \)
 | |
| 
 | |
|         _describe -c "$cmd" "$descr" ws -M "$match"
 | |
| 
 | |
|       elif [[ "$action" = \(*\) ]]; then
 | |
| 
 | |
|         # Anything inside `(...)' is added directly.
 | |
| 
 | |
|         compadd "$expl[@]" - ${=action[2,-2]}
 | |
|       elif [[ "$action" = \{*\} ]]; then
 | |
| 
 | |
|         # A string in braces is evaluated.
 | |
| 
 | |
|         eval "$action[2,-2]"
 | |
| 
 | |
|       elif [[ "$action" = \ * ]]; then
 | |
| 
 | |
|         # 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
 | |
| 
 | |
|   # Probably add the option names.
 | |
| 
 | |
|   if [[ -n "$opt" &&
 | |
|         ( ( ( nm -eq compstate[nmatches] || -n "$noargs" ) && -z "$aret" ) ||
 | |
|           -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
 | |
|         tmp1=( "${(@Mo)odescr:#[-+]?:*}" )
 | |
|         tmp2=(
 | |
|                "${PREFIX}${(@k)^opts[(I)${PREFIX[1]}?]#?}" \
 | |
| 	       "${PREFIX}${(@k)^dopts[(I)${PREFIX[1]}?]#?}" \
 | |
| 	       "${PREFIX}${(@)^${(@k)odopts[(I)${PREFIX[1]}?(|=)]#?}%=}"
 | |
|         )
 | |
|         tmp2=( "${(@o)tmp2}" )
 | |
| 
 | |
|         _describe -o -c "$cmd" option \
 | |
|           tmp1 tmp2  -Q -M 'r:|[_-]=* r:|=*'
 | |
|       else
 | |
|         # The last option takes an argument in the next word.
 | |
| 
 | |
|         compadd "$expl[@]" -Q -M "$match" - "${PREFIX}" && ret=0
 | |
|       fi
 | |
|     else
 | |
|       tmp1=( "${(@k)opts}" "${(@k)odopts[(I)*[^=]]}" )
 | |
|       tmp1=( "${(@M)odescr:#(${(j:|:)~tmp1}):*}" )
 | |
|       tmp2=( "${(@M)odescr:#(${(kj:|:)~dopts}):*}" )
 | |
|       tmp3=( "${(@M)odescr:#(${(kj:|:)~odopts[(I)*=]%=}):*}" )
 | |
|       _describe -o -c "$cmd" option \
 | |
|         tmp1 -Q -M "$match" -- \
 | |
|         tmp2 -QS '' -M "$match" -- \
 | |
|         tmp3 -QqS= -M "$match"
 | |
|     fi
 | |
|   fi
 | |
| 
 | |
|   if [[ nm -eq compstate[nmatches] && 
 | |
|         ( ( -z "$single" && "$PREFIX" = [-+]*\=* ) ||
 | |
|           ( $#_args_cache_long -ne 0 && "$PREFIX" = --*\=* ) ) ]]; then
 | |
|     tmp=( "${(@Mk)odopts:#[^:]#\=}" )
 | |
|     prefix="${PREFIX#*\=}"
 | |
|     suffix="$SUFFIX"
 | |
|     PREFIX="${PREFIX%%\=*}"
 | |
|     SUFFIX=''
 | |
|     compadd -M "$match" -D tmp - "${(@)tmp%\=}"
 | |
| 
 | |
|     if [[ $#tmp -eq 1 ]]; then
 | |
|       def="$odopts[$tmp[1]]"
 | |
|       PREFIX="$prefix"
 | |
|       SUFFIX="$suffix"
 | |
|       IPREFIX="$tmp[1]"
 | |
|       matched=yes
 | |
|       continue
 | |
|     fi
 | |
|   fi
 | |
| 
 | |
|   break
 | |
| done
 | |
| 
 | |
| [[ -n "$aret" ]] && return 300
 | |
| 
 | |
| [[ -n "$noargs" ]] && _message "$noargs"
 | |
| 
 | |
| # Set the return value.
 | |
| 
 | |
| [[ nm -ne "$compstate[nmatches]" ]]
 |