diff options
97 files changed, 2949 insertions, 547 deletions
diff --git a/Completion/Base/_command_names b/Completion/Base/_command_names new file mode 100644 index 000000000..d3b8a109a --- /dev/null +++ b/Completion/Base/_command_names @@ -0,0 +1,3 @@ +#defcomp -command- + +complist -c diff --git a/Completion/Base/_condition b/Completion/Base/_condition new file mode 100644 index 000000000..3e45e1b8f --- /dev/null +++ b/Completion/Base/_condition @@ -0,0 +1,10 @@ +#defcomp -condition- + +if [[ -current -1 -o ]]; then + complist -o -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' +elif [[ -current -1 -nt || -current -1 -ot || -current -1 -ef ]]; then + _files +else + _files + complist -v +fi diff --git a/Completion/Base/_default b/Completion/Base/_default new file mode 100644 index 000000000..8bcf14f6a --- /dev/null +++ b/Completion/Base/_default @@ -0,0 +1,13 @@ +#defcomp -default- + +# We first try the `compctl's. This is without first (-T) and default (-D) +# completion. If you want them add `-T' and/or `-D' to this command. +# If there is a `compctl' for the command we are working on, we return +# immediatly. If you want to use new style completion anyway, remove the +# `|| return'. Also, you may want to use new style completion if the +# `compctl' didn't produce any matches. In that case remove the `|| return' +# and at the line `[[ -nmatches 0 ]] || return' after `compcall'. + +compcall || return + +_files diff --git a/Completion/Base/_match_pattern b/Completion/Base/_match_pattern new file mode 100644 index 000000000..c5debc0b9 --- /dev/null +++ b/Completion/Base/_match_pattern @@ -0,0 +1,31 @@ +#autoload + +# This function is called from functions that do matching whenever they +# need to build a pattern that is used to match possible completions. +# It gets the name of the calling function and two names of parameters +# as arguments. The first one is used in the calling function to build +# the pattern used for matching possible completions. The content of this +# parameter on entry to this function is the string taken from the line. +# Here it parameter should be changed to a pattern that matches words as +# the match specs currently in use do. +# In the calling function this pattern may be changed again or used only +# in parts. The second parameter whose name is given as the third argument +# allows to give pattern flags liek `(#l)' that are to be used whenever +# matching is done. +# +# As an example, if you have global match specifications like: +# +# compctl -M 'm:{a-z}={A-Z}' 'm:{a-z}={A-Z} r:|[.-]=* r:|=*' +# +# This function would look like: +# +# eval "${3}='(#l)'" +# [[ MATCHER -eq 2 ]] && eval "$1='${(P)2:gs/./*./:gs/-/*-/}'" +# +# The first line makes sure that matching is done case-insensitive as +# specified by `m:{a-z}={A-Z}'. The second line replaces dots and hyphens +# in the given string by patterns matching any characters before them, +# like the `r:|[.-]=* r:|=*'. To make this work, the function `_match_test' +# would have to be changed to `(( MATCHERS <= 2 ))' +# +# The default implementation of this function is empty. diff --git a/Completion/Base/_match_test b/Completion/Base/_match_test new file mode 100644 index 000000000..e8b6e6424 --- /dev/null +++ b/Completion/Base/_match_test @@ -0,0 +1,15 @@ +#autoload + +# This function is called at the beginning of functions that do matching in +# shell code. It should test the value of the `MATCHER' special parameter +# and return non-zero if the calling function should try to generate matches +# for the global match specification in use. +# +# This function gets one argument, the name of the function calling it. +# +# If you have a global match specification with more than one set of patterns +# you may want to modify this function to return non-zero for all of your +# match specifications and modify the function `_match_pattern' to build the +# pattern to use in the calling function. + +(( MATCHER == 1 )) diff --git a/Completion/Base/_precommand b/Completion/Base/_precommand new file mode 100644 index 000000000..2cf661147 --- /dev/null +++ b/Completion/Base/_precommand @@ -0,0 +1,5 @@ +#defcomp - nohup nice eval time rusage noglob nocorrect exec + +[[ -position 1 -1 ]] + +_normal "$@" diff --git a/Completion/Base/_redirect b/Completion/Base/_redirect new file mode 100644 index 000000000..32113ad7c --- /dev/null +++ b/Completion/Base/_redirect @@ -0,0 +1,3 @@ +#defcomp -redirect- + +_files diff --git a/Completion/Base/_subscript b/Completion/Base/_subscript new file mode 100644 index 000000000..2b827a117 --- /dev/null +++ b/Completion/Base/_subscript @@ -0,0 +1,4 @@ +#defcomp -subscript- + +_compalso -math- "$@" +[[ ${(Pt)${COMMAND}} = assoc* ]] && complist -k "( ${(kP)${COMMAND}} )" diff --git a/Completion/Base/_vars b/Completion/Base/_vars new file mode 100644 index 000000000..7153b6f38 --- /dev/null +++ b/Completion/Base/_vars @@ -0,0 +1,3 @@ +#defcomp -math- getopts read unset vared + +complist -v diff --git a/Completion/Builtins/_aliases b/Completion/Builtins/_aliases new file mode 100644 index 000000000..1038a726e --- /dev/null +++ b/Completion/Builtins/_aliases @@ -0,0 +1,3 @@ +#defcomp unalias + +complist -a diff --git a/Completion/Builtins/_arrays b/Completion/Builtins/_arrays new file mode 100644 index 000000000..cbeac7118 --- /dev/null +++ b/Completion/Builtins/_arrays @@ -0,0 +1,3 @@ +#defcomp shift + +complist -A diff --git a/Completion/Builtins/_autoload b/Completion/Builtins/_autoload new file mode 100644 index 000000000..4f506baeb --- /dev/null +++ b/Completion/Builtins/_autoload @@ -0,0 +1,3 @@ +#defcomp autoload + +complist -s '${^fpath}/*(N:t)' diff --git a/Completion/Builtins/_bg_jobs b/Completion/Builtins/_bg_jobs new file mode 100644 index 000000000..511bb8308 --- /dev/null +++ b/Completion/Builtins/_bg_jobs @@ -0,0 +1,3 @@ +#defcomp bg + +complist -z -P '%' diff --git a/Completion/Builtins/_bindkey b/Completion/Builtins/_bindkey new file mode 100644 index 000000000..8eddeb2a8 --- /dev/null +++ b/Completion/Builtins/_bindkey @@ -0,0 +1,7 @@ +#defcomp bindkey + +if [[ -mword 1 -*[DAN]* || -mcurrent -1 -*M ]]; then + complist -s '$(bindkey -l)' +else + complist -b +fi diff --git a/Completion/Builtins/_builtin b/Completion/Builtins/_builtin new file mode 100644 index 000000000..a967932ee --- /dev/null +++ b/Completion/Builtins/_builtin @@ -0,0 +1,7 @@ +#defcomp builtin + +if [[ -position 2 -1 ]]; then + _normal "$@" +else + complist -eB +fi diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd new file mode 100644 index 000000000..f3ce67ec7 --- /dev/null +++ b/Completion/Builtins/_cd @@ -0,0 +1,3 @@ +#defcomp cd + +_files -W cdpath -g '*(-/)' diff --git a/Completion/Builtins/_command b/Completion/Builtins/_command new file mode 100644 index 000000000..b2812de25 --- /dev/null +++ b/Completion/Builtins/_command @@ -0,0 +1,7 @@ +#defcomp command + +if [[ -position 2 -1 ]]; then + _normal "$@" +else + complist -em +fi diff --git a/Completion/Builtins/_dirs b/Completion/Builtins/_dirs new file mode 100644 index 000000000..bc426e322 --- /dev/null +++ b/Completion/Builtins/_dirs @@ -0,0 +1,3 @@ +#defcomp rmdir df du dircmp + +_files -/ diff --git a/Completion/Builtins/_disable b/Completion/Builtins/_disable new file mode 100644 index 000000000..063b65a7d --- /dev/null +++ b/Completion/Builtins/_disable @@ -0,0 +1,6 @@ +#defcomp disable + +[[ -mcurrent -1 -*a* ]] && complist -ea +[[ -mcurrent -1 -*f* ]] && complist -eF +[[ -mcurrent -1 -*r* ]] && complist -ew +[[ ! -mcurrent -1 -* ]] && complist -eB diff --git a/Completion/Builtins/_echotc b/Completion/Builtins/_echotc new file mode 100644 index 000000000..85ebb97ce --- /dev/null +++ b/Completion/Builtins/_echotc @@ -0,0 +1,3 @@ +#defcomp echotc + +complist -k '(al dc dl do le up al bl cd ce cl cr dc dl do ho is le ma nd nl se so up)' diff --git a/Completion/Builtins/_enable b/Completion/Builtins/_enable new file mode 100644 index 000000000..22ff53ee7 --- /dev/null +++ b/Completion/Builtins/_enable @@ -0,0 +1,6 @@ +#defcomp enable + +[[ -mcurrent -1 -*a* ]] && complist -da +[[ -mcurrent -1 -*f* ]] && complist -dF +[[ -mcurrent -1 -*r* ]] && complist -dw +[[ ! -mcurrent -1 -* ]] && complist -dB diff --git a/Completion/Builtins/_fc b/Completion/Builtins/_fc new file mode 100644 index 000000000..f0d2c03fd --- /dev/null +++ b/Completion/Builtins/_fc @@ -0,0 +1,7 @@ +#defcomp fc + +if [[ -mcurrent -1 -*e ]]; then + complist -c +elif [[ -mcurrent -1 -[ARWI]## ]]; then + _files +fi diff --git a/Completion/Builtins/_functions b/Completion/Builtins/_functions new file mode 100644 index 000000000..8a352ea08 --- /dev/null +++ b/Completion/Builtins/_functions @@ -0,0 +1,3 @@ +#defcomp unfunction + +complist -F diff --git a/Completion/Builtins/_hash b/Completion/Builtins/_hash new file mode 100644 index 000000000..171c5e2e8 --- /dev/null +++ b/Completion/Builtins/_hash @@ -0,0 +1,13 @@ +#defcomp hash + +if [[ -mword 1 -*d* ]]; then + if [[ -string 1 '=' ]]; then + _path_files -g '*(-/)' + else + complist -n -q -S '=' + fi +elif [[ -string 1 '=' ]]; then + _files -/g '*(*)' +else + complist -m -q -S '=' +fi diff --git a/Completion/Builtins/_jobs b/Completion/Builtins/_jobs new file mode 100644 index 000000000..018883c61 --- /dev/null +++ b/Completion/Builtins/_jobs @@ -0,0 +1,3 @@ +#defcomp fg jobs + +complist -j -P '%' diff --git a/Completion/Builtins/_kill b/Completion/Builtins/_kill new file mode 100644 index 000000000..50796d36f --- /dev/null +++ b/Completion/Builtins/_kill @@ -0,0 +1,11 @@ +#defcomp kill + +local list + +if [[ -iprefix '-' ]]; then + complist -k "($signals[1,-3])" +else + complist -P '%' -j + list=("$(ps 2>/dev/null)") + complist -y '$list' -s '`ps 2>/dev/null | tail +2 | cut -c1-5`' +fi diff --git a/Completion/Builtins/_limits b/Completion/Builtins/_limits new file mode 100644 index 000000000..35ccbe07e --- /dev/null +++ b/Completion/Builtins/_limits @@ -0,0 +1,3 @@ +#defcomp limit unlimit + +complist -k "(${(j: :)${(f)$(limit)}%% *})" diff --git a/Completion/Builtins/_sched b/Completion/Builtins/_sched new file mode 100644 index 000000000..1e8ae3445 --- /dev/null +++ b/Completion/Builtins/_sched @@ -0,0 +1,3 @@ +#defcomp sched + +[[ -position 2 -1 ]] && _normal "$@" diff --git a/Completion/Builtins/_set b/Completion/Builtins/_set new file mode 100644 index 000000000..5597025bc --- /dev/null +++ b/Completion/Builtins/_set @@ -0,0 +1,7 @@ +#defcomp set + +if [[ -mcurrent -1 [-+]o ]]; then + complist -o +elif [[ -current -1 -A ]]; then + complist -A +fi diff --git a/Completion/Builtins/_setopt b/Completion/Builtins/_setopt new file mode 100644 index 000000000..4abb3ccee --- /dev/null +++ b/Completion/Builtins/_setopt @@ -0,0 +1,7 @@ +#defcomp setopt + +local nm=$NMATCHES + +complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \ + -s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)' +[[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o diff --git a/Completion/Builtins/_source b/Completion/Builtins/_source new file mode 100644 index 000000000..aae2c7320 --- /dev/null +++ b/Completion/Builtins/_source @@ -0,0 +1,7 @@ +#defcomp source + +if [[ -position 2 -1 ]]; then + _normal "$@" +else + _files +fi diff --git a/Completion/Builtins/_trap b/Completion/Builtins/_trap new file mode 100644 index 000000000..59e81c589 --- /dev/null +++ b/Completion/Builtins/_trap @@ -0,0 +1,7 @@ +#defcomp trap + +if [[ -position 1 ]]; then + complist -c +else + complist -k signals +fi diff --git a/Completion/Builtins/_unhash b/Completion/Builtins/_unhash new file mode 100644 index 000000000..fe40c25a2 --- /dev/null +++ b/Completion/Builtins/_unhash @@ -0,0 +1,6 @@ +#defcomp unhash + +[[ -mword 1 -*d* ]] && complist -n +[[ -mword 1 -*a* ]] && complist -a +[[ -mword 1 -*f* ]] && complist -F +[[ ! -mword 1 -* ]] && complist -m diff --git a/Completion/Builtins/_unsetopt b/Completion/Builtins/_unsetopt new file mode 100644 index 000000000..90d642b51 --- /dev/null +++ b/Completion/Builtins/_unsetopt @@ -0,0 +1,7 @@ +#defcomp unsetopt + +local nm=$NMATCHES + +complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \ + -s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)' +[[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o diff --git a/Completion/Builtins/_vars_eq b/Completion/Builtins/_vars_eq new file mode 100644 index 000000000..fcbb0148c --- /dev/null +++ b/Completion/Builtins/_vars_eq @@ -0,0 +1,3 @@ +#defcomp declare export integer local readonly typeset + +complist -v -q -S '=' diff --git a/Completion/Builtins/_wait b/Completion/Builtins/_wait new file mode 100644 index 000000000..29a7f6002 --- /dev/null +++ b/Completion/Builtins/_wait @@ -0,0 +1,7 @@ +#defcomp wait + +local list + +complist -P '%' -j +list=("$(ps 2>/dev/null)") +complist -y '$list' -s '`ps 2>/dev/null | tail +2 | cut -c1-5`' diff --git a/Completion/Builtins/_which b/Completion/Builtins/_which new file mode 100644 index 000000000..324256e3d --- /dev/null +++ b/Completion/Builtins/_which @@ -0,0 +1,3 @@ +#defcomp which whence where type + +complist -caF diff --git a/Completion/Builtins/_zftp b/Completion/Builtins/_zftp new file mode 100644 index 000000000..9be9c94db --- /dev/null +++ b/Completion/Builtins/_zftp @@ -0,0 +1,50 @@ +#defpatcomp zf* + +# Don't try any more completion after this. +_compskip=1 + +# Completion for zftp builtin and zf* functions. The functions +# zfcd_match and zfget_match (used for old-style completion) +# need to be installed for remote file and directory completion to work. + +local subcom + +if [[ $COMMAND = zftp ]]; then + if [[ $CURRENT -eq 1 ]]; then + compadd -m open params user login type ascii binary mode put \ + putat get getat append appendat ls dir local remote mkdir rmdir + return + fi + subcom=$1 +else + subcom=$COMMAND +fi + +case $subcom in + *(cd|ls|dir)) + # complete remote directories; we could be smarter about hiding prefixes + zfcd_match $PREFIX $SUFFIX + (( $#reply )) && compadd -m -S/ -q $reply + ;; + + *(get(|at)|gcp|delete|remote)) + # complete remote files + zfget_match $PREFIX $SUFFIX + (( $#reply )) && compadd -F fignore -m $reply + ;; + + *(put(|at)|pcp)) + # complete local files + _files + ;; + + *(open|anon|params)) + # complete hosts: should do cleverer stuff with user names + complist -k hosts + ;; + + *) + # dunno... try ordinary completion after all. + unset _compskip + ;; +esac diff --git a/Completion/Builtins/_zle b/Completion/Builtins/_zle new file mode 100644 index 000000000..bb1102e74 --- /dev/null +++ b/Completion/Builtins/_zle @@ -0,0 +1,7 @@ +#defcomp zle + +if [[ -word 1 -N && -position 3 ]]; then + complist -F +else + complist -b +fi diff --git a/Completion/Builtins/_zmodload b/Completion/Builtins/_zmodload new file mode 100644 index 000000000..112acb57c --- /dev/null +++ b/Completion/Builtins/_zmodload @@ -0,0 +1,9 @@ +#defcomp zmodload + +if [[ -mword 1 -*(a*u|u*a)* || -mword 1 -*a* && -position 3 -1 ]]; then + complist -B +elif [[ -mword 1 -*u* ]]; then + complist -s '$(zmodload)' +else + complist -s '${^module_path}/*(N:t:r)' +fi diff --git a/Completion/Commands/_correct_filename b/Completion/Commands/_correct_filename new file mode 100644 index 000000000..edf1c65c2 --- /dev/null +++ b/Completion/Commands/_correct_filename @@ -0,0 +1,37 @@ +#defkeycomp complete-word \C-xc + +# Function to correct a filename. Can be used as a completion widget, +# or as a function in its own right, in which case it will print the +# corrected filename to standard output. +# +# You can adapt max_approx to the maximum number of mistakes +# which are allowed in total. + +emulate -LR zsh +setopt extendedglob + +local file="$PREFIX$SUFFIX" trylist +integer approx max_approx=6 + +[[ -z $WIDGET ]] && file=$1 + +if [[ -e "$file" ]]; then + if [[ -n $WIDGET ]]; then + compadd "$file" + else + print "$file" + fi + return +fi + +for (( approx = 1; approx <= max_approx; approx++ )); do + trylist=( (#a$approx)"$file"(N) ) + (( $#trylist )) && break +done +(( $#trylist )) || return 1 + +if [[ -n $WIDGET ]]; then + compadd -U "${trylist[@]}" +else + print "${trylist[@]}" +fi diff --git a/Completion/Commands/_most_recent_file b/Completion/Commands/_most_recent_file new file mode 100644 index 000000000..ff5645de5 --- /dev/null +++ b/Completion/Commands/_most_recent_file @@ -0,0 +1,4 @@ +#defkeycomp complete-word \C-xm +local file +file=($~PREFIX*$~SUFFIX(om[1]N)) +(( $#file )) && compadd -f $file diff --git a/Completion/Core/_comp_parts b/Completion/Core/_comp_parts new file mode 100644 index 000000000..7c24fd19d --- /dev/null +++ b/Completion/Core/_comp_parts @@ -0,0 +1,147 @@ +#autoload + +# This function can be used to separately complete parts of strings +# where each part may be one of a set of matches and different parts +# have different sets. +# Arguments are alternatingly arrays and separator strings. Arrays may +# be given by name or literally as words separated by white space in +# parentheses, e.g.: +# +# _comp_parts '(foo bar)' @ hosts +# +# This will make this function complete the strings in the array +# `friends'. If the string on the line contains a `@', the substring +# after it will be completed from the array `hosts'. Of course more +# arrays may be given, each preceded by another separator string. +# +# This function understands the `-J group', `-V group', and +# `-X explanation' options. +# +# This function does part of the matching itself and calls the functions +# `_match_test' and `_match_pattern' for this. + +local str arr sep test testarr tmparr prefix suffixes matchers autosuffix +local matchflags opt group expl + +# Test if we should use this function for the global matcher in use. + +_match_test _comp_parts || return + +# Get the options. + +group=() +expl=() +while getopts "J:V:X:" opt; do + case "$opt" in + [JV]) group=("-$opt" "$OPTARG");; + X) expl=(-X "$OPTARG");; + esac +done +shift OPTIND-1 + +# Get the string from the line. + +str="$PREFIX$SUFFIX" +prefix="" + +# Walk through the arguments to find the longest unambiguous prefix. + +while [[ $# -gt 1 ]]; do + # Get the next array and separator. + arr="$1" + sep="$2" + + if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr + fi + # Is the separator on the line? + [[ "$str" != *${sep}* ]] && break + + # Build a pattern matching the possible matches and get all these + # matches in an array. + test="${str%%${sep}*}" + matchflags="" + _match_pattern _comp_parts test matchflags + test="${matchflags}${test}" + testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) + + # If there are no matches we give up. If there is more than one + # match, this is the part we will complete. + (( $#testarr )) || return + [[ $#testarr -gt 1 ]] && break + + # Only one match, add it to the prefix and skip over it in `str', + # continuing with the next array and separator. + prefix="${prefix}${testarr[1]}${sep}" + str="${str#*${sep}}" + shift 2 +done + +# Get the array to work upon. +arr="$1" +if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr +fi +if [[ $# -le 1 || "$str" != *${2}* ]]; then + # No more separators, build the matches. + matchflags="" + test="$str" + _match_pattern _comp_parts test matchflags + test="${matchflags}${test}" + testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) +fi + +[[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return + +# Now we build the suffixes to give to the completion code. +shift +matchers=() +suffixes=("") +autosuffix=() + +while [[ $# -gt 0 && "$str" == *${1}* ]]; do + # Remove anything up to the the suffix. + str="${str#*${1}}" + + # Again, we get the string from the line up to the next separator + # and build a pattern from it. + if [[ $# -gt 2 ]]; then + test="${str%%${3}*}" + else + test="$str" + fi + matchflags="" + _match_pattern _comp_parts test matchflags + test="${matchflags}${test}" + + # We incrementally add suffixes by appending to them the seperators + # and the strings from the next array that match the pattern we built. + + arr="$2" + if [[ "$arr[1]" == '(' ]]; then + tmparr=( ${=arr[2,-2]} ) + arr=tmparr + fi + suffixes=("${^suffixes[@]}${1}${(@M)^${(@P)arr}:#${~test}*}") + + # We want the completion code to generate the most specific suffix + # for us, so we collect matching specifications that allow partial + # word matching before the separators on the fly. + matchers=("$matchers[@]" "r:|${1}=*") + shift 2 +done + +# If we were given at least one more separator we make the completion +# code offer it by appending it as a autoremovable suffix. +(( $# )) && autosuffix=(-qS "$1") + +# If we have collected matching specifications, we build an array +# from it that can be used as arguments to `compadd'. +[[ $#matchers -gt 0 ]] && matchers=(-M "$matchers") + +# Add the matches for each of the suffixes. +for i in "$suffixes[@]"; do + compadd "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" -p "$prefix" -s "$i" - "$testarr[@]" +done diff --git a/Completion/Core/_compalso b/Completion/Core/_compalso new file mode 100644 index 000000000..23a40e2d0 --- /dev/null +++ b/Completion/Core/_compalso @@ -0,0 +1,13 @@ +#autoload + +# This searches $1 in the array for normal completions and calls the result. +# It is used to include completions for another command or special context +# into the list generated by the calling function. +# For example the function for `-subscript-' could call this as in +# `_compalso -math- "$@"' to get the completions that would be generated +# for a mathematical context. + +local tmp + +tmp="$_comps[$1]" +[[ -z "$tmp" ]] || "$tmp" "$@" diff --git a/Completion/Core/_files b/Completion/Core/_files new file mode 100644 index 000000000..d2cce35e7 --- /dev/null +++ b/Completion/Core/_files @@ -0,0 +1,26 @@ +#autoload + +# Utility function for completing files of a given type or any file. +# In many cases you will want to call this one instead of _path_files(). + +local nm=$NMATCHES + +_path_files "$@" + +if [[ $# -ne 0 && -nmatches nm ]]; then + local opt opts + + # We didn't get any matches for those types of files described by + # the `-g' or `-/' option. Now we try it again accepting all files. + # First we get those options that we have to use even if then. If + # we find out that the `-f' option was given, we already accepted + # all files and give up immediatly. + + opts=() + while getopts "P:S:W:F:J:V:X:f/g:" opt; do + [[ "$opt" = f ]] && return + [[ "$opt" = [PSWFJVX] ]] && opts=("$opts[@]" "-$opt" "$OPTARG") + done + + _path_files "$opts[@]" +fi diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete new file mode 100644 index 000000000..c7f5a5a96 --- /dev/null +++ b/Completion/Core/_main_complete @@ -0,0 +1,48 @@ +#autoload + +# The main loop of the completion code. This is what is called when +# completion is attempted from the command line. +# The completion code gives us the special variables and the arguments +# from the command line are given as positional parameters. + +local comp name + +setopt localoptions nullglob rcexpandparam globdots +unsetopt markdirs globsubst shwordsplit nounset + +# An entry for `-first-' is the replacement for `compctl -T' +# Completion functions may set `_compskip' to any value to make the +# main loops stop calling other completion functions. + +comp="$_comps[-first-]" +if [[ ! -z "$comp" ]]; then + "$comp" "$@" + if (( $+_compskip )); then + unset _compskip + return + fi +fi + +# For arguments we use the `_normal function. + +if [[ $CONTEXT == argument || $CONTEXT == command ]]; then + _normal "$@" +else + # Let's see if we have a special completion definition for the other + # possible contexts. + + comp='' + + case $CONTEXT in + redirect) comp="$_comps[-redirect-]";; + math) comp="$_comps[-math-]";; + subscript) comp="$_comps[-subscript-]";; + value) comp="$_comps[-value-]";; + condition) comp="$_comps[-condition-]";; + esac + + # If not, we use default completion, if any. + + [[ -z "$comp" ]] && comp="$_comps[-default-]" + [[ -z "$comp" ]] || "$comp" "$@" +fi diff --git a/Completion/Core/_normal b/Completion/Core/_normal new file mode 100644 index 000000000..19da6d79b --- /dev/null +++ b/Completion/Core/_normal @@ -0,0 +1,54 @@ +#autoload + +local comp cmd1 cmd2 pat val name + +# Completing in command position? If not we set up `cmd1' and `cmd2' as +# two strings we have search in the completion definition arrays (e.g. +# a path and the last path name component). + +if [[ $CONTEXT == command ]]; then + comp="$_comps[-command-]" + [[ -z "$comp" ]] || "$comp" "$@" + return +elif [[ "$COMMAND[1]" == '=' ]]; then + eval cmd1\=$COMMAND + cmd2="$COMMAND[2,-1]" +elif [[ "$COMMAND" == */* ]]; then + cmd1="$COMMAND" + cmd2="${COMMAND:t}" +else + cmd1="$COMMAND" + eval cmd2=$(whence -p $COMMAND) +fi + +# See if there are any matching pattern completions. + +for i in "$_patcomps[@]"; do + pat="${i% *}" + val="${i#* }" + if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]]; then + "$val" "$@" + if (( $+_compskip )); then + unset _compskip + return + fi + fi +done + +# Now look up the two names in the normal completion array. + +name="$cmd1" +comp="$_comps[$cmd1]" + +if [[ -z "$comp" ]]; then + name="$cmd2" + comp="$_comps[$cmd2]" +fi + +# And generate the matches, probably using default completion. + +if [[ -z "$comp" ]]; then + name=-default- + comp="$_comps[-default-]" +fi +[[ -z "$comp" ]] || "$comp" "$@" diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files new file mode 100644 index 000000000..83b6e8a09 --- /dev/null +++ b/Completion/Core/_path_files @@ -0,0 +1,311 @@ +#autoload + +# Utility function for in-path completion. +# Supported arguments are: `-f', `-/', `-g <patterns>', `-J <group>', +# `-V <group>', `-W paths', `-X explanation', and `-F <ignore>'. All but +# the last have the same syntax and meaning as for `complist'. The +# `-F <ignore>' option may be used to give a list of suffixes either by +# giving the name of an array or literally by giving them in a string +# surrounded by parentheses. Files with one of the suffixes thus given +# are treated like files with one of the suffixes in the `fignore' array +# in normal completion. +# +# This function uses the helper functions `_match_test' and `_match_pattern'. + +# First see if we should generate matches for the global matcher in use. + +_match_test _path_files || return + +# Yes, so... + +local nm prepaths str linepath realpath donepath patstr prepath testpath rest +local tmp1 collect tmp2 suffixes i ignore matchflags opt group sopt pats gopt +local addpfx addsfx expl + +setopt localoptions nullglob rcexpandparam globdots extendedglob +unsetopt markdirs globsubst shwordsplit nounset + +prepaths=('') +ignore=() +group=() +sopt='-' +gopt='' +pats=() +addpfx=() +addsfx=() +expl=() + +# Get the options. + +while getopts "P:S:W:F:J:V:X:f/g:" opt; do + case "$opt" in + P) addpfx=(-P "$OPTARG") + ;; + S) addsfx=(-S "$OPTARG") + ;; + W) tmp1="$OPTARG" + if [[ "$tmp1[1]" = '(' ]]; then + prepaths=( ${^=tmp1[2,-2]}/ ) + else + prepaths=( ${(P)=${tmp1}} ) + (( ! $#prepaths )) && prepaths=( ${tmp1}/ ) + fi + (( ! $#prepaths )) && prepaths=( '' ) + ;; + F) tmp1="$OPTARG" + if [[ "$tmp1[1]" = '(' ]]; then + ignore=( ${^=tmp1[2,-2]}/ ) + else + ignore=( ${(P)${tmp1}} ) + fi + (( $#ignore )) && ignore=(-F "( $ignore )") + ;; + [JV]) group=("-$opt" "$OPTARG") + ;; + X) expl=(-X "$OPTARG") + ;; + f) sopt="${sopt}f" + pats=("$pats[@]" '*') + ;; + /) sopt="${sopt}/" + pats=("$pats[@]" '*(-/)') + ;; + g) gopt='-g' + pats=("$pats[@]" ${=OPTARG}) + ;; + esac +done + +# If we were given no file selection option, we behave as if we were given +# a `-f'. + +if [[ "$sopt" = - ]]; then + if [[ -z "$gopt" ]]; then + sopt='-f' + pats=('*') + else + unset sopt + fi +fi + +# str holds the whole string from the command line with a `*' between +# the prefix and the suffix. + +str="${PREFIX:q}*${SUFFIX:q}" + +# If the string began with a `~', the quoting turned this into `\~', +# remove the slash. + +[[ "$str" = \\\~* ]] && str="$str[2,-1]" + +# We will first try normal completion called with `complist', but only if we +# weren't given a `-F' option. + +if (( ! $#ignore )); then + # First build an array containing the `-W' option, if there is any and we + # want to use it. We don't want to use it if the string from the command line + # is a absolute path or relative to the current directory. + + if [[ -z "$tmp1[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]]; then + tmp1=() + else + tmp1=(-W "( $prepaths )") + fi + + # Now call complist. + + nm=$NMATCHES + if [[ -z "$gopt" ]]; then + complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt + else + complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt -g "$pats" + fi + + # If this generated any matches, we don't want to do in-path completion. + + [[ -nmatches nm ]] || return + + # No `-F' option, so we want to use `fignore'. + + ignore=(-F fignore) +fi + +# Now let's have a closer look at the string to complete. + +if [[ "$str[1]" = \~ ]]; then + # It begins with `~', so remember anything before the first slash to be able + # to report it to the completion code. Also get an expanded version of it + # (in `realpath'), so that we can generate the matches. Then remove that + # prefix from the string to complete, set `donepath' to build the correct + # paths and make sure that the loop below is run only once with an empty + # prefix path by setting `prepaths'. + + linepath="${str%%/*}/" + eval realpath\=$linepath + str="${str#*/}" + donepath='' + prepaths=( '' ) +else + # If the string does not start with a `~' we don't remove a prefix from the + # string. + + liniepath='' + realpath='' + + if [[ "$str[1]" = / ]]; then + # If it is a absolut path name, we remove the first slash and put it in + # `donepath' meaning that we treat it as the path that was already handled. + # Also, we don't use the paths from `-W'. + + str="$str[2,-1]" + donepath='/' + prepaths=( '' ) + else + # The common case, we just use the string as it is, unless it begins with + # `./' or `../' in which case we don't use the paths from `-W'. + + [[ "$str" = (.|..)/* ]] && prepaths=( '' ) + donepath='' + fi +fi + +# First we skip over all pathname components in `str' which really exist in +# the file-system, so that `/usr/lib/l<TAB>' doesn't offer you `lib' and +# `lib5'. Pathname components skipped this way are taken from `str' and added +# to `donepath'. + +while [[ "$str" = */* ]] do + [[ -e "$realpath$donepath${str%%/*}" ]] || break + donepath="$donepath${str%%/*}/" + str="${str#*/}" +done + +# Now build the glob pattern by calling `_match_pattern'. +patstr="$str" +matchflags="" +_match_pattern _path_files patstr matchflags + +# We almost expect the pattern to have changed `..' into `*.*.', `/.' into +# `/*.', and probably to contain two or more consecutive `*'s. Since these +# have special meaning for globbing, we remove them. But before that, we +# add the pattern for matching any characters before a slash. + +patstr="$patstr:gs-/-*/-:gs/*.*.//:gs-/*.-/.-:gs/**/*/" + +# Finally, generate the matches. First we loop over all the paths from `-W'. +# Note that in this loop `str' is used as a modifyable version of `patstr' +# and `testpath' is a modifyable version of `donepath'. + +for prepath in "$prepaths[@]"; do + str="$patstr" + testpath="$donepath" + + # The second loop tests the components of the path in `str' to get the + # possible matches. + + while [[ "$str" = */* ]] do + # `rest' is the pathname after the first slash that is left. In `tmp1' + # we get the globbing matches for the pathname component currently + # handled. + + rest="${str#*/}" + tmp1="${prepath}${realpath}${testpath}${~matchflags}${str%%/*}(-/)" + tmp1=( $~tmp1 ) + + if [[ $#tmp1 -eq 0 ]]; then + # If this didn't produce any matches, we don't need to test this path + # any further, so continue with the next `-W' path, if any. + + continue 2 + elif [[ $#tmp1 -gt 1 ]]; then + # If it produced more than one match, we want to remove those which + # don't have possible following pathname components matching the + # rest of the string we are completing. (The case with only one + # match is handled below.) + # In `collect' we will collect those of the produced pathnames that + # have a matching possible path-suffix. In `suffixes' we build an + # array containing strings build from the rest of the string to + # complete and the glob patterns we were given as arguments. + + collect=() + suffixes=( $rest$^pats ) + suffixes=( "${(@)suffixes:gs.**.*.}" ) + + # In the loop the prefixes from the `tmp1' array produced above and + # the suffixes we just built are used to produce possible matches + # via globbing. + + for i in $tmp1; do + tmp2=( ${~i}/${~matchflags}${~suffixes} ) + [[ $#tmp2 -ne 0 ]] && collect=( $collect $i ) + done + + # If this test showed that none of the matches from the glob in `tmp1' + # has a possible sub-path matching what's on the line, we give up and + # continue with the next `-W' path. + + if [[ $#collect -eq 0 ]]; then + continue 2 + elif [[ $#collect -ne 1 ]]; then + # If we have more than one possible match, this means that the + # pathname component currently handled is ambiguous, so we give + # it to the completion code. + # First we build the full path prefix in `tmp1'. + + tmp1="$prepath$realpath$testpath" + + # Now produce all matching pathnames in `collect'. + + collect=( ${~collect}/${~matchflags}${~suffixes} ) + + # And then remove the common path prefix from all these matches. + + collect=( ${collect#$tmp1} ) + + # Finally, we add all these matches with the common (unexpanded) + # pathprefix (the `-p' option), the path-prefix (the `-W' option) + # to allow the completion code to test file type, and the path- + # suffix (the `-s' option). We also tell the completion code that + # these are file names and that `fignore' should be used as usual + # (the `-f' and `-F' options). + + for i in $collect; do + compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -p "$linepath$testpath" -W "$tmp1" -s "/${i#*/}" -f "$ignore[@]" - "${i%%/*}" + done + + # We have just finished handling all the matches from above, so we + # can continue with the next `-W' path. + + continue 2 + fi + # We reach this point if only one of the path prefixes in `tmp1' + # has a existing path-suffix matching the string from the line. + # In this case we accept this match and continue with the next + # path-name component. + + tmp1=( "$collect[1]" ) + fi + # This is also reached if the first globbing produced only one match + # in this case we just continue with the next pathname component, too. + + tmp1="$tmp1[1]" + testpath="$testpath${tmp1##*/}/" + str="$rest" + done + + # We are here if all pathname components except the last one (which is still + # not tested) are unambiguous. So we add matches with the full path prefix, + # no path suffix, the `-W' we are currently handling, all the matches we + # can produce in this directory, if any. + + tmp1="$prepath$realpath$testpath" + suffixes=( $str$^pats ) + suffixes=( "${(@)suffixes:gs.**.*.}" ) + tmp2=( ${~tmp1}${~matchflags}${~suffixes} ) + if [[ $#tmp2 -eq 0 && "$sopt" = */* ]]; then + [[ "$testpath[-1]" = / ]] && testpath="$testpath[1,-2]" + compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -f - "$linepath$testpath" + else + compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -p "$linepath$testpath" -W "$prepath$realpath$testpath" -f "$ignore[@]" - ${(@)tmp2#$tmp1} + fi +done diff --git a/Completion/Core/compdump b/Completion/Core/compdump new file mode 100644 index 000000000..8be096f50 --- /dev/null +++ b/Completion/Core/compdump @@ -0,0 +1,89 @@ +# This is a file to be sourced to dump the definitions for new-style +# completion defined by 'compinit' in the same directory. The output +# should be directed into the "compinit.dump" in the same directory as +# compinit. If you rename init, just stick .dump onto the end of whatever +# you have called it and put it in the same directory. This is handled +# automatically if you invoke compinit with the option -d. +# +# You will need to update the dump every time you add a new completion. +# To do this, simply remove the .dump file, start a new shell, and +# create the .dump file as before. Again, compinit -d handles this +# automatically. +# +# It relies on KSH_ARRAYS not being set. + +# Print the number of files used for completion. This is used in compinit +# to see if auto-dump should re-dump the dump-file. + +_d_file=${COMPDUMP-${0:h}/compinit.dump} + +typeset -U _d_files +_d_files=( ${^~fpath}/_*~*~(N:t) ) + +print "#files: $#_d_files" > $_d_file + +unset _d_files + +# First dump the arrays _comps and _patcomps. The quoting hieroglyphyics +# ensure that a single quote inside a variable is itself correctly quoted. + +print "_comps=(" >> $_d_file +for _d_f in ${(ok)_comps}; do + print -r - "'${_d_f//\'/'\\''}'" "'${_comps[$_d_f]//\'/'\\''}'" +done >> $_d_file +print ")" >> $_d_file + +print "\n_patcomps=(" >> $_d_file +for _d_f in "$_patcomps[@]"; do + print -r - "'${_d_f//\'/'\\''}'" +done >> $_d_file +print ")" >> $_d_file + +print >> $_d_file + +# Now dump the key bindings. We dump all bindings for zle widgets +# whose names start with a underscore. +# We need both the zle -C's and the bindkey's to recreate. + +_d_bks=() +zle -lL | + while read -rA _d_line; do + if [[ ${_d_line[5]} = _* ]]; then + print -r - ${_d_line} + _d_bks=($_d_bks ${_d_line[3]}) + fi + done >> $_d_file +bindkey | + while read -rA _d_line; do + if [[ ${_d_line[2]} = (${(j.|.)~_d_bks}) ]]; then + print -r "bindkey '${_d_line[1][2,-2]}' ${_d_line[2]}" + fi + done >> $_d_file + +print >> $_d_file + + +# Autoloads: whence -w produces "_d_foo: function", so look for +# all functions beginning with `_'. + +_d_als=($(whence -wm '_*' | sort | +while read -rA _d_line; do + [[ ${_d_line[2]} = function ]] && print -r - ${_d_line[1]%:} +done)) + +# print them out: about five to a line looks neat + +while (( $#_d_als )); do + print -n autoload + for (( _i = 0; _i < 5; _i++ )); do + if (( $#_d_als )); then + print -n " $_d_als[1]" + shift _d_als + fi + done + print +done >> $_d_file + +print >> $_d_file + +unset _d_line _d_zle _d_bks _d_als _d_f _f_file diff --git a/Completion/Core/compinit b/Completion/Core/compinit new file mode 100644 index 000000000..ec5867838 --- /dev/null +++ b/Completion/Core/compinit @@ -0,0 +1,269 @@ +# Initialisation for new style completion. This mainly contains some helper +# function and aliases. Everything else is split into different files in this +# directory that will automatically be made autoloaded (see the end of this +# file). +# The names of the files that will be considered for autoloading have to +# start with a underscores (like `_setopt). +# The first line of these files will be read and has to say what should be +# done with its contents: +# +# `#defcomp <names ...>' +# if the first line looks like this, the file is +# autoloaded as a function and that function will +# be called to generate the matches when completing +# for one of the commands whose <name> is given +# +# `#defpatcomp <pattern>' +# this defines a function that should be called to generate +# matches for commands whose name matches <pattern>; note +# that only one pattern may be given +# +# `#defkeycomp <style> [ <key-sequence> ... ] +# this is used to bind special completions to all the given +# <key-sequence>(s). The <style> is the name of one of the built-in +# completion widgets (complete-word, delete-char-or-list, +# expand-or-complete, expand-or-complete-prefix, list-choices, +# menu-complete, menu-expand-or-complete, or reverse-menu-complete). +# This creates a widget behaving like <style> so that the +# completions are chosen as given in the the rest of the file, +# rather than by the context. The widget has the same name as +# the autoload file and can be bound using bindkey in the normal way. +# +# `#autoload' +# this is for helper functions that are not used to +# generate matches, but should automatically be loaded +# when they are called +# +# Note that no white space is allowed between the `#' and the rest of +# the string. +# +# See the file `compdump' for how to speed up initialiation. +# +# If you are using global matching specifications with `compctl -M ...' +# have a look at the files `_match_test' and `_match_pattern'. To make +# all the example functions use matching as specified with `-M' these +# need some editing. +# +# If we got the `-d'-flag, we will automatically dump the new state (at +# the end). + +if [[ "$1" = -d ]]; then + _i_autodump=1 +else + _i_autodump=0 +fi + +# The associative array containing the definitions for the commands. +# Definitions for patterns will be stored in the normal array `_patcomps'. + +typeset -A _comps +_patcomps=() + +# This function is used to register or delete completion functions. For +# registering completion functions, it is invoked with the name of the +# function as it's first argument (after the options). The other +# arguments depend on what type of completion function is defined. If +# none of the `-p' and `-k' options is given a function for a command is +# defined. The arguments after the function name are then interpreted as +# the names of the command for which the function generates matches. +# With the `-p' option a function for a name pattern is defined. This +# function will be invoked when completing for a command whose name +# matches the pattern given as argument after the function name (in this +# case only one argument is accepted). +# With the `-k' option a function for a special completion keys is +# defined and immediatly bound to those keys. Here, the extra arguments +# are the name of one of the builtin completion widgets and any number +# of key specifications as accepted by the `bindkey' builtin. +# In any case the `-a' option may be given which makes the function +# whose name is given as the first argument be autoloaded. When defining +# a function for command names the `-n' option may be given and keeps +# the definitions from overriding any previous definitions for the +# commands. +# For deleting definitions, the `-d' option must be given. Without the +# `-p' option, this deletes definitions for functions for the commands +# whose names are given as arguments. If combined with the `-p' option +# it deletes the definitions for the patterns given as argument. +# The `-d' option may not be combined with the `-k' option, i.e. +# definitions for key function can not be removed. +# +# Examples: +# +# compdef -a foo bar baz +# make the completion for the commands `bar' and `baz' use the +# function `foo' and make this function be autoloaded +# +# compdef -p foo 'c*' +# make completion for all command whose name begins with a `c' +# generate matches by calling the function `foo' before generating +# matches defined for the command itself +# +# compdef -k foo list-choices '^X^M' '\C-xm' +# make the function `foo' be invoked when typing `Control-X Control-M' +# or `Control-X m'; the function should generate matches and will +# behave like the `list-choices' builtin widget +# +# compdef -d bar baz +# delete the definitions for the command names `bar' and `baz' + +compdef() { + local opt autol type func delete new i + + # Get the options. + + while getopts "anpkd" opt; do + case "$opt" in + a) autol=yes;; + n) new=yes;; + [pk]) if [[ -n "$type" ]]; then + # Error if both `-p' and `-k' are given (or one of them + # twice). + echo "$0: type already set to $type" + return 1 + fi + if [[ "$opt" = p ]]; then + type=pattern + else + type=key + fi + ;; + d) delete=yes;; + esac + done + shift OPTIND-1 + + if [[ -z "$delete" ]]; then + # Adding definitions, first get the name of the function name + # and probably do autoloading. + + func="$1" + [[ -n "$autol" ]] && autoload "$func" + shift + + case "$type" in + pattern) + if [[ $# -gt 1 ]]; then + echo "$0: only one pattern allowed" + return 1 + fi + # Patterns are stored in strings like `c* foo', with a space + # between the pattern and the function name. + + _patcomps=("$_patcomps[@]" "$1 $func") + ;; + key) + if [[ $# -lt 2 ]]; then + echo "$0: missing keys" + return 1 + fi + + # Define the widget. + zle -C "$func" "$1" "$func" + shift + + # And bind the keys... + for i; do + bindkey "$i" "$func" + done + ;; + *) + # For commands store the function name in the `_comps' + # associative array, command names as keys. + for i; do + [[ -z "$new" || "${+_comps[$i]}" -eq 0 ]] && _comps[$i]="$func" + done + ;; + esac + else + # Handle the `-d' option, deleting. + case "$type" in + pattern) + # Note the space. + for i; do + _patcomps=("${(@)patcomps:#$i *}") + done + ;; + key) + # Oops, cannot do that yet. + + echo "$0: cannot restore key bindings" + return 1 + ;; + *) + # Deleting definitons for command is even simpler. + for i; do + unset "_comps[$i]" + done + esac + fi +} + +# Now we automatically make the definition files autoloaded. + +# First we get the name of a dump file if this will be used. + +: ${COMPDUMP:=$0.dump} + +if [[ ! -o extendedglob ]]; then + _i_noextglob=yes + setopt extendedglob +fi + +typeset -U _i_files +_i_files=( ${^~fpath}/_*~*~(N:t) ) +_i_initname=$0 +_i_done='' + +# If we have a dump file, load it. + +if [[ -f "$COMPDUMP" ]]; then + read -rA _i_line < "$COMPDUMP" + if [[ _i_autodump -eq 1 && $_i_line[2] -eq $#_i_files ]]; then + builtin . "$COMPDUMP" + _i_done=yes + fi + unset _i_line +fi +if [[ -z "$_i_done" ]]; then + for _i_dir in $fpath; do + [[ $_i_dir = . ]] && continue + for _i_file in $_i_dir/_*~*~(N); do + read -rA _i_line < $_i_file + _i_tag=$_i_line[1] + shift _i_line + if [[ $_i_tag = '#defcomp' ]]; then + compdef -na "${_i_file:t}" "${_i_line[@]}" + elif [[ $_i_tag = '#defpatcomp' ]]; then + compdef -pa "${_i_file:t}" "${_i_line[@]}" + elif [[ $_i_tag = '#defkeycomp' ]]; then + compdef -ka "${_i_file:t}" "${_i_line[@]}" + elif [[ $_i_tag = '#autoload' ]]; then + autoload ${_i_file:t} + fi + done + done + + bindkey | + while read -rA _i_line; do + if [[ "$_i_line[2]" = complete-word || + "$_i_line[2]" = delete-char-or-list || + "$_i_line[2]" = expand-or-complete || + "$_i_line[2]" = expand-or-complete-prefix || + "$_i_line[2]" = list-choices || + "$_i_line[2]" = menu-complete || + "$_i_line[2]" = menu-expand-or-complete || + "$_i_line[2]" = reverse-menu-complete ]]; then + zle -C _complete_$_i_line[2] $_i_line[2] _main_complete + bindkey "${_i_line[1][2,-2]}" _complete_$_i_line[2] + fi + done + + unset _i_dir _i_line _i_file _i_tag + + # If autodumping was requested, do it now. + + (( _i_autodump )) && builtin . ${_i_initname:h}/compdump +fi + +[[ -z "$_i_noextglob" ]] || unsetopt extendedglob + +unset _i_files _i_initname _i_done _i_autodump _i_noextglob diff --git a/Completion/README b/Completion/README new file mode 100644 index 000000000..ac2accfca --- /dev/null +++ b/Completion/README @@ -0,0 +1,107 @@ +The subdirectories contain code for the new function-based completion +system. Broadly speaking, this uses shell functions defined for each +command to determine how the arguments of a command should be completed. + +You should copy all the files you need or want to a directory of your own, +which should be included in your autoload path as defined by $fpath. Then +in your .zshrc you should source the file which appears here in +Core/compinit. It is recommnded that you use the -d option, which outputs +a file containing the necessary variables, bindkeys etc., making later +loading much faster. For example, + [[ -f ~/completion/compinit ]] && . ~/completion/compinit -d +This will rebind any keys which do completion to use the new system. +For more detailed instructions, including how to add new completions, see +the top of Core/compinit . + +The subdirectories contain: + +Core: + The basic functions and files to be sourced. You will certainly need + these, and will most likely not feel like altering them (or, in some + cases, even reading them, unless you are a shell wizard). The files are: + compinit + As already described, this is not a function, but is sourced once + (with the `source' or `.' commands) to set up the completion system. + compdump + This dumps the completions status for faster initialisation. The + easiest way of doing this is to use the -d option to compinit rather + than calling compdump directly. + _comp_parts + Utility used for completing words with multiple separate parts, such as + `<user>@<host>' + _compalso + Utility for calling a function to add additional completions to an + already existing set. + _files + A frontend to _path_files which will default to any old file if the + specified file was not found. + _main_complete + The main entry point called by the key bindings which compinit sets + up (the main `completion widget' in zsh jargon). + _normal + The function called by _main_complete to handle the most common + cases, such as completing a command name or its arguments. This + function dispatches to the various other functions for individual + commands. (Actually, the system is fairly context-sensitive, so + it is wider than just command+argument.) + _path_files + The function usually called to complete filenames and directories. It + replaces the standard -f and -/ options for the basic completion + commands: it can do various extra tricks, such as expanding a whole + path at once, e.g. F/C/C/_p<TAB> -> Functions/Completion/Core/_path_files +Base: + You will almost certainly want these files, too, which handle standard + tasks like completing files. However, you may want to edit them for + your own particular setup. Files are: + _command_names + This handles completion of the command word, i.e. the first thing + on the command line. You may want to alter this, for example, + to complete parameters to assign to. + _condition + This handles completing inside [[ ... ]] . + _default + This handles completion of command arguments when no special function + exists. Usually this means completing files, but you can modify this + as you wish. + _match_pattern + _match_test + These are used by Base/_path_files (and hence also Base/_files) for + file completion with control over matching (whether to complete + case-insensitively, or to allow insertion before `.', etc.) See + _match_test for instructions. Note _path_files expects these files + to be present. + _precommand + Allows completion when the first word on the line has to be ignored, + for example `noglob ...' should ignore the noglob and just complete + as if it wasn't there. Add other such commands to the top line. + _redirect + Completes after `<' or `<': this version calls _files. + _subscript + For completion in subscripts of parameters, e.g $foo[...]. + _vars + Completion for commands which need variables (so this could also be in + the Builtins directory), but also in math environments such as ((...)). +Builtins: + Define completions for various shell builtins. The top line of each file + says which builtins they apply to; in many cases you can guess from the + name. Note in particular that _zftp defines completions for all commands + beginning `zf', not just for the module command zftp. This is only + really useful if you use zftp with the zf* function suite (zfopen, zfget, + ...). +User: + This contains a pot pourri of completions for various external commands. + Not all will work unmodified on your system. +Commands: + These functions define separate completion commands which do not use + the usual context information, and hence have to be bound separately + to keys. As they appear, they have bindings which you can change or + delete by altering the top line of the file. To bind a function + (strictly speaking, the corresponding completion widget) yourself + after completion is loaded, use `bindkey '<key-string>' <_function_name>'. + The files are: + _correct_filename, bound to \C-xc + Correct the word under the cursor as a filename. This is significantly + more powerful than the standard \e$ (spell-word) binding. + _most_recent_file, bound to \C-xm + Insert the name of the most recent file matching the pattern + so far on the command line. diff --git a/Completion/User/_a2ps b/Completion/User/_a2ps new file mode 100644 index 000000000..9aa9d3d99 --- /dev/null +++ b/Completion/User/_a2ps @@ -0,0 +1,22 @@ +#defcomp a2ps + +if [[ -prefix -- ]]; then + _comp_parts '(--borders --compact --truncate-lines --interpret + --print-anyway --delegate)' '=' '(yes no)' + _comp_parts '(--major)' '=' '(rows columns)' + _comp_parts '(--end-of-line)' '=' '(r n nr rn any)' + + complist -S= -k '(--medium --columns --rows --line-numbers + --font-size --lines-per-page --chars-per-line + --tabsize --non-printable-format --encoding + --title --stdin --prologue --highlight-level + --strip-level --output --version-control --suffix + --printer --copies --sides --page-prefeed + --no-page-prefeed)' + complist -qS= -k '(--margin --header --underlay --left-title + --right-title --left-footer --footer --right-footer + --pages --pretty-print)' + complist -k '(--landscape --portrait --catman --no-header)' +else + _files -F fignore -g "*~*.ps" +fi diff --git a/Completion/User/_compress b/Completion/User/_compress new file mode 100644 index 000000000..860aeb5b0 --- /dev/null +++ b/Completion/User/_compress @@ -0,0 +1,3 @@ +#defcomp compress + +_files -g '*~*.Z' diff --git a/Completion/User/_configure b/Completion/User/_configure new file mode 100644 index 000000000..de8d5fba5 --- /dev/null +++ b/Completion/User/_configure @@ -0,0 +1,12 @@ +#defcomp configure + +if [[ $PREFIX = *=* ]]; then + # Complete filenames after e.g. --prefix= + IPREFIX=${PREFIX%%=*}= + PREFIX=${PREFIX#*=} + complist -f +else + # Generate a list of options from configure --help + complist -s '$($COMMAND --help | + sed -n -e '\''s/^ *\(--[-a-z0-9]*\)[ =,].*$/\1/p'\'')' +fi diff --git a/Completion/User/_dd b/Completion/User/_dd new file mode 100644 index 000000000..2458541ea --- /dev/null +++ b/Completion/User/_dd @@ -0,0 +1,13 @@ +#defcomp dd + +if [[ -iprefix conv= ]]; then + # If there's a comma present, ignore up to the last one. The + # test alone will have that effect. + [[ -string , ]] + complist -S, -q \ + -k '(ascii ebcdic ibm block unblock lcase ucase swab noerror sync)' +elif [[ -iprefix 'if=' || -iprefix 'of=' ]]; then + _files +else + complist -S '=' -k '(if of ibs obs bs cbs skip files seek count conv)' +fi diff --git a/Completion/User/_dvi b/Completion/User/_dvi new file mode 100644 index 000000000..bb2fc293e --- /dev/null +++ b/Completion/User/_dvi @@ -0,0 +1,3 @@ +#defcomp xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype + +_files -g '*.(dvi|DVI)' diff --git a/Completion/User/_find b/Completion/User/_find new file mode 100644 index 000000000..ca4f79908 --- /dev/null +++ b/Completion/User/_find @@ -0,0 +1,21 @@ +#defcomp find + +if [[ -mbetween -(ok|exec) \\\; ]]; then + _normal "$@" +elif [[ -iprefix - ]]; then + complist -s 'daystart {max,min,}depth follow noleaf version xdev \ + {a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \ + {i,}{l,}name {no,}{user,group} path perm regex size true uid used \ + exec {f,}print{f,0,} ok prune ls' +elif [[ -position 1 ]]; then + complist -g '. ..' + _files -g '(-/)' +elif [[ -mcurrent -1 -((a|c|)newer|fprint(|0|f)) ]]; then + _files +elif [[ -current -1 -fstype ]]; then + complist -k '(ufs 4.2 4.3 nfs tmp mfs S51K S52K)' +elif [[ -current -1 -group ]]; then + complist -k groups +elif [[ -current -1 -user ]]; then + complist -u +fi diff --git a/Completion/User/_gunzip b/Completion/User/_gunzip new file mode 100644 index 000000000..35a27e774 --- /dev/null +++ b/Completion/User/_gunzip @@ -0,0 +1,3 @@ +#defcomp gunzip zcat + +_files -g '*.[gG][z]' diff --git a/Completion/User/_gzip b/Completion/User/_gzip new file mode 100644 index 000000000..3cda1e4ed --- /dev/null +++ b/Completion/User/_gzip @@ -0,0 +1,3 @@ +#defcomp gzip + +_files -g '*~*.[gG][zZ]' diff --git a/Completion/User/_hosts b/Completion/User/_hosts new file mode 100644 index 000000000..3acc327ac --- /dev/null +++ b/Completion/User/_hosts @@ -0,0 +1,3 @@ +#defcomp ftp ncftp ping rwho rup xping traceroute nslookup + +complist -k hosts diff --git a/Completion/User/_make b/Completion/User/_make new file mode 100644 index 000000000..d576b0308 --- /dev/null +++ b/Completion/User/_make @@ -0,0 +1,3 @@ +#defcomp make gmake pmake + +complist -s "\$(awk '/^[a-zA-Z0-9][^/ ]+:/ {print \$1}' FS=: [mM]akefile)" diff --git a/Completion/User/_man b/Completion/User/_man new file mode 100644 index 000000000..8204fba0b --- /dev/null +++ b/Completion/User/_man @@ -0,0 +1,11 @@ +#defcomp man +setopt localoptions rcexpandparam + +local rep +if [[ $2 = (<->*|ln) ]]; then + rep=( $manpath/(man|cat)$2/$PREFIX*$SUFFIX.<->*(N:t:r) ) +else + rep=( $manpath/(man|cat)*/$PREFIX*$SUFFIX.<->*(N:t:r) ) +fi + +(( $#rep )) && compadd -m $rep diff --git a/Completion/User/_mh b/Completion/User/_mh new file mode 100644 index 000000000..67ce49fd2 --- /dev/null +++ b/Completion/User/_mh @@ -0,0 +1,70 @@ +#defcomp folder comp inc mark refile repl scan show next prev rmm pick whom mhn mhpath mhpatch + +# Completion for all possible MH commands. +# Alter the following two to your own mh directory and the directory +# where standard mh library files live. (It works anyway, but this +# will save a little time.) +local mymhdir=~/Mail +local mhlib=/usr/lib/mh + +# To be on the safe side, check this exists and if not, get it anyway. +[[ -d $mymhdir ]] || mymhdir=$(mhpath +) + +if [[ -iprefix - ]]; then + # get list of options, which MH commands can generate themselves + # awk is just too icky to use for this, sorry. send me one if + # you come up with it. + compadd -m $($COMMAND -help | perl -ne 'if (/^\s*-\(?(\S+)/) { + $n = $1; + $n =~ s/\)//g; + print $n =~ s/^\[([a-z]+)\]// ? "$n\n$1$n\n" : "$n\n"; + }') + return +elif [[ -iprefix '+' || -iprefix '@' || -current -1 -draftfolder ]]; then + # Complete folder names. + local mhpath + if [[ $IPREFIX != '@' ]]; then + [[ $IPREFIX = '+' ]] || IPREFIX=+ + mhpath=$mymhdir + else + mhpath=$(mhpath) + fi + + # painless, or what? + complist -W mhpath -/ +elif [[ -mcurrent -1 -(editor|(whatnow|rmm|show|more)proc) ]]; then + complist -c +elif [[ -current -1 -file ]]; then + complist -f +elif [[ -mcurrent -1 -(form|audit|filter) ]]; then + # Need some MH template file, which may be in our own MH directory + # or with the standard library. + local mhfpath + # This is the only place we need mhlib, so leave the test till here. + [[ -d $mhlib ]] || { mhlib=$(mhparam mhlproc); mhlib=$mhlib:h; } + mhfpath=($mymhdir $mhlib) + + complist -W mhfpath -g '*(.)' +elif [[ -mcurrent -1 -(no|)cc ]]; then + compadd -m all to cc me +elif [[ -mcurrent -1 -[rw]cache ]]; then + compadd -m public private never ask +else + # Generate sequences. + local foldnam folddir f + for f in $argv; do + [[ $f = [@+]* ]] && foldnam=$f + done + if [[ $foldnam = '+'* ]]; then + folddir=$mymhdir/${foldnam#+} + elif [[ $foldnam = '@'* ]]; then + folddir=$(mhpath)/${foldnam#@} + else + folddir=$(mhpath) + # leaving foldnam empty works here + fi + + complist -s '$(mark $foldnam | awk -F: '\''{ print $1 }'\'')' + compadd -m reply next cur prev first last all unseen + complist -W folddir -g '<->' +fi diff --git a/Completion/User/_pdf b/Completion/User/_pdf new file mode 100644 index 000000000..7d7756c3e --- /dev/null +++ b/Completion/User/_pdf @@ -0,0 +1,3 @@ +function acroread + +_files -g '*.(pdf|PDF)' diff --git a/Completion/User/_ps b/Completion/User/_ps new file mode 100644 index 000000000..6bc0643b2 --- /dev/null +++ b/Completion/User/_ps @@ -0,0 +1,3 @@ +#defcomp gs ghostview gview psnup psselect pswrap pstops pstruct lpr + +_files -g '*([pP][sS]|eps)' diff --git a/Completion/User/_rcs b/Completion/User/_rcs new file mode 100644 index 000000000..537db6278 --- /dev/null +++ b/Completion/User/_rcs @@ -0,0 +1,9 @@ +#defcomp co ci rcs + +[[ $COMMAND = ci || $COMMAND = rcs ]] && _files + +if [[ $NMATCHES -eq 0 && -d RCS && $COMMAND != ci ]]; then + local rep + rep=(RCS/$PREFIX*$SUFFIX,v(:t:s/\,v//)) + (( $#rep )) && compadd -m $rep +fi diff --git a/Completion/User/_rlogin b/Completion/User/_rlogin new file mode 100644 index 000000000..e36554f23 --- /dev/null +++ b/Completion/User/_rlogin @@ -0,0 +1,9 @@ +#defcomp rlogin rsh ssh + +if [[ -position 1 ]]; then + complist -k hosts +elif [[ -position 2 ]]; then + complist -k '(-l)' +else + complist -u +fi diff --git a/Completion/User/_strip b/Completion/User/_strip new file mode 100644 index 000000000..6962ac455 --- /dev/null +++ b/Completion/User/_strip @@ -0,0 +1,2 @@ +#defcomp strip +_files -g '*(*)' diff --git a/Completion/User/_stty b/Completion/User/_stty new file mode 100644 index 000000000..6b54b5007 --- /dev/null +++ b/Completion/User/_stty @@ -0,0 +1,16 @@ +#defcomp stty + +if [[ -mcurrent -1 \ + (*erase|discard|status|dsusp|intr|kill|lnext|quit|reprint|start|s*p) ]] +then + compadd -m -Q '^-' '^h' '^?' '^c' '^u' +else + [[ -string '-' || -string '+' ]] + compadd -m rows columns intr quit erase kill eof eol \ + eol2 start stop susp dsusp reprint discard werase lnext \ + parenb parodd cs8 cstopb hupcl cread clocal parext \ + ignbrk brkint ignpar parmrk inpck istrip inlcr igncr icrnl iuclc \ + ixon ixany ixoff imaxbel isig icanon xcase echo echoe echok \ + echonl noflsh tostop echoctl echoprt echoke flusho pending iexten \ + opost olcuc onlcr ocrnl onocr onlret ofill ofdel +fi diff --git a/Completion/User/_tar b/Completion/User/_tar new file mode 100644 index 000000000..91767e44d --- /dev/null +++ b/Completion/User/_tar @@ -0,0 +1,11 @@ +#defcomp tar + +local nm=$NMATCHES tf="$2" + +if [[ ( -mword 1 *t*f* || -mword 1 *x*f* ) && -position 3 100000 ]]; then + complist -k "( $(tar tf $tf) )" +elif [[ -mword 1 *c*f* && -position 3 100000 ]]; then + _files +elif [[ -mcurrent -1 *f* && -position 2 ]]; then + _files -g '*.(tar|TAR)' +fi diff --git a/Completion/User/_tex b/Completion/User/_tex new file mode 100644 index 000000000..1f645e2a2 --- /dev/null +++ b/Completion/User/_tex @@ -0,0 +1,3 @@ +#defcomp tex latex slitex + +_files -g '*.(tex|TEX|texinfo|texi)' diff --git a/Completion/User/_uncompress b/Completion/User/_uncompress new file mode 100644 index 000000000..e25805d50 --- /dev/null +++ b/Completion/User/_uncompress @@ -0,0 +1,3 @@ +#defcomp uncompress zmore + +_files -g '*.Z' diff --git a/Completion/User/_x_options b/Completion/User/_x_options new file mode 100644 index 000000000..cc469286d --- /dev/null +++ b/Completion/User/_x_options @@ -0,0 +1,5 @@ +#defpatcomp */X11/* + +# A simple pattern completion, just as an example. + +complist -J options -k '(-display -name -xrm)' diff --git a/Completion/User/_xfig b/Completion/User/_xfig new file mode 100644 index 000000000..fcd2bba9f --- /dev/null +++ b/Completion/User/_xfig @@ -0,0 +1,3 @@ +#defcomp xfig + +_files -g '*.fig' diff --git a/Config/version.mk b/Config/version.mk index 3e36faf5c..496dea13a 100644 --- a/Config/version.mk +++ b/Config/version.mk @@ -27,5 +27,5 @@ # This must also serve as a shell script, so do not add spaces around the # `=' signs. -VERSION=3.1.5-pws-9 -VERSION_DATE='February 18, 1999' +VERSION=3.1.5-pws-10 +VERSION_DATE='February 26, 1999' diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo index 2cb12e2c2..4208317fb 100644 --- a/Doc/Zsh/compwid.yo +++ b/Doc/Zsh/compwid.yo @@ -162,7 +162,7 @@ xitem(tt(compadd) [ tt(-qQfnUam) ] [ tt(-F) var(array) ]) xitem([ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ]) xitem([ tt(-p) var(hidden-prefix) ] [ tt(-s) var(hidden-suffix) ]) xitem([ tt(-i) var(ignored-prefix) ] [ tt(-W) var(file-prefix) ]) -xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ]) +xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ]) xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ]) item([ tt(-M) var(match-spec) ] [ tt(--) ] [ var(words) ... ])( @@ -182,14 +182,17 @@ item(tt(-S) var(suffix))( Like tt(-P) but gives a string that has to be inserted after the match. ) item(tt(-p) var(hidden-prefix))( -This gives a string that should be -... +This gives a string that should be inserted in the line before the +match but that should not appear in the list of matches. ) item(tt(-s) var(hidden-suffix))( -... +Like `tt(-p)', but gives a string to insert after the match. ) item(tt(-i) var(ignored-prefix))( -... +This gives a string to insert into the command line just before any +string given with the `tt(-P)' option. Without `tt(-P)' the string is +inserted before the string given with `tt(-p)' or directly before the +match. ) item(tt(-J) var(name))( As for tt(compctl) and tt(complist) this gives the name of the group @@ -198,6 +201,10 @@ of matches the words should be stored in. item(tt(-V) var(name))( Like tt(-J) but naming a unsorted group. ) +item(tt(-X) var(explanation))( +The var(explanation) string will be printed with the list of matches, +as for tt(compctl -X). +) item(tt(-q))( This flag has the same meaning as for tt(compctl) and tt(complist), too. It makes the suffix given with tt(-S) be automatically removed if @@ -382,15 +389,19 @@ given, it is true if the number of words is equal to or greater than var(min) and equal to or less than var(max) ) item(tt(-after) var(string))( -true if the cursor is after a word that is equal to var(string) +true if the cursor is after a word that is equal to var(string); this +removes all words up to and including the matched word from the +positional parameters ) item(tt(-mafter) var(pattern))( like tt(-after) but using pattern matching ) item(tt(-between) var(string1) var(string2))( true if the cursor is after a word that is equal to var(string1), if -there is also a word that is equal to va(string2), this is true only -if the cursor is before it +there is also a word that is equal to var(string2), this is true only +if the cursor is before it; as a side effect, all words before +var(string1) and after var(string2) (both inclusive) are removed from +the positional parameters ) item(tt(-mbetween) var(pattern1) var(pattern2))( like tt(-between) but using pattern matching diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index ccf245367..fa56bc0af 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -869,7 +869,7 @@ can be specified by separating two characters by a `tt(-)'. A `tt(-)' or `tt(])' may be matched by including it as the first character in the list. There are also several named classes of characters, in the form -`tt([:)var(name)(tt:])' with the following meanings: `tt([:alnum:])' +`tt([:)var(name)tt(:])' with the following meanings: `tt([:alnum:])' alphanumeric, `tt([:alpha:])' alphabetic, `tt([:blank:])' space or tab, `tt([:cntrl:])' control character, `tt([:digit:])' decimal @@ -988,12 +988,18 @@ item(I)( Case sensitive: locally negates the effect of tt(i) or tt(l) from that point on. ) +item(tt(a)var(num))( +Approximate matching: var(num) errors are allowed in the string matched by +the pattern. The rules for this are described in the next subsection. +) enditem() For example, the test string tt(fooxx) can be matched by the pattern tt(LPAR()#i)tt(RPAR()FOOXX), but not by tt(LPAR()#l)tt(RPAR()FOOXX), tt(LPAR()#i)tt(RPAR()FOO)tt(LPAR()#I)tt(RPAR()XX) or -tt(LPAR()LPAR()#i)tt(RPAR()FOOX)tt(RPAR()X). +tt(LPAR()LPAR()#i)tt(RPAR()FOOX)tt(RPAR()X). The string +tt(LPAR()#ia2)tt(RPAR()readme) specifies case-insensitive matching of +tt(readme) with up to two errors. When using the ksh syntax for grouping both tt(KSH_GLOB) and tt(EXTENDED_GLOB) must be set and the left parenthesis should be @@ -1004,6 +1010,61 @@ examining whole paths case-insensitively every directory must be searched for all files which match, so that a pattern of the form tt(LPAR()#i)tt(RPAR()/foo/bar/...) is potentially slow. +subsect(Approximate Matching) +When matching approximately, the shell keeps a count of the errors found, +which cannot exceed the number specified in the +tt(LPAR()#a)var(num)tt(RPAR()) flags. Four types of error are recognised: + +startitem() +item(1.)( +Different characters, as in tt(fooxbar) and tt(fooybar). +) +item(2.)( +Transposition of characters, as in tt(banana) and tt(abnana). +) +item(3.)( +A character missing in the target string, as with the pattern tt(road) and +target string tt(rod). +) +item(4.)( +An extra character appearing in the target string, as with tt(stove) +and tt(strove). +) +enditem() + +Thus, the pattern tt(LPAR()#a3)tt(RPAR()abcd) matches tt(dcba), with the +errors occurring by using the first rule twice and the second once, +grouping the string as tt([d][cb][a]) and tt([a][bc][d]). + +Non-literal parts of the pattern must match exactly, including characters +in character ranges: hence tt(LPAR()#a1)tt(RPAR()???) matches strings of +length four, by applying rule 4 to an empty part of the pattern, but not +strings of length three, since all the tt(?) must match. Other characters +which must match exactly are initial dots in filenames (unless the +tt(GLOB_DOTS) option is set), and all slashes in file names, so that +tt(a/bc) is two errors from tt(ab/c) (the slash cannot be transposed with +another character). Similarly, errors are counted separately for +non-contiguous strings in the pattern, so that tt(LPAR()ab|cd)tt(RPAR()ef) +is two errors from tt(aebf). + +When using exclusion via the tt(~) operator, approximate matching is +treated entirely separately for the excluded part and must be activated +separately. Thus, tt(LPAR()#a1)tt(RPAR()README~READ_ME) matches +tt(READ.ME) but not tt(READ_ME), as the trailing tt(READ_ME) is matched +without approximation. However, +tt(LPAR()#a1)tt(RPAR()README~LPAR()#a1)tt(RPAR()READ_ME) +does not match any pattern of the form tt(READ)var(?)tt(ME) as all +such forms are now excluded. + +Apart from exclusions, there is only one overall error count; however, the +maximum errors allowed may be altered locally, and this can be delimited by +grouping. For example, +tt(LPAR()#a1)tt(RPAR()cat)tt(LPAR()LPAR()#a0)tt(RPAR()dog)tt(RPAR()fox) +allows one error in total, which may not occur in the tt(dog) section, and +the pattern +tt(LPAR()#a1)tt(RPAR()cat)tt(LPAR()#a0)tt(RPAR()dog)tt(LPAR()#a1)tt(RPAR()fox) +is equivalent. + subsect(Recursive Globbing) A pathname component of the form `tt(LPAR())var(foo)tt(/RPAR()#)' matches a path consisting of zero or more directories diff --git a/Doc/Zsh/guide.yo b/Doc/Zsh/guide.yo index e0005e339..96e12fc58 100644 --- a/Doc/Zsh/guide.yo +++ b/Doc/Zsh/guide.yo @@ -144,8 +144,8 @@ then be processed with bf(dvips) and optionally bf(gs) (Ghostscript) to produce a nicely formatted printed guide. ) item(The HTML guide)( -Mark Borges, tt(<mdb@cdc.noaa.gov), maintains an HTML version of this -guide at tt(http://www.peak.org/zsh/Doc/zsh_toc.html). +An HTML version of this guide is available at the Zsh web site via +tt(http://sunsite.auc.dk/zsh/Doc/index.html). (The HTML version is produced with bf(texi2html), which may be obtained from tt(http://wwwcn.cern.ch/dci/texi2html/). The command is `tt(texi2html -split_chapter -expandinfo zsh.texi)'.) diff --git a/Misc/globtests b/Misc/globtests index 9a12a11e1..3bd634ca9 100755 --- a/Misc/globtests +++ b/Misc/globtests @@ -103,6 +103,7 @@ t :] [:]]# t [ [[] t ] []] t [] [^]]] +# Case insensitive matching t fooxx (#i)FOOXX f fooxx (#l)FOOXX t FOOXX (#l)fooxx @@ -113,5 +114,36 @@ f fooxx ((#i)FOOX)X f BAR (bar|(#i)foo) t FOO (bar|(#i)foo) t Modules (#i)*m* +# Approximate matching +t READ.ME (#ia1)readme +f READ..ME (#ia1)readme +t README (#ia1)readm +t READM (#ia1)readme +t README (#ia1)eadme +t EADME (#ia1)readme +t READEM (#ia1)readme +f ADME (#ia1)readme +f README (#ia1)read +t bob (#a1)[b][b] +f bob (#a1)[b][b]a +t bob (#a1)[b]o[b]a +f bob (#a1)[c]o[b] +t abcd (#a2)XbcX +t abcd (#a2)ad +t ad (#a2)abcd +t abcd (#a2)bd +t bd (#a2)abcd +t badc (#a2)abcd +# This next one is a little tricky: a[d]bc[] = a[]bc[d] +t adbc (#a2)abcd +f dcba (#a2)abcd +# the next one is [d][cb][a] = [a][bc][d] with a transposition +t dcba (#a3)abcd +t aabaXaaabY (#a1)(a#b)#Y +t aabaXaaabY (#a1)(a#b)(a#b)Y +t aaXaaaaabY (#a1)(a#b)(a#b)Y +f read.me (#ia1)README~READ.ME +t read.me (#ia1)README~READ_ME +f read.me (#ia1)README~(#a1)READ_ME EOT print "$failed tests failed." diff --git a/Src/.cvsignore b/Src/.cvsignore index edec5401b..fcef4da67 100644 --- a/Src/.cvsignore +++ b/Src/.cvsignore @@ -19,6 +19,7 @@ zsh libzsh.so* sigcount.h signames.c +version.h zshpaths.h zshxmods.h bltinmods.list diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index 2af8dc6ac..7825a900d 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -37,7 +37,7 @@ # include "rlimits.h" -# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED) +# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED) static rlim_t zstrtorlimt(const char *s, char **t, int base) { @@ -62,9 +62,9 @@ zstrtorlimt(const char *s, char **t, int base) *t = (char *)s; return ret; } -# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */ +# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */ # define zstrtorlimt(a, b, c) zstrtol((a), (b), (c)) -# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */ +# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */ /* Display resource limits. hard indicates whether `hard' or `soft' * * limits should be displayed. lim specifies the limit, or may be -1 * @@ -107,9 +107,15 @@ showlimits(int hard, int lim) else printf("%qdkB\n", val / 1024L); # else +# ifdef RLIM_T_IS_LONG_LONG + printf("%lldMB\n", val / (1024L * 1024L)); + else + printf("%lldkB\n", val / 1024L); +# else printf("%ldMB\n", val / (1024L * 1024L)); else printf("%ldkB\n", val / 1024L); +# endif /* RLIM_T_IS_LONG_LONG */ # endif /* RLIM_T_IS_QUAD_T */ } } diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 0d5419817..76ac67114 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -271,9 +271,10 @@ struct cline { #define CLF_MISS 4 #define CLF_DIFF 8 #define CLF_SUF 16 -#define CLF_NEW 32 -#define CLF_VAR 64 -#define CLF_JOIN 128 +#define CLF_PNEW 32 +#define CLF_SNEW 64 +#define CLF_VAR 128 +#define CLF_JOIN 256 /* Flags for makecomplist*(). Things not to do. */ diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c index 77fe0a1fe..72f3cea53 100644 --- a/Src/Zle/comp1.c +++ b/Src/Zle/comp1.c @@ -49,10 +49,10 @@ void (*makecompparamsptr) _((void)); /* pointers to functions required by compctl and defined by zle */ /**/ -void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char **)); +void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **)); /**/ -char *(*comp_strptr) _((int*,int*)); +char *(*comp_strptr) _((int*, int*, int)); /**/ int (*getcpatptr) _((char *, int, char *, int)); @@ -410,6 +410,7 @@ setup_comp1(Module m) cc_first.mask2 = CC_CCCONT; compcontext = compcommand = compprefix = compsuffix = compiprefix = NULL; + makecompparamsptr = NULL; return 0; } diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index e3b778508..f71d67510 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1676,7 +1676,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) char *p, **sp, *e; char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL; char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL; - char *ign = NULL, *rf = NULL; + char *ign = NULL, *rf = NULL, *expl = NULL; int f = 0, a = 0, dm; Cmatcher match = NULL; @@ -1757,6 +1757,10 @@ bin_compadd(char *name, char **argv, char *ops, int func) e = "matching specification expected after -%c"; dm = 1; break; + case 'X': + sp = &expl; + e = "string expected after -%c"; + break; case 'r': f |= CMF_REMOVE; sp = &rs; @@ -1802,7 +1806,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) match = cpcmatcher(match); addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group, - rs, rf, ign, f, a, match, argv); + rs, rf, ign, f, a, match, expl, argv); freecmatcher(match); return 0; @@ -2049,8 +2053,8 @@ cond_strcl(char **a, int id) zerr("zle not loaded, zle condition not available", NULL, 0); return 1; } - i = getcpatptr(comp_strptr(&ipl, NULL), i, s, id); - if (i != -1) { + i = getcpatptr(comp_strptr(&ipl, NULL, 1), i, s, id); + if (i != -1 && i >= ipl) { ignore_prefix(i - ipl); return 1; } diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index d055c45c1..28781e7f0 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -478,6 +478,17 @@ bin_zle_complete(char *name, char **args, char *ops, char func) Thingy t; Widget w, cw; +#ifdef DYNAMIC + if (!require_module(name, "compctl", 0, 0)) { + zerrnam(name, "can't load compctl module", NULL, 0); + return 1; + } +#else + if (!makecompparamsptr) { + zerrnam(name, "compctl module not available", NULL, 0); + return 1; + } +#endif t = rthingy(args[1]); cw = t->widget; unrefthingy(t); diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index af78e1c06..4b42640e1 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -214,11 +214,9 @@ static Cmlist mstack; /* The completion matchers used when building new stuff for the line. */ -static struct cmlist bmstack0, bmstack1; -static Cmlist bmstack; -static int had_lm; +static Cmlist bmatchers; -/* A list with references to all matcher we used. */ +/* A list with references to all matchers we used. */ static LinkList matchers; @@ -293,12 +291,24 @@ enum { COMP_COMPLETE, static int lastambig; -/* Non-zero if the string on the line came from a previous completion. * - * This is used to detect if the string should be taken as an exact * - * match (see do_ambiguous()). */ +/* This says what of the state the line is in when completion is started * + * came from a previous completion. If the FC_LINE bit is set, the * + * string was inserted. If FC_INWORD is set, the last completion moved * + * the cursor into the word although it was at the end of it when the * + * last completion was invoked. * + * This is used to detect if the string should be taken as an exact * + * match (see do_ambiguous()) and if the cursor has to be moved to the * + * end of the word before generating the completions. */ static int fromcomp; +/* This holds the end-position of the last string inserted into the line. */ + +static int lastend; + +#define FC_LINE 1 +#define FC_INWORD 2 + /**/ void completecall(void) @@ -607,6 +617,12 @@ docomplete(int lst) return; } + /* We may have to reset the cursor to its position after the * + * string inserted by the last completion. */ + + if (fromcomp & FC_INWORD) + cs = lastend; + /* Check if we have to start a menu-completion (via automenu). */ if ((amenu = (isset(AUTOMENU) && lastambig && @@ -1567,6 +1583,83 @@ addtocline(Cline *retp, Cline *lrp, *lrp = ln; } +/* This compares two cpattern lists and returns non-zero if they are + * equal. */ + +static int +cmp_cpatterns(Cpattern a, Cpattern b) +{ + while (a) { + if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256)) + return 0; + a = a->next; + b = b->next; + } + return 1; +} + +/* This compares two cmatchers and returns non-zero if they are equal. */ + +static int +cmp_cmatchers(Cmatcher a, Cmatcher b) +{ + return (a == b || + (a->flags == b->flags && + a->llen == b->llen && a->wlen == b->wlen && + (!a->llen || cmp_cpatterns(a->line, b->line)) && + (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) && + (!(a->flags & CMF_LEFT) || + (a->lalen == b->lalen && + (!a->lalen || cmp_cpatterns(a->left, b->left)))) && + (!(a->flags & CMF_RIGHT) || + (a->ralen == b->ralen && + (!a->ralen || cmp_cpatterns(a->right, b->right)))))); +} + +/* Add the given matchers to the bmatcher list. */ + +static void +add_bmatchers(Cmatcher m) +{ + Cmlist old = bmatchers, *q = &bmatchers, n; + + for (; m; m = m->next) { + if ((!m->flags && m->wlen > 0 && m->llen > 0) || + (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) { + *q = n = (Cmlist) halloc(sizeof(struct cmlist)); + n->matcher = m; + q = &(n->next); + } + } + *q = old; +} + +/* This is called when the matchers in the mstack have changed to + * ensure that the bmatchers list contains no matchers not in mstack. */ + +static void +update_bmatchers(void) +{ + Cmlist p = bmatchers, q = NULL, ms; + Cmatcher mp; + int t; + + while (p) { + t = 0; + for (ms = mstack; ms && !t; ms = ms->next) + for (mp = ms->matcher; mp && !t; mp = mp->next) + t = cmp_cmatchers(mp, p->matcher); + + p = p->next; + if (!t) { + if (q) + q->next = p; + else + bmatchers = p; + } + } +} + /* When building up cline lists that are to be inserted at the end of the * string or on the left hand side in the middle, we do this separately for * multiple runs of characters separated by the anchors of `*' match patterns. @@ -1582,21 +1675,20 @@ end_list(int len, char *str) char *p = str; while (len) { - for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { - for (mp = ms->matcher; mp && !t; mp = mp->next) { - if (mp->flags == CMF_RIGHT && mp->wlen == -1 && - !mp->llen && len >= mp->ralen && mp->ralen && - pattern_match(mp->right, str, NULL, NULL)) { - /* This is one of those patterns, so add a cline struct. - * We store the anchor string in the line and the contents - * (i.e. the strings between the anchors) in the word field. */ - *q = getcline(str, mp->ralen, p, str - p, 0); - q = &((*q)->next); - str += mp->ralen; - len -= mp->ralen; - p = str; - t = 1; - } + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + if (mp->flags == CMF_RIGHT && mp->wlen == -1 && + !mp->llen && len >= mp->ralen && mp->ralen && + pattern_match(mp->right, str, NULL, NULL)) { + /* This is one of those patterns, so add a cline struct. + * We store the anchor string in the line and the contents + * (i.e. the strings between the anchors) in the word field. */ + *q = getcline(str, mp->ralen, p, str - p, 0); + q = &((*q)->next); + str += mp->ralen; + len -= mp->ralen; + p = str; + t = 1; } } if (!t) { @@ -1616,7 +1708,7 @@ end_list(int len, char *str) /* This builds a string that may be put on the line that fully matches the * given strings. The return value is NULL if no such string could be built - * or that string, allocated on the heap. + * or that string in local static memory, dup it. * All this is a lot like the procedure in bld_new_pfx(), only slightly * simpler, see that function for more comments. */ @@ -1644,60 +1736,59 @@ join_strs(int la, char *sa, int lb, char *sb) } while (la && lb) { if (*sa != *sb) { - for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { - for (mp = ms->matcher; mp && !t; mp = mp->next) { - if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && - mp->wlen <= la && mp->wlen <= lb) { - if (pattern_match(mp->word, sa, NULL, ea)) { - if (mp->llen + 1 > llen) { - if (llen) - zfree(line, llen); - line = (char *) zalloc(llen = mp->llen + 20); - } - if ((bl = bld_line_pfx(mp->line, line, line, - lb, sb, ea))) { - line[mp->llen] = '\0'; - if (rr <= mp->llen) { - char *or = rs; - - rs = realloc(rs, (rl += 20)); - rr += 20; - rp += rs - or; - } - memcpy(rp, line, mp->llen); - rp += mp->llen; - rr -= mp->llen; - sa += mp->wlen; - sb += bl; - la -= mp->wlen; - lb -= bl; - t = 1; - } - } else if (pattern_match(mp->word, sb, NULL, ea)) { - if (mp->llen + 1 > llen) { - if (llen) - zfree(line, llen); - line = (char *) zalloc(llen = mp->llen + 20); + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= la && mp->wlen <= lb) { + if (pattern_match(mp->word, sa, NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + lb, sb, ea))) { + line[mp->llen] = '\0'; + if (rr <= mp->llen) { + char *or = rs; + + rs = realloc(rs, (rl += 20)); + rr += 20; + rp += rs - or; } - if ((bl = bld_line_pfx(mp->line, line, line, - la, sa, ea))) { - line[mp->llen] = '\0'; - if (rr <= mp->llen) { - char *or = rs; - - rs = realloc(rs, (rl += 20)); - rr += 20; - rp += rs - or; - } - memcpy(rp, line, mp->llen); - rp += mp->llen; - rr -= mp->llen; - sa += bl; - sb += mp->wlen; - la -= bl; - lb -= mp->wlen; - t = 1; + memcpy(rp, line, mp->llen); + rp += mp->llen; + rr -= mp->llen; + sa += mp->wlen; + sb += bl; + la -= mp->wlen; + lb -= bl; + t = 1; + } + } else if (pattern_match(mp->word, sb, NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + la, sa, ea))) { + line[mp->llen] = '\0'; + if (rr <= mp->llen) { + char *or = rs; + + rs = realloc(rs, (rl += 20)); + rr += 20; + rp += rs - or; } + memcpy(rp, line, mp->llen); + rp += mp->llen; + rr -= mp->llen; + sa += bl; + sb += mp->wlen; + la -= bl; + lb -= mp->wlen; + t = 1; } } } @@ -1725,7 +1816,7 @@ join_strs(int la, char *sa, int lb, char *sb) *rp = '\0'; - return dupstring(rs); + return rs; } /* This gets two cline lists with separated runs and joins the corresponding @@ -1752,26 +1843,55 @@ join_ends(Cline o, Cline n, int *olp, int *nlp) * and mark the cline so that we don't try to join it again. */ o->flags |= CLF_JOIN; o->llen = strlen(j); - o->line = j; + o->line = dupstring(j); bld_pfx(o, n, &mol, &mnl); smol += mol + o->llen; smnl += mnl + n->llen; } else { - /* Different anchor strings, so shorten the list. */ + /* Different anchors, see if we can find matching anchors + * further down the lists. */ + Cline to, tn; + int t = 0; + + /* But first build the common prefix. */ bld_pfx(o, n, &mol, &mnl); smol += mol; smnl += mnl; - o->flags |= CLF_MISS; - o->next = NULL; - o->llen = 0; - if (olp) - *olp = smol; - if (nlp) - *nlp = smnl; - return ret; + + for (to = o; to && !t; to = to->next) { + for (tn = n; tn && !t; tn = tn->next) { + if ((t = ((to->llen == tn->llen && + !strncmp(to->line, tn->line, to->llen)) || + (!(to->flags & CLF_JOIN) && + join_strs(to->llen, to->line, + tn->llen, tn->line))))) + break; + } + if (t) + break; + } + if (t) { + /* Found matching anchors, continue with them. */ + o->line = to->line; + o->llen = to->llen; + o->next = to->next; + o->flags |= CLF_MISS; + n = tn; + } else { + /* No matching anchors found, shorten the list. */ + o->flags |= CLF_MISS; + o->next = NULL; + o->llen = 0; + if (olp) + *olp = smol; + if (nlp) + *nlp = smnl; + return ret; + } } /* If we reached the end of the new list but not the end of the old - * list, we mark the old list (saying that characters are missing here). */ + * list, we mark the old list (saying that characters are missing + * here). */ if (!(n = n->next) && o->next) o->flags |= CLF_MISS; o = o->next; @@ -1859,20 +1979,19 @@ bld_line_pfx(Cpattern pat, char *line, char *lp, rl++; } else { t = 0; - for (ms = bmstack; ms && !t; ms = ms->next) { - for (mp = ms->matcher; mp && !t; mp = mp->next) { - if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && - pattern_match(mp->line, line, NULL, ea) && - pattern_match(mp->word, word, ea, NULL)) { - /* Both the line and the word pattern matched, - * now skip over the matched portions. */ - line += mp->llen; - word += mp->wlen; - l -= mp->llen; - wlen -= mp->wlen; - rl += mp->wlen; - t = 1; - } + for (ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && + pattern_match(mp->line, line, NULL, ea) && + pattern_match(mp->word, word, ea, NULL)) { + /* Both the line and the word pattern matched, + * now skip over the matched portions. */ + line += mp->llen; + word += mp->wlen; + l -= mp->llen; + wlen -= mp->wlen; + rl += mp->wlen; + t = 1; } } if (!t) @@ -1923,100 +2042,99 @@ bld_new_pfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp) while (ol && nl) { if (*ow != *nw) { /* Not the same character, use the patterns. */ - for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { - for (mp = ms->matcher; mp && !t; mp = mp->next) { - /* We use only those patterns that match a non-empty - * string in both the line and the word, that match - * strings no longer than the string we still have - * to compare, that don't have anchors, and that don't - * use the line strings for insertion. */ - if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && - mp->wlen <= ol && mp->wlen <= nl) { - /* Try the pattern only if the word-pattern matches - * one of the strings. */ - if (pattern_match(mp->word, ow, NULL, ea)) { - /* Make the buffer where we build the possible - * line patterns big enough. */ - if (mp->llen + 1 > llen) { - if (llen) - zfree(line, llen); - line = (char *) zalloc(llen = mp->llen + 20); - } - /* Then build all the possible lines and see - * if one of them matches the othe string. */ - if ((bl = bld_line_pfx(mp->line, line, line, - nl, nw, ea))) { - /* Yep, one of the lines matched the other - * string. */ - if (p != ow) { - /* There is still a substring that is - * the same in both strings, so add - * a cline for it. */ - char sav = *ow; - - *ow = '\0'; - n = getcline(NULL, 0, dupstring(p), - ow - p, 0); - *ow = sav; - if (l) - l->next = n; - else - ret = n; - l = n; - } - /* Then we add the line string built. */ - line[mp->llen] = '\0'; - n = getcline(dupstring(line), mp->llen, - NULL, 0, CLF_DIFF); + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + /* We use only those patterns that match a non-empty + * string in both the line and the word, that match + * strings no longer than the string we still have + * to compare, that don't have anchors, and that don't + * use the line strings for insertion. */ + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= ol && mp->wlen <= nl) { + /* Try the pattern only if the word-pattern matches + * one of the strings. */ + if (pattern_match(mp->word, ow, NULL, ea)) { + /* Make the buffer where we build the possible + * line patterns big enough. */ + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + /* Then build all the possible lines and see + * if one of them matches the othe string. */ + if ((bl = bld_line_pfx(mp->line, line, line, + nl, nw, ea))) { + /* Yep, one of the lines matched the other + * string. */ + if (p != ow) { + /* There is still a substring that is + * the same in both strings, so add + * a cline for it. */ + char sav = *ow; + + *ow = '\0'; + n = getcline(NULL, 0, dupstring(p), + ow - p, 0); + *ow = sav; if (l) l->next = n; else ret = n; l = n; - ow += mp->wlen; - nw += bl; - ol -= mp->wlen; - nl -= bl; - p = ow; - t = 1; - } - } else if (pattern_match(mp->word, nw, NULL, ea)) { - /* Now do the same for the other string. */ - if (mp->llen + 1 > llen) { - if (llen) - zfree(line, llen); - line = (char *) zalloc(llen = mp->llen + 20); } - if ((bl = bld_line_pfx(mp->line, line, line, - ol, ow, ea))) { - if (p != ow) { - char sav = *ow; - - *ow = '\0'; - n = getcline(NULL, 0, dupstring(p), - ow - p, 0); - *ow = sav; - if (l) - l->next = n; - else - ret = n; - l = n; - } - line[mp->llen] = '\0'; - n = getcline(dupstring(line), mp->llen, - NULL, 0, CLF_DIFF); + /* Then we add the line string built. */ + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow += mp->wlen; + nw += bl; + ol -= mp->wlen; + nl -= bl; + p = ow; + t = 1; + } + } else if (pattern_match(mp->word, nw, NULL, ea)) { + /* Now do the same for the other string. */ + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + ol, ow, ea))) { + if (p != ow) { + char sav = *ow; + + *ow = '\0'; + n = getcline(NULL, 0, dupstring(p), + ow - p, 0); + *ow = sav; if (l) l->next = n; else ret = n; l = n; - ow += bl; - nw += mp->wlen; - ol -= bl; - nl -= mp->wlen; - p = ow; - t = 1; } + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow += bl; + nw += mp->wlen; + ol -= bl; + nl -= mp->wlen; + p = ow; + t = 1; } } } @@ -2107,19 +2225,18 @@ join_new_pfx(Cline line, int len, char *word, int *missp) ll--; len--; } else { - for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { - for (mp = ms->matcher; mp && !t; mp = mp->next) { - if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && - mp->wlen <= len && mp->llen <= len && - pattern_match(mp->word, word, NULL, ea) && - pattern_match(mp->line, p, ea, NULL)) { - /* We have a matched substring, skip over. */ - p += mp->llen; - word += mp->wlen; - ll -= mp->llen; - len -= mp->wlen; - t = 1; - } + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= len && mp->llen <= len && + pattern_match(mp->word, word, NULL, ea) && + pattern_match(mp->line, p, ea, NULL)) { + /* We have a matched substring, skip over. */ + p += mp->llen; + word += mp->wlen; + ll -= mp->llen; + len -= mp->wlen; + t = 1; } } if (!t) @@ -2148,6 +2265,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = line; else ret = line; + l = line; } } else { /* The cline doesn't have a string built by reverse matching, @@ -2163,6 +2281,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = line; else ret = line; + l = line; len = 0; } else { char sav = word[len]; @@ -2180,6 +2299,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = line; else ret = line; + l = line; len = 0; } else if (strpfx(line->word, word)) { word[len] = sav; @@ -2190,6 +2310,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = line; else ret = line; + l = line; len = 0; } else { /* Not the same and no prefix, so we try to build a @@ -2208,6 +2329,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp) l->next = sl; else ret = sl; + l = sl; if (!mol) { send->next = next; word += len - mnl; @@ -2238,7 +2360,11 @@ join_new_pfx(Cline line, int len, char *word, int *missp) static void bld_pfx(Cline o, Cline n, int *olp, int *nlp) { - if (o->flags & CLF_NEW) { + if (olp) + *olp = 0; + if (nlp) + *nlp = 0; + if (o->flags & CLF_PNEW) { if (o->flags & (CLF_END | CLF_MID)) /* We split the suffix in the middle and at the end into * separate runs. */ @@ -2255,7 +2381,7 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp) o->flags |= CLF_MISS; } } else if (o->flags & (CLF_END | CLF_MID)) { - o->flags |= CLF_NEW; + o->flags |= CLF_PNEW; o->prefix = join_ends(end_list(o->wlen, o->word), end_list(n->wlen, n->word), olp, nlp); } else if (o->wlen && n->wlen) { @@ -2280,7 +2406,7 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp) * matches both strings from the original cline structs * and thus can be put in the command line to represent * them. This cline list is stored in o. */ - o->flags |= CLF_NEW; + o->flags |= CLF_PNEW; o->prefix = bld_new_pfx(o->wlen, o->word, n->wlen, n->word, &mol, &mnl, NULL); newl = 0; @@ -2300,7 +2426,8 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp) if (!o->prefix && n->wlen != o->wlen) o->flags |= CLF_MISS; - } + } else + o->wlen = 0; } /* The following function are like their counterparts above, only for @@ -2353,20 +2480,19 @@ bld_line_sfx(Cpattern pat, char *line, char *lp, rl++; } else { t = 0; - for (ms = bmstack; ms && !t; ms = ms->next) { - for (mp = ms->matcher; mp && !t; mp = mp->next) { - if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && - pattern_match(mp->line, line - mp->llen, - NULL, ea) && - pattern_match(mp->word, word - mp->wlen, - ea, NULL)) { - line -= mp->llen; - word -= mp->wlen; - l -= mp->llen; - wlen -= mp->wlen; - rl += mp->wlen; - t = 1; - } + for (ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && + pattern_match(mp->line, line - mp->llen, + NULL, ea) && + pattern_match(mp->word, word - mp->wlen, + ea, NULL)) { + line -= mp->llen; + word -= mp->wlen; + l -= mp->llen; + wlen -= mp->wlen; + rl += mp->wlen; + t = 1; } } if (!t) @@ -2404,84 +2530,83 @@ bld_new_sfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp) } while (ol && nl) { if (ow[-1] != nw[-1]) { - for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { - for (mp = ms->matcher; mp && !t; mp = mp->next) { - if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && - mp->wlen <= ol && mp->wlen <= nl) { - if (pattern_match(mp->word, ow - mp->wlen, - NULL, ea)) { - if (mp->llen + 1 > llen) { - if (llen) - zfree(line, llen); - line = (char *) zalloc(llen = mp->llen + 20); - } - if ((bl = bld_line_sfx(mp->line, line, line, - nl, nw, ea))) { - if (p != ow) { - char sav = *p; - - *p = '\0'; - n = getcline(NULL, 0, dupstring(ow), - p - ow, 0); - *p = sav; - if (l) - l->next = n; - else - ret = n; - l = n; - } - line[mp->llen] = '\0'; - n = getcline(dupstring(line), mp->llen, - NULL, 0, CLF_DIFF); + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= ol && mp->wlen <= nl) { + if (pattern_match(mp->word, ow - mp->wlen, + NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_sfx(mp->line, line, line, + nl, nw, ea))) { + if (p != ow) { + char sav = *p; + + *p = '\0'; + n = getcline(NULL, 0, dupstring(ow), + p - ow, 0); + *p = sav; if (l) l->next = n; else ret = n; l = n; - ow -= mp->wlen; - nw -= bl; - ol -= mp->wlen; - nl -= bl; - p = ow; - t = 1; } - } else if (pattern_match(mp->word, nw - mp->wlen, - NULL, ea)) { - if (mp->llen + 1 > llen) { - if (llen) - zfree(line, llen); - line = (char *) zalloc(llen = mp->llen + 20); - } - if ((bl = bld_line_sfx(mp->line, line, line, - ol, ow, ea))) { - if (p != ow) { - char sav = *p; - - *p = '\0'; - n = getcline(NULL, 0, dupstring(ow), - p - ow, 0); - *p = sav; - if (l) - l->next = n; - else - ret = n; - l = n; - } - line[mp->llen] = '\0'; - n = getcline(dupstring(line), mp->llen, - NULL, 0, CLF_DIFF); + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow -= mp->wlen; + nw -= bl; + ol -= mp->wlen; + nl -= bl; + p = ow; + t = 1; + } + } else if (pattern_match(mp->word, nw - mp->wlen, + NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_sfx(mp->line, line, line, + ol, ow, ea))) { + if (p != ow) { + char sav = *p; + + *p = '\0'; + n = getcline(NULL, 0, dupstring(ow), + p - ow, 0); + *p = sav; if (l) l->next = n; else ret = n; l = n; - ow -= bl; - nw -= mp->wlen; - ol -= bl; - nl -= mp->wlen; - p = ow; - t = 1; } + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow -= bl; + nw -= mp->wlen; + ol -= bl; + nl -= mp->wlen; + p = ow; + t = 1; } } } @@ -2554,21 +2679,20 @@ join_new_sfx(Cline line, int len, char *word, int *missp) len--; ind++; } else { - for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { - for (mp = ms->matcher; mp && !t; mp = mp->next) { - if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && - mp->wlen <= len && mp->llen <= len && - pattern_match(mp->word, word - mp->wlen, - NULL, ea) && - pattern_match(mp->line, p - mp->llen, - ea, NULL)) { - p -= mp->llen; - word -= mp->wlen; - ll -= mp->llen; - len -= mp->wlen; - ind += mp->wlen; - t = 1; - } + for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { + mp = ms->matcher; + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= len && mp->llen <= len && + pattern_match(mp->word, word - mp->wlen, + NULL, ea) && + pattern_match(mp->line, p - mp->llen, + ea, NULL)) { + p -= mp->llen; + word -= mp->wlen; + ll -= mp->llen; + len -= mp->wlen; + ind += mp->wlen; + t = 1; } } if (!t) @@ -2663,7 +2787,7 @@ join_new_sfx(Cline line, int len, char *word, int *missp) static void bld_sfx(Cline o, Cline n) { - if (o->flags & CLF_NEW) { + if (o->flags & CLF_SNEW) { int miss; o->suffix = join_new_sfx(o->suffix, n->wlen, n->word, &miss); @@ -2679,7 +2803,7 @@ bld_sfx(Cline o, Cline n) new = dupstring(n->word); newl = n->wlen; } else if (!strpfx(o->word, n->word)) { - o->flags |= CLF_NEW; + o->flags |= CLF_SNEW; o->suffix = bld_new_sfx(o->wlen, o->word, n->wlen, n->word, &mol, &mnl, NULL); newl = 0; @@ -2732,7 +2856,7 @@ join_clines(Cline o, Cline n) n = q; } } - if (n->flags & CLF_MID) { + if (n && n->flags & CLF_MID) { while (o && !(o->flags & CLF_MID)) { o->word = NULL; o->flags |= CLF_DIFF; @@ -3128,6 +3252,7 @@ inst_cline(Cline l, int pl, int sl) } l = l->next; } + lastend = cs; /* Now place the cursor. Preferably in a position where something * is missing, otherwise in a place where the string differs from * any of the matches, or just leave it at the end. */ @@ -3281,6 +3406,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) * to continue after the matched portion. */ w = q; iw = j; + lw = k; l = p; il = jj; ll = kk; @@ -3501,6 +3627,7 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) } w = q + 1; iw = j; + lw = k; l = p + 1; il = jj; ll = kk; @@ -3765,6 +3892,7 @@ instmatch(Cmatch m) inststrlen(m->suf, 1, (l = strlen(m->suf))); r += l; } + lastend = cs; cs = ocs; return r; } @@ -3777,10 +3905,10 @@ instmatch(Cmatch m) void addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, char *suf, char *group, char *rems, char *remf, char *ign, - int flags, int aflags, Cmatcher match, char **argv) + int flags, int aflags, Cmatcher match, char *exp, char **argv) { - char *s, *t, *e, *me, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL; - int lpl, lsl, i, pl, sl, test, bpl, bsl, lsm, llpl; + char *s, *t, *e, *me, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL; + int lpl, lsl, i, pl, sl, test, bpl, bsl, llpl, llsl; Aminfo ai; Cline lc = NULL; LinkList l; @@ -3788,43 +3916,39 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, struct cmlist mst; Cmlist oms = mstack; - /* Select the set of matches. */ - if (aflags & CAF_ALT) { - l = fmatches; - ai = fainfo; - } else { - l = matches; - ai = ainfo; - } - /* Store the matcher in our stack of matchers. */ - if (match) { - mst.next = mstack; - mst.matcher = match; - mstack = &mst; - if (had_lm && mnum) - bmstack1.matcher = NULL; - else { - bmstack1.matcher = match; - had_lm = 1; - } - addlinknode(matchers, match); - match->refc++; - } else { - bmstack1.matcher = NULL; - if (mnum) - had_lm = 1; - } /* Use menu-completion (-U)? */ if ((aflags & CAF_MENU) && isset(AUTOMENU)) usemenu = 1; - /* Get the suffixes to ignore. */ - if (ign) - aign = get_user_var(ign); /* Switch back to the heap that was used when the completion widget * was invoked. */ SWITCHHEAPS(compheap) { HEAPALLOC { + if (exp) { + expl = (Cexpl) halloc(sizeof(struct cexpl)); + expl->count = expl->fcount = 0; + expl->str = dupstring(exp); + } else + expl = NULL; + + /* Store the matcher in our stack of matchers. */ + if (match) { + mst.next = mstack; + mst.matcher = match; + mstack = &mst; + + if (!mnum) + add_bmatchers(match); + + addlinknode(matchers, match); + match->refc++; + } + if (mnum && (mstack || bmatchers)) + update_bmatchers(); + + /* Get the suffixes to ignore. */ + if (ign) + aign = get_user_var(ign); /* Get the contents of the completion variables if we have * to perform matching. */ if (aflags & CAF_MATCH) { @@ -3832,6 +3956,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, lpre = dupstring(compprefix); llpl = strlen(lpre); lsuf = dupstring(compsuffix); + llsl = strlen(lsuf); } /* Now duplicate the strings we have from the command line. */ if (ipre) @@ -3848,8 +3973,6 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, lsl = strlen(psuf); } else lsl = 0; - if (aflags & CAF_MATCH) - lsm = (psuf ? !strcmp(psuf, lsuf) : (!lsuf || !*lsuf)); if (pre) pre = dupstring(pre); if (suf) @@ -3865,6 +3988,17 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, begcmgroup(group, (aflags & CAF_NOSORT)); if (aflags & CAF_NOSORT) mgroup->flags |= CGF_NOSORT; + } else { + endcmgroup(NULL); + begcmgroup("default", 0); + } + /* Select the set of matches. */ + if (aflags & CAF_ALT) { + l = fmatches; + ai = fainfo; + } else { + l = matches; + ai = ainfo; } if (remf) { remf = dupstring(remf); @@ -3908,19 +4042,19 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } if (aflags & CAF_MATCH) { /* Do the matching. */ - t = (ppre ? dyncat(ppre, s) : s); - pl = sl + lpl; - if ((test = (llpl <= pl && !strncmp(t, lpre, pl)))) - test = lsm; + test = (sl >= llpl + llsl && + strpfx(lpre, s) && strsfx(lsuf, s)); if (!test && mstack && - (ms = comp_match(lpre, lsuf, - (psuf ? dyncat(t, psuf) : t), + (ms = comp_match(lpre, lsuf, s, &lc, (aflags & CAF_QUOTE), &bpl, &bsl))) test = 1; + if (!test) continue; - me = e = s + sl; + pl = sl - llsl; + me = s + sl - llsl; + e = s + llpl; } else { e = s; me = s + sl; @@ -3928,8 +4062,10 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } /* Quoting? */ if (!(aflags & CAF_QUOTE)) { - te = s + pl; - s = quotename(s, &e, te, &pl); + int tmp = me - s; + + s = quotename(s, &e, me, &tmp); + me = s + tmp; sl = strlen(s); } /* The rest is almost the same as in addmatch(). */ @@ -3944,31 +4080,48 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, if (ppre) t = dyncat(ppre, t); if (!ms && mstack) { + int bl = ((aflags & CAF_MATCH) ? llpl : 0); Cline *clp = &lc, tlc; char *ss = dupstring(s), *ee = me + (ss - s); if (ppre && *ppre) { - *clp = str_cline(ppre, strlen(ppre), &tlc); + *clp = tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR); clp = &(tlc->next); } - if (ee != ss + sl || (lpsuf && *lpsuf)) { - *clp = tlc = getcline(ss, ee - ss, + if (bl) { + *clp = str_cline(ss, bl, &tlc); + clp = &(tlc->next); + } + if (ee != ss + sl) { + *clp = tlc = getcline(ss + bl, ee - ss - bl, NULL, 0, CLF_MID); clp = &(tlc->next); - if (ee != ss + sl) { - *clp = str_cline(ee, (ss + sl) - ee, &tlc); - clp = &(tlc->next); - } - if (lpsuf && *lpsuf) { - *clp = str_cline(lpsuf, strlen(lpsuf), &tlc); - clp = &(tlc->next); - } + *clp = str_cline(ee, (ss + sl) - ee, &tlc); + clp = &(tlc->next); } else { - *clp = tlc = getcline(NULL, 0, ss, sl, + *clp = tlc = getcline(NULL, 0, ss + bl, sl - bl, + CLF_END | CLF_VAR); + clp = &(tlc->next); + } + if (psuf && *psuf) { + *clp = tlc = getcline(NULL, 0, psuf, lsl, CLF_END | CLF_VAR); clp = &(tlc->next); } *clp = NULL; + } else if (mstack) { + Cline tlc; + + if (ppre && *ppre) { + tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR); + tlc->next = lc; + lc = tlc; + } + if (psuf && *psuf) { + for (tlc = lc; tlc->next; tlc = tlc->next); + tlc->next = getcline(NULL, 0, psuf, lsl, + CLF_END | CLF_VAR); + } } if (ipre && *ipre) { Cline tlc = prepend_cline(ipre, lc); @@ -4047,6 +4200,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } } compnmatches = mnum; + if (exp) + addexpl(); } LASTALLOC; } SWITCHBACKHEAPS; @@ -4129,8 +4284,10 @@ addmatch(char *s, char *t) mpl = fpl; msl = fsl; } else { if ((cp = filecomp)) { - if ((test = domatch(s, filecomp, 0))) + if ((test = domatch(s, filecomp, 0))) { + e = s + sl; cc = 1; + } } else { e = s + sl - fsl; if ((test = !strncmp(s, fpre, fpl))) @@ -4168,6 +4325,7 @@ addmatch(char *s, char *t) strcat(tt, s); if (lpsuf) strcat(tt, lpsuf); + e += (tt - s); untokenize(s = tt); sl = strlen(s); } @@ -4196,9 +4354,10 @@ addmatch(char *s, char *t) ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) || ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) { if (sl >= rpl + rsl || mstack) { - if (cp) + if (cp) { test = domatch(s, patcomp, 0); - else { + e = s + sl; + } else { e = s + sl - rsl; if ((test = !strncmp(s, rpre, rpl))) if ((test = !strcmp(e, rsuf))) { @@ -4888,9 +5047,11 @@ makecomplist(char *s, int incmd, int lst) { struct cmlist ms; Cmlist m; + char *os = s; - /* If we already have a list from a previous execution of this * - * function, skip the list building code. */ + /* We build a copy of the list of matchers to use to make sure that this + * works even if a shell function called from the completion code changes + * the global matchers. */ if ((m = cmatcher)) { Cmlist mm, *mp = &mm; @@ -4906,16 +5067,19 @@ makecomplist(char *s, int incmd, int lst) m = mm; } compmatcher = 1; - bmstack = &bmstack1; - bmstack1.next = &bmstack0; - bmstack0.next = NULL; - bmstack0.matcher = bmstack1.matcher = NULL; + + /* Walk through the global matchers. */ for (;;) { + bmatchers = NULL; if (m) { ms.next = NULL; ms.matcher = m->matcher; mstack = &ms; - bmstack0.matcher = m->matcher; + + /* Store the matchers used in the bmatchers list which is used + * when building new parts for the string to insert into the + * line. */ + add_bmatchers(m->matcher); } else mstack = NULL; @@ -4932,12 +5096,12 @@ makecomplist(char *s, int incmd, int lst) lastambig = 0; amatches = 0; mnum = 0; - had_lm = 0; begcmgroup("default", 0); ccused = newlinklist(); ccstack = newlinklist(); + s = dupstring(os); if (compfunc) callcompfunc(s, compfunc); else @@ -5004,7 +5168,7 @@ ctokenize(char *p) /**/ char * -comp_str(int *ipl, int *pl) +comp_str(int *ipl, int *pl, int untok) { char *p = dupstring(compprefix); char *s = dupstring(compsuffix); @@ -5012,12 +5176,14 @@ comp_str(int *ipl, int *pl) char *str; int lp, ls, lip; - ctokenize(p); - remnulargs(p); - ctokenize(s); - remnulargs(s); - ctokenize(ip); - remnulargs(ip); + if (!untok) { + ctokenize(p); + remnulargs(p); + ctokenize(s); + remnulargs(s); + ctokenize(ip); + remnulargs(ip); + } ls = strlen(s); lip = strlen(ip); lp = strlen(p); @@ -5041,7 +5207,7 @@ makecomplistcall(Compctl cc) SWITCHHEAPS(compheap) { HEAPALLOC { int ooffs = offs, lip, lp; - char *str = comp_str(&lip, &lp); + char *str = comp_str(&lip, &lp, 0); offs = lip + lp; cc->refc++; @@ -5073,7 +5239,7 @@ makecomplistctl(int flags) SWITCHHEAPS(compheap) { HEAPALLOC { int ooffs = offs, lip, lp; - char *str = comp_str(&lip, &lp), *t; + char *str = comp_str(&lip, &lp, 0), *t; char *os = cmdstr, **ow = clwords, **p, **q; int on = clwnum, op = clwpos; @@ -5571,19 +5737,16 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) ms.next = mstack; ms.matcher = cc->matcher; mstack = &ms; - if (had_lm && mnum) - bmstack1.matcher = NULL; - else { - bmstack1.matcher = cc->matcher; - had_lm = 1; - } + + if (!mnum) + add_bmatchers(cc->matcher); + addlinknode(matchers, cc->matcher); cc->matcher->refc++; - } else { - bmstack1.matcher = NULL; - if (mnum) - had_lm = 1; } + if (mnum && (mstack || bmatchers)) + update_bmatchers(); + /* Insert the prefix (compctl -P), if any. */ if (cc->prefix) { int pl = 0; @@ -6344,7 +6507,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) } if ((tt = cc->explain)) { - if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) { + tt = dupstring(tt); + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { singsub(&tt); untokenize(tt); } @@ -6358,7 +6522,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) begcmgroup("default", 0); } else if ((tt = cc->explain)) { - if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) { + tt = dupstring(tt); + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { singsub(&tt); untokenize(tt); } @@ -6631,7 +6796,7 @@ begcmgroup(char *n, int nu) } } mgroup = (Cmgroup) halloc(sizeof(struct cmgroup)); - mgroup->name = n; + mgroup->name = dupstring(n); mgroup->flags = mgroup->lcount = mgroup->mcount = 0; mgroup->matches = NULL; mgroup->ylist = NULL; @@ -6872,7 +7037,7 @@ do_ambiguous(void) /* If we have to insert the first match, call do_single(). This is * * how REC_EXACT takes effect. We effectively turn the ambiguous * * completion into an unambiguous one. */ - if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !fromcomp && + if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !(fromcomp & FC_LINE) && (usemenu == 0 || unset(AUTOMENU))) { do_single(ainfo->exactm); invalidatelist(); @@ -6945,7 +7110,6 @@ do_ambiguous(void) merge_cline(lc, ps, lp, NULL, 0, 0); } inst_cline(lc, pl, sl); - } else { inststrlen(ps, 1, -1); ocs = cs; @@ -6966,6 +7130,7 @@ do_ambiguous(void) cs -= brsl; inststrlen(brend, 1, -1); } + lastend = cs; cs = ocs; } /* la is non-zero if listambiguous may be used. Copying and @@ -6973,11 +7138,13 @@ do_ambiguous(void) * solution. Really. */ la = (ll != oll || strncmp(oline, (char *) line, ll)); - /* If REC_EXACT and AUTO_MENU are set and what we inserted is an * - * exact match, we want menu completion the next time round * - * so we set fromcomp,to ensure that the word on the line is not * - * taken as an exact match. */ - fromcomp = isset(AUTOMENU); + /* If REC_EXACT and AUTO_MENU are set and what we inserted is an * + * exact match, we want menu completion the next time round * + * so we set fromcomp,to ensure that the word on the line is not * + * taken as an exact match. Also we remember if we just moved the * + * cursor into the word. */ + fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) | + ((atend && cs != lastend) ? FC_INWORD : 0)); /* * If the LIST_AMBIGUOUS option (meaning roughly `show a list only * diff --git a/Src/exec.c b/Src/exec.c index 59061af3f..764b7140c 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2558,7 +2558,7 @@ execarith(Cmd cmd) val = matheval(e); } if (isset(XTRACE)) { - fprintf(stderr, " ))\n", stderr); + fprintf(stderr, " ))\n"); fflush(stderr); } errflag = 0; diff --git a/Src/glob.c b/Src/glob.c index 516f75618..47fa63567 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -71,6 +71,7 @@ struct gmatch { int badcshglob; static int mode; /* != 0 if we are parsing glob patterns */ +static int scanning; /* != 0 if we are scanning file paths */ static int pathpos; /* position in pathbuf */ static int matchsz; /* size of matchbuf */ static int matchct; /* number of matches found */ @@ -134,7 +135,7 @@ struct complist { struct comp { Comp left, right, next, exclude; char *str; - int stat; + int stat, errsmax; }; /* Type of Comp: a closure with one or two #'s, the end of a * @@ -162,6 +163,18 @@ struct comp { #define GF_PATHADD 1 /* file glob, adding path components */ #define GF_TOPLEV 2 /* outside (), so ~ ends main match */ +/* Next character after one which may be a Meta (x is any char *) */ +#define METANEXT(x) (*(x) == Meta ? (x)+2 : (x)+1) +/* + * Increment pointer which may be on a Meta (x is a pointer variable), + * returning the incremented value (i.e. like pre-increment). + */ +#define METAINC(x) ((x) += (*(x) == Meta) ? 2 : 1) +/* + * Return unmetafied char from string (x is any char *) + */ +#define UNMETA(x) (*(x) == Meta ? (x)[1] ^ 32 : *(x)) + static char *pptr; /* current place in string being matched */ static Comp tail; static int first; /* are leading dots special? */ @@ -352,6 +365,25 @@ haswilds(char *str) return 0; } +/* Flags to apply to current level of grouping */ + +static int addflags; + +/* + * Approximate matching. + * + * errsmax is used while parsing to store the current number + * of errors allowed. While executing it is usually set to -1, + * but can be set to something else to fix a different maximum + * no. of errors which acts as a limit on the local value: see + * scanner() for why this is necessary. + * + * errsfound is used while executing a pattern to record the + * number of errors found so far. + */ + +static int errsmax, errsfound; + /* Do the globbing: scanner is called recursively * * with successive bits of the path until we've * * tried all of it. */ @@ -363,6 +395,7 @@ scanner(Complist q) Comp c; int closure; int pbcwdsav = pathbufcwd; + int errssofar = errsfound; struct dirsav ds; ds.ino = ds.dev = 0; @@ -381,7 +414,7 @@ scanner(Complist q) c = q->comp; /* Now the actual matching for the current path section. */ if (!(c->next || c->left) && !haswilds(c->str) - && (!(c->stat & (C_LCMATCHUC|C_IGNCASE)) + && (!((c->stat & (C_LCMATCHUC|C_IGNCASE)) || c->errsmax) || !strcmp(".", c->str) || !strcmp("..", c->str))) { /* * We always need to match . and .. explicitly, even if we're @@ -433,6 +466,7 @@ scanner(Complist q) ((glob_pre && !strpfx(glob_pre, fn)) || (glob_suf && !strsfx(glob_suf, fn)))) continue; + errsfound = errssofar; if (domatch(fn, c, gf_noglobdots)) { /* if this name matchs the pattern... */ if (pbcwdsav == pathbufcwd && @@ -453,7 +487,34 @@ scanner(Complist q) if (dirs) { int l; - /* if not the last component in the path */ + /* + * If not the last component in the path: + * + * If we made an approximation in the new path segment, + * and the pattern includes closures other than a + * trailing * (we only test if it is not a string), + * then it is possible we made too many errors. For + * example, (ab)#(cb)# will match the directory abcb + * with one error if allowed to, even though it can + * match with none. This will stop later parts of the + * path matching, so we need to check by reducing the + * maximum number of errors and seeing if the directory + * still matches. Luckily, this is not a terribly + * common case, since complex patterns typically occur + * in the last part of the path which is not affected + * by this problem. + */ + if (errsfound > errssofar && (c->next || c->left)) { + errsmax = errsfound - 1; + while (errsmax >= errssofar) { + errsfound = errssofar; + if (!domatch(fn, c, gf_noglobdots)) + break; + errsmax = errsfound - 1; + } + errsfound = errsmax + 1; + errsmax = -1; + } if (closure) { /* if matching multiple directories */ struct stat buf; @@ -470,9 +531,14 @@ scanner(Complist q) continue; } l = strlen(fn) + 1; - subdirs = hrealloc(subdirs, subdirlen, subdirlen + l); + subdirs = hrealloc(subdirs, subdirlen, subdirlen + l + + sizeof(int)); strcpy(subdirs + subdirlen, fn); subdirlen += l; + /* store the count of errors made so far, too */ + memcpy(subdirs + subdirlen, (char *)&errsfound, + sizeof(int)); + subdirlen += sizeof(int); } else /* if the last filename component, just add it */ insert(fn, 1); @@ -482,8 +548,11 @@ scanner(Complist q) if (subdirs) { int oppos = pathpos; - for (fn = subdirs; fn < subdirs+subdirlen; fn += strlen(fn) + 1) { + for (fn = subdirs; fn < subdirs+subdirlen; ) { addpath(fn); + fn += strlen(fn) + 1; + memcpy((char *)&errsfound, fn, sizeof(int)); + fn += sizeof(int); scanner((q->closure) ? q : q->next); /* scan next level */ pathbuf[pathpos = oppos] = '\0'; } @@ -502,16 +571,13 @@ scanner(Complist q) /* Parse a series of path components pointed to by pptr */ -/* Flags to apply to current level of grourping */ - -static int addflags; - /**/ static Comp compalloc(void) { Comp c = (Comp) alloc(sizeof *c); c->stat |= addflags; + c->errsmax = errsmax; return c; } @@ -519,10 +585,19 @@ compalloc(void) static int getglobflags() { + char *nptr; /* (#X): assumes we are still positioned on the initial '(' */ pptr++; while (*++pptr && *pptr != Outpar) { switch (*pptr) { + case 'a': + /* Approximate matching, max no. of errors follows */ + errsmax = zstrtol(++pptr, &nptr, 10); + if (pptr == nptr) + return 1; + pptr = nptr-1; + break; + case 'l': /* Lowercase in pattern matches lower or upper in target */ addflags |= C_LCMATCHUC; @@ -635,6 +710,7 @@ parsecomp(int gflag) if (eptr == cstr) { /* if no string yet, carry on and get one. */ c->stat = addflags; + c->errsmax = errsmax; cstr = pptr; continue; } @@ -817,6 +893,7 @@ parsecompsw(int gflag) { Comp c1, c2, c3, excl = NULL, stail = tail; int oaddflags = addflags; + int oerrsmax = errsmax; char *sptr; /* @@ -845,12 +922,14 @@ parsecompsw(int gflag) return NULL; if (isset(EXTENDEDGLOB) && *pptr == Tilde) { /* Matching remainder of pattern excludes the pattern from matching */ - int oldmode = mode; + int oldmode = mode, olderrsmax = errsmax; mode = 1; + errsmax = 0; pptr++; excl = parsecomp(gflag); mode = oldmode; + errsmax = olderrsmax; if (!excl) return NULL; } @@ -878,8 +957,10 @@ parsecompsw(int gflag) c2->stat |= C_PATHADD; c1 = c2; } - if (!(gflag & GF_TOPLEV)) + if (!(gflag & GF_TOPLEV)) { addflags = oaddflags; + errsmax = oerrsmax; + } return c1; } @@ -965,6 +1046,7 @@ parsepat(char *str) { mode = 0; /* path components present */ addflags = 0; + errsmax = 0; pptr = str; tail = NULL; /* @@ -1102,7 +1184,7 @@ static int gmatchcmp(Gmatch a, Gmatch b) { int i, *s; - long r; + long r = 0L; for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) { switch (*s & ~GS_DESC) { @@ -1594,10 +1676,14 @@ glob(LinkList list, LinkNode np) matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) * sizeof(struct gmatch)); matchct = 0; + errsfound = 0; + errsmax = -1; /* The actual processing takes place here: matches go into * * matchbuf. This is the only top-level call to scanner(). */ + scanning = 1; scanner(q); + scanning = 0; /* Deal with failures to match depending on options */ if (matchct) @@ -2427,6 +2513,14 @@ domatch(char *str, Comp c, int fist) int ret; pptr = str; first = fist; + /* + * If scanning paths, keep the error count over the whole + * filename + */ + if (!scanning) { + errsfound = 0; + errsmax = -1; + } if (*pptr == Nularg) pptr++; PERMALLOC { @@ -2444,15 +2538,17 @@ static int excluded(Comp c, char *eptr) { char *saves = pptr; - int savei = first, savel = longest, ret; + int savei = first, savel = longest, savee = errsfound, ret; first = 0; /* * Even if we've been told always to match the longest string, * i.e. not anchored to the end, we want to match the full string * we've already matched when we're trying to exclude it. + * Likewise, approximate matching here is treated separately. */ longest = 0; + errsfound = 0; if (PATHADDP(c) && pathpos) { VARARR(char, buf, pathpos + strlen(eptr) + 1); @@ -2470,13 +2566,18 @@ excluded(Comp c, char *eptr) pptr = saves; first = savei; longest = savel; + errsfound = savee; return ret; } +/* + * Structure for storing instances of a pattern in a closured. + */ struct gclose { - char *start; - char *end; + char *start; /* Start of this instance */ + char *end; /* End of this instance */ + int errsfound; /* Errors found up to start of instance */ }; typedef struct gclose *Gclose; @@ -2488,34 +2589,48 @@ typedef struct gclose *Gclose; * also not bother going any further, since the first time we got to * that position (when it was marked), we must already have failed on * and backtracked over any further closure matches beyond that point. + * If we are using approximation, the number in the string is incremented + * by the current error count; if we come back to that point with a + * lower error count, it is worth trying from that point again, but + * we must now mark that point in trystring with the new error. */ /**/ static void -addclosures(Comp c, LinkList closlist, int *pdone, char *trystring) +addclosures(Comp c, LinkList closlist, int *pdone, unsigned char *trystring) { Gclose gcnode; char *opptr = pptr; + unsigned char *tpos; while (*pptr) { if (STARP(c)) { - if (trystring[(pptr+1)-opptr]) - break; + if (*(tpos = trystring + ((pptr+1) - opptr))) { + if ((unsigned)(errsfound+1) >= *tpos) + break; + *tpos = (unsigned)(errsfound+1); + } gcnode = (Gclose)zalloc(sizeof(struct gclose)); gcnode->start = pptr; - gcnode->end = ++pptr; + gcnode->end = METAINC(pptr); + gcnode->errsfound = errsfound; } else { char *saves = pptr; + int savee = errsfound; if (OPTIONALP(c) && *pdone >= 1) return; if (!matchonce(c) || saves == pptr || - trystring[pptr-opptr]) { + (*(tpos = trystring + (pptr-opptr)) && + (unsigned)(errsfound+1) >= *tpos)) { pptr = saves; break; } + if (*tpos) + *tpos = (unsigned)(errsfound+1); gcnode = (Gclose)zalloc(sizeof(struct gclose)); gcnode->start = saves; gcnode->end = pptr; + gcnode->errsfound = savee; } pushnode(closlist, gcnode); (*pdone)++; @@ -2523,13 +2638,29 @@ addclosures(Comp c, LinkList closlist, int *pdone, char *trystring) } /* - * Match characters with case-insensitivity. - * Note CHARMATCH(x,y) != CHARMATCH(y,x) + * Match characters with case-insensitivity. Note charmatch(x,y) != + * charmatch(y,x): the first argument comes from the target string, the + * second from the pattern. This used to be a macro, and in principle + * could be turned back into one. */ -#define CHARMATCH(x, y) \ -(x == y || (((c->stat & C_IGNCASE) ? (tulower(x) == tulower(y)) : \ - (c->stat & C_LCMATCHUC) ? (islower(y) && tuupper(y) == x) : 0))) +/**/ +static int +charmatch(Comp c, char *x, char *y) +{ + /* + * This takes care of matching two characters which may be a + * two byte sequence, Meta followed by something else. + * Here we bypass tulower() and tuupper() for speed. + */ + int xi = (STOUC(UNMETA(x)) & 0xff), yi = (STOUC(UNMETA(y)) & 0xff); + return xi == yi || + (((c->stat & C_IGNCASE) ? + ((isupper(xi) ? tolower(xi) : xi) == + (isupper(yi) ? tolower(yi) : yi)) : + (c->stat & C_LCMATCHUC) ? + (islower(yi) && toupper(yi) == xi) : 0)); +} /* see if current string in pptr matches c */ @@ -2539,14 +2670,16 @@ doesmatch(Comp c) { if (CLOSUREP(c)) { int done, retflag = 0; - char *saves, *trystring, *opptr; + char *saves, *opptr; + unsigned char *trystring, *tpos; + int savee; LinkList closlist; Gclose gcnode; if (first && *pptr == '.') return 0; - if (!inclosure && !c->left) { + if (!inclosure && !c->left && !c->errsmax) { /* We are not inside another closure, and the current * pattern is a simple string. We handle this very common * case specially: otherwise, matches like *foo* are @@ -2561,26 +2694,30 @@ doesmatch(Comp c) !itok(looka)) { /* Another simple optimisation for a very common case: * we are processing a * and there is - * an ordinary character match next. We look ahead for - * that character, taking care of Meta bytes. + * an ordinary character match (which may not be a Metafied + * character, just to make it easier) next. We look ahead + * for that character, taking care of Meta bytes. */ while (*pptr) { for (; *pptr; pptr++) { if (*pptr == Meta) pptr++; - else if (CHARMATCH(STOUC(*pptr), STOUC(looka))) + else if (charmatch(c, pptr, &looka)) break; } if (!*(saves = pptr)) break; + savee = errsfound; if (doesmatch(c->next)) return 1; pptr = saves+1; + errsfound = savee; } } else { /* Standard track-forward code */ for (done = 0; ; done++) { saves = pptr; + savee = errsfound; if ((done || ONEHASHP(c) || OPTIONALP(c)) && ((!c->next && (!LASTP(c) || !*pptr || longest)) || (c->next && doesmatch(c->next)))) @@ -2588,6 +2725,7 @@ doesmatch(Comp c) if (done && OPTIONALP(c)) return 0; pptr = saves; + errsfound = savee; first = 0; if (STARP(c)) { if (!*pptr) @@ -2602,7 +2740,7 @@ doesmatch(Comp c) /* The full, gory backtracking code is now necessary. */ inclosure++; closlist = newlinklist(); - trystring = zcalloc(strlen(pptr)+1); + trystring = (unsigned char *)zcalloc(strlen(pptr)+1); opptr = pptr; /* Start by making a list where each match is as long @@ -2621,7 +2759,7 @@ doesmatch(Comp c) retflag = 1; break; } - trystring[saves-opptr] = 1; + trystring[saves-opptr] = (unsigned)(errsfound + 1); /* * If we failed, the first thing to try is whether we can * shorten the match using the last pattern in the closure. @@ -2633,14 +2771,18 @@ doesmatch(Comp c) char savec = *gcnode->end; *gcnode->end = '\0'; pptr = gcnode->start; + errsfound = gcnode->errsfound; if (matchonce(c) && pptr != gcnode->start - && !trystring[pptr-opptr]) { + && (!*(tpos = trystring + (pptr-opptr)) || + *tpos > (unsigned)(errsfound+1))) { + if (*tpos) + *tpos = (unsigned)(errsfound+1); *gcnode->end = savec; gcnode->end = pptr; /* Try again to construct a list based on * this new position */ - addclosures(c, closlist, &done, trystring+(pptr-opptr)); + addclosures(c, closlist, &done, tpos); continue; } *gcnode->end = savec; @@ -2650,6 +2792,7 @@ doesmatch(Comp c) */ if ((gcnode = (Gclose)getlinknode(closlist))) { pptr = gcnode->start; + errsfound = gcnode->errsfound; zfree(gcnode, sizeof(struct gclose)); done--; } else @@ -2723,8 +2866,7 @@ rangematch(char **patptr, int ch, int rchar) #define PAT(X) (pat[X] == Meta ? pat[(X)+1] ^ 32 : untok(pat[X])) #define PPAT(X) (pat[(X)-1] == Meta ? pat[X] ^ 32 : untok(pat[X])) - for (pat++; *pat != Outbrack && *pat; - *pat == Meta ? pat += 2 : pat++) { + for (pat++; *pat != Outbrack && *pat; METAINC(pat)) { if (*pat == Inbrack) { /* Inbrack can only occur inside a range if we found [:...:]. */ pat += 2; @@ -2748,6 +2890,22 @@ rangematch(char **patptr, int ch, int rchar) *patptr = pat; } +/* + * matchonce() is the core of the pattern matching, handling individual + * strings and instances of a pattern in a closure. + * + * Note on approximate matching: The rule is supposed to be + * (1) Take the longest possible match without approximation. + * (2) At any failure, make the single approximation that results + * in the longest match for the remaining part (which may + * include further approximations). + * (3) If we match the same distance, take the one with fewer + * approximations. + * If this is wrong, I haven't yet discovered a counterexample. Email + * lines are now open. + * pws 1999/02/23 + */ + /**/ static int matchonce(Comp c) @@ -2758,7 +2916,7 @@ matchonce(Comp c) if (!pat || !*pat) { /* No current pattern (c->str). */ char *saves; - int savei; + int savee, savei; if (errflag) return 0; @@ -2766,6 +2924,7 @@ matchonce(Comp c) * check for exclusion of pattern or alternatives. */ saves = pptr; savei = first; + savee = errsfound; /* Loop over alternatives with exclusions: (foo~bar|...). * * Exclusions apply to the pattern in c->left. */ if (c->left || c->right) { @@ -2812,6 +2971,7 @@ matchonce(Comp c) */ exclend--; pptr = saves; + errsfound = savee; } inclosure--; if (ret) @@ -2822,19 +2982,43 @@ matchonce(Comp c) if (c->right && (!ret || inclosure)) { /* If in a closure, we always want the longest match. */ char *newpptr = pptr; + int newerrsfound = errsfound; pptr = saves; first = savei; + errsfound = savee; ret2 = doesmatch(c->right); - if (ret && (!ret2 || pptr < newpptr)) + if (ret && (!ret2 || pptr < newpptr)) { pptr = newpptr; + errsfound = newerrsfound; + } + } + if (!ret && !ret2) { + pptr = saves; + first = savei; + errsfound = savee; + break; } - if (!ret && !ret2) - return 0; } if (CLOSUREP(c)) return 1; - if (!c->next) /* no more patterns left */ - return (!LASTP(c) || !*pptr || longest); + if (!c->next) { + /* + * No more patterns left, but we may still be in the middle + * of a match, in which case alles in Ordnung... + */ + if (!LASTP(c)) + return 1; + /* + * ...else we're at the last pattern, so this is our last + * ditch attempt at an approximate match: try to omit the + * last few characters. + */ + for (; *pptr && errsfound < c->errsmax && + (errsmax == -1 || errsfound < errsmax); + METAINC(pptr)) + errsfound++; + return !*pptr || longest; + } /* optimisation when next pattern is not a closure */ if (!CLOSUREP(c->next)) { c = c->next; @@ -2843,7 +3027,10 @@ matchonce(Comp c) } return doesmatch(c->next); } - /* Don't match leading dot if first is set */ + /* + * Don't match leading dot if first is set + * (don't even try for an approximate match) + */ if (first && *pptr == '.' && *pat != '.') return 0; if (*pat == Star) { /* final * is not expanded to ?#; returns success */ @@ -2852,39 +3039,47 @@ matchonce(Comp c) return 1; } first = 0; /* finished checking start of pattern */ - if (*pat == Quest && *pptr) { + if (*pat == Quest) { /* match exactly one character */ - if (*pptr == Meta) - pptr++; - pptr++; + if (!*pptr) + break; + METAINC(pptr); pat++; continue; } if (*pat == Inbrack) { /* Match groups of characters */ char ch; + char *saves, *savep; if (!*pptr) break; - ch = *pptr == Meta ? pptr[1] ^ 32 : *pptr; + saves = pptr; + savep = pat; + ch = UNMETA(pptr); if (pat[1] == Hat || pat[1] == '^' || pat[1] == '!') { /* group is negated */ *++pat = Hat; rangematch(&pat, ch, Hat); DPUTS(!*pat, "BUG: something is very wrong in doesmatch()"); - if (*pat != Outbrack) + if (*pat != Outbrack) { + pptr = saves; + pat = savep; break; + } pat++; - *pptr == Meta ? pptr += 2 : pptr++; + METAINC(pptr); continue; } else { /* pattern is not negated (affirmed? asserted?) */ rangematch(&pat, ch, Inbrack); DPUTS(!pat || !*pat, "BUG: something is very wrong in doesmatch()"); - if (*pat == Outbrack) + if (*pat == Outbrack) { + pptr = saves; + pat = savep; break; - for (*pptr == Meta ? pptr += 2 : pptr++; - *pat != Outbrack; pat++); + } + for (METAINC(pptr); *pat != Outbrack; pat++); pat++; continue; } @@ -2892,7 +3087,7 @@ matchonce(Comp c) if (*pat == Inang) { /* Numeric globbing. */ unsigned long t1, t2, t3; - char *ptr; + char *ptr, *saves = pptr, *savep = pat; if (!idigit(*pptr)) break; @@ -2933,19 +3128,146 @@ matchonce(Comp c) pptr--; t1 /= 10; } - if (t1 < t2 || (!not3 && t1 > t3)) + if (t1 < t2 || (!not3 && t1 > t3)) { + pptr = saves; + pat = savep; break; + } } continue; } - if (CHARMATCH(STOUC(*pptr), STOUC(*pat))) { - /* just plain old characters */ - pptr++; - pat++; + /* itok(Meta) is zero */ + DPUTS(itok(*pat), "BUG: matching tokenized character"); + if (charmatch(c, pptr, pat)) { + /* just plain old characters (or maybe unplain new characters) */ + METAINC(pptr); + METAINC(pat); continue; } + if (errsfound < c->errsmax && + (errsmax == -1 || errsfound < errsmax)) { + /* + * We tried to match literal characters and failed. Now it's + * time to match approximately. Note this doesn't handle the + * case where we *didn't* try to match literal characters, + * including the case where we were already at the end of the + * pattern, because then we never get here. In that case the + * pattern has to be matched exactly, but we could maybe + * advance up the target string before trying it, so we have to + * handle that case elsewhere. + */ + char *saves = pptr, *savep = c->str; + char *maxpptr = pptr, *patnext = METANEXT(pat); + int savee, maxerrs = -1; + + /* First try to advance up the pattern. */ + c->str = patnext; + savee = ++errsfound; + if (matchonce(c)) { + maxpptr = pptr; + maxerrs = errsfound; + } + errsfound = savee; + + if (*saves) { + /* + * If we have characters on both strings, we have more + * choice. + * + * Try to edge up the target string. + */ + char *strnext = METANEXT(saves); + pptr = strnext; + c->str = pat; + if (matchonce(c) && + (maxerrs == -1 || pptr > maxpptr || + (pptr == maxpptr && errsfound <= maxerrs))) { + maxpptr = pptr; + maxerrs = errsfound; + } + errsfound = savee; + + /* + * Try edging up both of them at once. + * Note this takes precedence in the case of equal + * length as we get further up the pattern. + */ + c->str = patnext; + pptr = strnext; + if (matchonce(c) && + (maxerrs == -1 || pptr > maxpptr || + (pptr == maxpptr && errsfound <= maxerrs))) { + maxpptr = pptr; + maxerrs = errsfound; + } + errsfound = savee; + + /* + * See if we can transpose: that counts as just one error. + * + * Note, however, `abanana' and `banana': transposing + * is wrong here, as it gets us two errors, + * [ab][a]nana vs [ba][]nana, instead of the correct + * [a]banana vs []banana, so we still need to try + * the other possibilities. + */ + if (strnext && patnext && !itok(*patnext) && + charmatch(c, strnext, pat) && + charmatch(c, saves, patnext)) { + c->str = patnext; + METAINC(c->str); + + pptr = strnext; + METAINC(pptr); + + if (matchonce(c) && + (maxerrs == -1 || pptr > maxpptr || + (pptr == maxpptr && errsfound <= maxerrs))) { + maxpptr = pptr; + maxerrs = errsfound; + } + } + } + /* + * We don't usually restore state on failure, but we need + * to fix up the Comp structure which we altered to + * look at the tail of the pattern. + */ + c->str = savep; + /* + * If that failed, game over: we don't want to break + * and try the other approximate test, because we just did + * that. + */ + if (maxerrs == -1) + return 0; + pptr = maxpptr; + errsfound = maxerrs; + return 1; + } break; } + if (*pptr && errsfound < c->errsmax && + (errsmax == -1 || errsfound < errsmax)) { + /* + * The pattern failed, but we can try edging up the target string + * and rematching with an error. Note we do this from wherever we + * got to in the pattern string c->str, not the start. hence the + * need to modify c->str. + * + * At this point, we don't have a literal character in the pattern + * (handled above), so we don't try any funny business on the + * pattern itself. + */ + int ret; + char *savep = c->str; + errsfound++; + METAINC(pptr); + c->str = pat; + ret = matchonce(c); + c->str = savep; + return ret; + } return 0; } @@ -2958,6 +3280,7 @@ parsereg(char *str) remnulargs(str); mode = 1; /* no path components */ addflags = 0; + errsmax = 0; pptr = str; tail = NULL; return parsecompsw(GF_TOPLEV); diff --git a/Src/hashtable.c b/Src/hashtable.c index b816f1892..72e4db21b 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -607,6 +607,9 @@ hashdir(char **dirp) Cmdnam cn; DIR *dir; char *fn; +#ifdef _WIN32 + char *exe; +#endif if (isrelative(*dirp) || !(dir = opendir(unmeta(*dirp)))) return; @@ -618,6 +621,23 @@ hashdir(char **dirp) cn->u.name = dirp; cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn); } +#ifdef _WIN32 + /* Hash foo.exe as foo, since when no real foo exists, foo.exe + will get executed by DOS automatically. This quiets + spurious corrections when CORRECT or CORRECT_ALL is set. */ + if ((exe = strrchr(fn, '.')) && + (exe[1] == 'E' || exe[1] == 'e') && + (exe[2] == 'X' || exe[2] == 'x') && + (exe[3] == 'E' || exe[3] == 'e') && exe[4] == 0) { + *exe = 0; + if (!cmdnamtab->getnode(cmdnamtab, fn)) { + cn = (Cmdnam) zcalloc(sizeof *cn); + cn->flags = 0; + cn->u.name = dirp; + cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn); + } + } +#endif /* _WIN32 */ } closedir(dir); } diff --git a/Src/module.c b/Src/module.c index f91650a7f..1117571a4 100644 --- a/Src/module.c +++ b/Src/module.c @@ -567,6 +567,37 @@ load_module(char const *name) return m; } +/* This ensures that the module with the name given as the second argument + * is loaded. + * The third argument should be non-zero if the function should complain + * about trying to load a module with a full path name in restricted mode. + * The last argument should be non-zero if this function should signal an + * error if the module is already loaded. + * The return value is the module of NULL if the module couldn't be loaded. */ + +/**/ +Module +require_module(char *nam, char *module, int res, int test) +{ + Module m = NULL; + LinkNode node; + + node = find_module(module); + if (node && (m = ((Module) getdata(node)))->handle && + !(m->flags & MOD_UNLOAD)) { + if (test) { + zwarnnam(nam, "module %s already loaded.", module, 0); + return NULL; + } + } else if (res && isset(RESTRICTED) && strchr(module, '/')) { + zwarnnam(nam, "%s: restricted", module, 0); + return NULL; + } else + return load_module(module); + + return m; +} + /**/ void add_dep(char *name, char *from) @@ -963,22 +994,10 @@ bin_zmodload_load(char *nam, char **args, char *ops) return 0; } else { /* load modules */ - for (; *args; args++) { - Module m; - - node = find_module(*args); - if (node && (m = ((Module) getdata(node)))->handle && - !(m->flags & MOD_UNLOAD)) { - if (!ops['i']) { - zwarnnam(nam, "module %s already loaded.", *args, 0); - ret = 1; - } - } else if (isset(RESTRICTED) && strchr(*args, '/')) { - zwarnnam(nam, "%s: restricted", *args, 0); + for (; *args; args++) + if (!require_module(nam, *args, 1, (!ops['i']))) ret = 1; - } else if (!load_module(*args)) - ret = 1; - } + return ret; } } diff --git a/Src/params.c b/Src/params.c index 77166209f..e8182815e 100644 --- a/Src/params.c +++ b/Src/params.c @@ -679,11 +679,13 @@ static char **garr; static long getarg(char **str, int *inv, Value v, int a2, long *w) { - int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i; + int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash; char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt; long r = 0; Comp c; + ishash = (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED); + /* first parse any subscription flags */ if (v->pm && (*s == '(' || *s == Inpar)) { int escapes = 0; @@ -771,12 +773,13 @@ getarg(char **str, int *inv, Value v, int a2, long *w) } else if (rev) { v->isarr |= SCANPM_WANTVALS; } - if (!down && v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) + if (!down && ishash) v->isarr &= ~SCANPM_MATCHMANY; *inv = ind; } - for (t=s, i=0; *t && ((*t != ']' && *t != Outbrack && *t != ',') || i); t++) + for (t=s, i=0; + *t && ((*t != ']' && *t != Outbrack && (ishash || *t != ',')) || i); t++) if (*t == '[' || *t == Inbrack) i++; else if (*t == ']' || *t == Outbrack) @@ -791,7 +794,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) singsub(&s); if (!rev) { - if (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) { + if (ishash) { HashTable ht = v->pm->gets.hfn(v->pm); if (!ht) { ht = newparamtable(17, v->pm->nam); @@ -867,7 +870,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) if ((c = parsereg(s))) { if (v->isarr) { - if (PM_TYPE(v->pm->flags) == PM_HASHED) { + if (ishash) { scancomp = c; if (ind) v->isarr |= SCANPM_MATCHKEY; diff --git a/Src/subst.c b/Src/subst.c index 66c363145..2e9b84718 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -999,11 +999,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) if (!(v = fetchvalue((subexp ? &ov : &s), (wantt ? -1 : ((unset(KSHARRAYS) || inbrace) ? 1 : -1)), - hkeys|hvals))) + hkeys|hvals)) || + (v->pm && (v->pm->flags & PM_UNSET))) vunset = 1; if (wantt) { - if (v) { + if (v && v->pm && !(v->pm->flags & PM_UNSET)) { int f = v->pm->flags; switch (PM_TYPE(f)) { diff --git a/Src/system.h b/Src/system.h index 292943dd9..650690b51 100644 --- a/Src/system.h +++ b/Src/system.h @@ -70,8 +70,15 @@ char *alloca _((size_t)); # endif #endif -#ifdef HAVE_LIBC_H /* NeXT */ -# include <libc.h> +/* + * libc.h in an optional package for Debian Linux is broken (it + * defines dup() as a synonym for dup2(), which has a different + * number of arguments), so just include it for next. + */ +#ifdef __NeXT__ +# ifdef HAVE_LIBC_H +# include <libc.h> +# endif #endif #ifdef HAVE_SYS_TYPES_H diff --git a/Src/zsh.export b/Src/zsh.export index bfad7aea2..d31e7902e 100644 --- a/Src/zsh.export +++ b/Src/zsh.export @@ -173,6 +173,7 @@ refreshptr remlpaths remnulargs removehashnode +require_module resetneeded restoredir reswdtab diff --git a/acconfig.h b/acconfig.h index 77110b457..506441635 100644 --- a/acconfig.h +++ b/acconfig.h @@ -196,10 +196,13 @@ /* Define to 1 if system has working FIFO's */ #undef HAVE_FIFOS -/* Define to 1 if struct rlimit use quad_t */ +/* Define to 1 if struct rlimit uses quad_t */ #undef RLIM_T_IS_QUAD_T -/* Define to 1 if rlimit use unsigned */ +/* Define to 1 if struct rlimit uses long long */ +#undef RLIM_T_IS_LONG_LONG + +/* Define to 1 if rlimit uses unsigned */ #undef RLIM_T_IS_UNSIGNED /* Define to the type used in struct rlimit */ diff --git a/configure.in b/configure.in index e41bf47d6..dd6bfee4b 100644 --- a/configure.in +++ b/configure.in @@ -673,7 +673,8 @@ dnl The backslash substitution is to persuade cygwin to cough up dnl slashes rather than doubled backslashes in the path. echo "#include <signal.h>" > nametmp.c sigfile_list="`$CPP nametmp.c | -sed -n -e 's/^#[ ].*\"\(.*\)\"/\1/p' -e 's/\\\\\\\\/\//g' | +sed -n 's/^#[ ].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | $AWK '{ if (\$1 ~ \"sig\") files[[\$1]] = \$1 } END { for (var in files) print var }'`" rm -f nametmp.c @@ -681,6 +682,7 @@ if test -z "$sigfile_list"; then dnl In case we don't get the stuff from the preprocesor, use the old dnl list of standard places. sigfile_list="/usr/include/bsd/sys/signal.h +/usr/include/signum.h /usr/include/asm/signum.h /usr/include/asm/signal.h /usr/include/linux/signal.h @@ -689,9 +691,13 @@ if test -z "$sigfile_list"; then fi for SIGNAL_H in $sigfile_list do - test -f $SIGNAL_H && \ - grep '#[ ]*define[ ][ ]*SIG[0-9A-Z]*[ ]*[0-9][0-9]*' $SIGNAL_H > /dev/null && \ - break + dnl Try to make sure it doesn't get confused by files that don't + dnl have real signal definitions in, but do #define SIG* by counting + dnl the number of signals. Maybe we could even check for e.g. SIGHUP? + nsigs=`test -f $SIGNAL_H && \ + grep '#[ ]*define[ ][ ]*SIG[0-9A-Z]*[ ]*[0-9][0-9]*' $SIGNAL_H | \ + wc -l | sed 's/[ ]//g'` + test "x$nsigs" != x && test "$nsigs" -ge 7 && break done zsh_cv_path_signal_h=$SIGNAL_H ]) @@ -729,20 +735,43 @@ dnl ------------------ dnl rlimit type checks dnl ------------------ DEFAULT_RLIM_T=long -AC_CACHE_CHECK(if rlim_t is quad_t, -zsh_cv_rlim_t_is_quad_t, +AC_CACHE_CHECK(if rlim_t is longer than a long, +zsh_cv_rlim_t_is_longer, [AC_TRY_RUN([ #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif #include <sys/resource.h> main(){struct rlimit r;exit(sizeof(r.rlim_cur) <= sizeof(long));}], -zsh_cv_rlim_t_is_quad_t=yes, -zsh_cv_rlim_t_is_quad_t=no, -zsh_cv_rlim_t_is_quad_t=yes)]) -if test $zsh_cv_rlim_t_is_quad_t = yes; then - AC_DEFINE(RLIM_T_IS_QUAD_T) - DEFAULT_RLIM_T=quad_t +zsh_cv_rlim_t_is_longer=yes, +zsh_cv_rlim_t_is_longer=no, +zsh_cv_rlim_t_is_longer=yes)]) +if test $zsh_cv_rlim_t_is_longer = yes; then + AC_CACHE_CHECK(if rlim_t is a quad, + zsh_cv_rlim_t_is_quad_t, + [AC_TRY_RUN([ +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <stdio.h> +#include <sys/resource.h> +main() { + struct rlimit r; + char buf[[20]]; + r.rlim_cur = 0; + sprintf(buf, "%qd", r.rlim_cur); + exit(strcmp(buf, "0")); +}], + zsh_cv_rlim_t_is_quad_t=yes, + zsh_cv_rlim_t_is_quad_t=no, + zsh_cv_rlim_t_is_quad_t=no)]) + if test $zsh_cv_tlim_t_is_quad_t = yes; then + AC_DEFINE(RLIM_T_IS_QUAD_T) + DEFAULT_RLIM_T=quad_t + else + AC_DEFINE(RLIM_T_IS_LONG_LONG) + DEFAULT_RLIM_T='long long' + fi else AC_CACHE_CHECK(if the rlim_t is unsigned, zsh_cv_type_rlim_t_is_unsigned, @@ -1113,7 +1142,6 @@ main() elif test "x$zsh_cv_func_dlsym_needs_underscore" != xno; then dnl Do not cache failed value unset zsh_cv_func_dlsym_needs_underscore - dynamic=no fi fi diff --git a/patchlist.txt b/patchlist.txt index 9ec40d8dd..0497dee80 100644 --- a/patchlist.txt +++ b/patchlist.txt @@ -299,7 +299,7 @@ Phil: zless, 5032, simplified by Bart, 5037, also added a `setopt localoptions' after spending an hour wondering why nothing worked any more. -Me: `make install' does not do `make install.info', 5047 +pws: `make install' does not do `make install.info', 5047 Sven: compcall tries old-style completion from new-style function, compctl -K ' func' handles newstyle completion, 5059; avoid recursion, @@ -309,7 +309,7 @@ Sven: inserting completion inside brace expansion, 5060 Sven: extra completion context, 5092 -Me: typeset -T MYPATH mypath, 5094, plus fix for MYPATH=(foo), +pws: typeset -T MYPATH mypath, 5094, plus fix for MYPATH=(foo), mypath=foo (and also existing PATH=(foo) bug), 5120 Sven: doc fix for glob qualifiers, 5102 @@ -320,18 +320,18 @@ Drazen Kacar, modified by me: workaround for terminal bug on Solaris, Sven: zle and widget information via variables in new completion functions, 5104 -Me: remove old zle -C, zle -C now does new completion, 5105 +pws: remove old zle -C, zle -C now does new completion, 5105 Sven: glob qualifier o for modes, 5107 -Me: fix for unsetting special zle variables, 5111 +pws: fix for unsetting special zle variables, 5111 Drazen Kacar, modified by me: unlock terminal device on Solaris, 5118 (5117 was wrong) pws-7 -Me: patch for zls, 5054 (appeared in pws-6 but not in corresponding +pws: patch for zls, 5054 (appeared in pws-6 but not in corresponding patchlist). Bart: finally added missing hunk from 4965 which allowed unsetting an @@ -355,7 +355,7 @@ Sven: fix for command completion and pattern completions, 5178 Sven: ${(P)...} 5183, 5199, 5200 -Me: compctl documentation tidy-up, 5185, 5198 +pws: compctl documentation tidy-up, 5185, 5198 Sven: zle commands which use the minibuffer erase completion listings, 5201 @@ -370,13 +370,13 @@ Sven: ${foo:q}, 5208, preliminary Sven: use ${foo:q} for quoting prefix and suffix in new completion, 5120 -Me: bashautolist option, 5229; Sven's addition, 5234, and doc 5235; 5269 +pws: bashautolist option, 5229; Sven's addition, 5234, and doc 5235; 5269 -Me: .zlogout doc, 5233 +pws: .zlogout doc, 5233 -Me: added note on Linux Alpha with egcs to Etc/MACHINES, not posted +pws: added note on Linux Alpha with egcs to Etc/MACHINES, not posted -Me: typeset -T fix, 5247 +pws: typeset -T fix, 5247 Bart: parameter scoping docs, 5258 @@ -390,7 +390,7 @@ Sven: rewrite of $foo:q, 5265, +doc, 5284 Sven: get matcher number in new completion function, 5266 -Me: interrupts in getquery() weren't gracefully handled, 5281 +pws: interrupts in getquery() weren't gracefully handled, 5281 pws-8 @@ -409,14 +409,14 @@ Sven: compctl matcher to use reference counts, 5316 Sven: keys available in zle widget functions, 5320 -Me: compctl -LM, 5321 +pws: compctl -LM, 5321 -Me: revamped signames.c generation, 5326, 5329, plus Matt fix, 5330 +pws: revamped signames.c generation, 5326, 5329, plus Matt fix, 5330 Sweth, Bart, Me: Functions/allopt, basically as in 2121 with the odd emulate and local. -Me: emulate -L, 5332 +pws: emulate -L, 5332 Sven: printing of zle condition codes, 5335 @@ -425,9 +425,9 @@ Sven: Modularisation of new completion shell code, 5341 Sven: ignoring ignored prefix in new conditions, 5342; related fixes, 5343 -Me: patch for completion init and __normal, 5344; Sven fix, 5351 +pws: patch for completion init and __normal, 5344; Sven fix, 5351 -Me: "$foo[@]" didn't remove the argument if $foo wasn't set, 5349; +pws: "$foo[@]" didn't remove the argument if $foo wasn't set, 5349; Bart's fix so this works on OSes other than AIX, 5361 Sven: change fignore handling, 5352 @@ -437,14 +437,14 @@ failed, 5354 Sven: compadd -R function for suffix removal, 5355 -Me: #key-* completions now allow 0 or more key bindings, 5362 +pws: #key-* completions now allow 0 or more key bindings, 5362 -Me: Moved Misc/Completion to Functions/Completion; added some of my own +pws: Moved Misc/Completion to Functions/Completion; added some of my own new-style completions: not posted -Me: 5281 now works, 5364 +pws: 5281 now works, 5364 -Me: make dependencies for main.o, Makemod, zshpaths.h, 5365 +pws: make dependencies for main.o, Makemod, zshpaths.h, 5365 pws-9 @@ -458,9 +458,9 @@ Andrej: Reliant UNIX configuration, 5377 Sven: Manual for new completion (so far), 5384, 5397 -Me: dump new completion status for fast initialisation, 5393 +pws: dump new completion status for fast initialisation, 5393 -Me: bug fixlet for __path_files, 5398 +pws: bug fixlet for __path_files, 5398 Sven: overhaul of do_ambiguous with some bug-fixage, 5399, 5407 @@ -469,11 +469,11 @@ $COMPDUMP file, 5402 Sven: files -> __files, 5401 -Me: magicequalsubst now affects all arguments ...=~...:~..., 5403 +pws: magicequalsubst now affects all arguments ...=~...:~..., 5403 -Me: set -x output for [[ ... ]], 5408 +pws: set -x output for [[ ... ]], 5408 -Me: IRIX 6.5 problems in Etc/MACHINES (see 5410 from Helmut Jarausch). +pws: IRIX 6.5 problems in Etc/MACHINES (see 5410 from Helmut Jarausch). Sven: 5412: better matcher control. @@ -483,7 +483,7 @@ Sven: 5417: multiple subscripts with undefined array Sven: 5418: small addmatches fixes -Me: 5421: setting same element of assoc array in full array assignment crashed +pws: 5421: setting same element of assoc array in full array assignment crashed Sven: 5422: braces in completions were not tokenized; array parameters were used uncopied @@ -492,14 +492,73 @@ Sven: 5423: compadd accepts either - or -- to end options Sven: 5424: addmatches fix when not doing matching -Me: 5425: fix pattern matching for new completion +pws: 5425: fix pattern matching for new completion Sven: 5429: $CONTEXT strings Sven: 5430: rewrite Functions/Completions with simplified syntax (no #array type completions). -Me: 5436: set -x for function calls and ((...)). +pws: 5436: set -x for function calls and ((...)). -Me: unposted (but see 5440): zftp changes: more return 6's, functions now +pws: unposted (but see 5440): zftp changes: more return 6's, functions now do auto-open and avoid subshells. + + pws-10 + +Martin Buchholz: 5448: libc.h can't be included on Debian Linux, so only +include it on NeXT where it's necessary. + +Matt: 5330: I've put this back the way it original was. I hate sed almost +as much as awk. + +Sven: 5455: keep track of which matcher specification to use + +Sven: 5466: compwid manual for -after and -between + +Sven: 5467: expn manual typo + +Sven: 5469: init fix and Functions/Completion/_comp_parts + +Sven: 5470: new completion conditions didn't handle untokenization +consistently. + +Sven: 5471: range code knows not to handle associative arrays + +Sven: 5476: quoting of tildes in Functions/Completion/_path_files + +Sven: 5483: completeinword fixes + +Sven: 5489: control for matching in _path_files and _comp_parts + +Sven: 5490: unset test for AA elements when substituting + +pws: unposted, see 5503: remove dynamic=no from configure.in when +underscore is needed. + +pws: 5508: init and dump, globbing and printing. + +Sven: 5511: make sure compctl is available for new completion + +Sven: 5512, 5525: globcomplete fix for new completion + +Sven: 5521: improved option handling for _path_files + +Sven: 5529: cleanup for Functions/Completion + +pws: 5531: small init fix + +pws: 5538: approximate pattern matching, (#a1)readme etc. + +Sven: 5543: compadd -X, zshcompwid manual + +Sven: 5544: another completion cleanup + +pws: 5545: silly set -x mistake + +Sven: 5548: _path_files, _comp_parts + +Matt: 5553: under _WIN32, .exe suffix is optional for commands + +pws: unposted: Functions/Completion moved to Completion; subdirectories +Core, Base, Builtins, User, Commands created; Completion/README created. |