mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-29 19:00:57 +02:00
913 lines
25 KiB
Text
913 lines
25 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
|
|
|
|
# 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,rest,args,sopts,soptseq,soptseq1}
|
|
|
|
# See if we are using single-letter options.
|
|
|
|
if [[ "$1" = -s ]]; then
|
|
shift
|
|
_args_cache_single=yes
|
|
fi
|
|
|
|
# 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 -/' '*:unknown:'
|
|
|
|
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\]}"
|
|
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"
|
|
|
|
# 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"
|
|
_options[$curopt]=''
|
|
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::)${(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
|
|
_message 'no more arguments'
|
|
noargs=yes
|
|
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]}"
|
|
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=''
|
|
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]}"
|
|
break
|
|
elif compset -P "$tmp[1]"; then
|
|
def="$odopts[$tmp[1]]"
|
|
opt=''
|
|
break
|
|
fi
|
|
shift 1 tmp
|
|
done
|
|
fi
|
|
|
|
[[ -n "$sopts" && -n "$opt" && "$PREFIX" = [-+]${~soptseq}[$sopts] ]] && \
|
|
uns="${PREFIX[2,-1]}"
|
|
|
|
if [[ -n "$uns" ]]; then
|
|
uns="${(@j::)${(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]}" \)
|
|
|
|
if [[ -n "$compconfig[describe_values]" &&
|
|
"$compconfig[describe_values]" != *\!${cmd}* ]]; then
|
|
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
|
|
else
|
|
compadd "$expl[@]" - "${(@)ws%%:*}"
|
|
fi
|
|
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
|
|
local dpre="$PREFIX" dsuf="$SUFFIX"
|
|
|
|
PREFIX=''
|
|
SUFFIX=''
|
|
if [[ -z "$compconfig[describe_options]" ||
|
|
"$compconfig[describe_options]" = *\!${cmd}* ]] ||
|
|
! _display tmp odescr; then
|
|
tmp=( "${dpre[1]}${(@o)^${(@)${(@M)${=:-${(k)opts} ${(k)dopts} ${(k)odopts}}:#[-+]?(|=)}#?}%=}" )
|
|
fi
|
|
PREFIX="$dpre"
|
|
SUFFIX="$dsuf"
|
|
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.
|
|
|
|
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] &&
|
|
( -z "$single" ||
|
|
( $#_args_cache_long -ne 0 && "$PREFIX" = --*=* ) ) ]]; then
|
|
local suffix
|
|
|
|
tmp=( "${(@Mk)odopts:#[^:]#\=}" )
|
|
prefix="${PREFIX#*\=}"
|
|
suffix="$SUFFIX"
|
|
PREFIX="${PREFIX%%\=*}"
|
|
SUFFIX=''
|
|
compadd -M 'r:|[_-]=* r:|=*' -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
|
|
|
|
# Set the return value.
|
|
|
|
[[ nm -ne "$compstate[nmatches]" ]]
|