From e0b26186f1d3c1a3a580eb7e8a8199c25536f4e6 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Tue, 26 Oct 1999 15:36:10 +0000 Subject: manual/8424 --- Completion/Base/_arguments | 2 - Completion/Base/_brace_parameter | 2 - Completion/Base/_command_names | 24 +- Completion/Base/_describe | 2 - Completion/Base/_equal | 4 +- Completion/Base/_first | 2 +- Completion/Base/_job | 26 + Completion/Base/_regex_arguments | 3 - Completion/Base/_tilde | 17 +- Completion/Base/_values | 2 - Completion/Builtins/_aliases | 6 +- Completion/Builtins/_arrays | 2 +- Completion/Builtins/_bg_jobs | 5 +- Completion/Builtins/_bindkey | 6 +- Completion/Builtins/_builtin | 2 +- Completion/Builtins/_cd | 3 +- Completion/Builtins/_command | 4 +- Completion/Builtins/_compdef | 12 +- Completion/Builtins/_disable | 8 +- Completion/Builtins/_enable | 8 +- Completion/Builtins/_fc | 5 +- Completion/Builtins/_functions | 2 +- Completion/Builtins/_hash | 4 +- Completion/Builtins/_jobs | 5 +- Completion/Builtins/_kill | 6 +- Completion/Builtins/_popd | 39 + Completion/Builtins/_set | 8 +- Completion/Builtins/_stat | 3 +- Completion/Builtins/_trap | 6 +- Completion/Builtins/_unhash | 9 +- Completion/Builtins/_wait | 17 +- Completion/Builtins/_which | 14 +- Completion/Builtins/_zle | 4 +- Completion/Builtins/_zmodload | 4 +- Completion/Commands/_bash_completions | 2 +- Completion/Commands/_history_complete_word | 123 +- Completion/Commands/_read_comp | 8 +- Completion/Core/_approximate | 25 +- Completion/Core/_main_complete | 11 +- Completion/Core/_normal | 6 +- Completion/Core/_options | 3 +- Completion/Core/_parameters | 8 +- Completion/Core/_path_files | 32 +- Completion/Core/compinit | 4 + Completion/Debian/_apt | 2 +- Completion/User/_cvs | 10 +- Completion/User/_gdb | 3 +- Completion/User/_man | 2 +- Completion/User/_mh | 7 +- Completion/User/_nslookup | 2 - Completion/User/_tar | 3 - Completion/User/_urls | 2 - Completion/User/_users | 2 +- Completion/User/_whereis | 5 +- Completion/User/_whois | 1 - Completion/X/_x_font | 2 +- Completion/X/_x_window | 2 - Completion/X/_xmodmap | 2 - Doc/Makefile.in | 6 +- Doc/Zsh/compctl.yo | 9 +- Doc/Zsh/compsys.yo | 30 +- Doc/Zsh/compwid.yo | 65 +- Doc/Zsh/expn.yo | 6 + Doc/Zsh/manual.yo | 3 +- Doc/Zsh/mod_clone.yo | 2 +- Doc/Zsh/mod_compctl.yo | 10 +- Doc/Zsh/mod_complete.yo | 7 + Doc/Zsh/mod_computil.yo | 2 +- Doc/Zsh/mod_parameter.yo | 62 +- Doc/Zsh/mod_zle.yo | 2 +- Doc/Zsh/mod_zleparameter.yo | 28 + Doc/Zsh/modules.yo | 12 +- Src/Modules/parameter.c | 1071 ++++++++- Src/Modules/parameter.mdd | 2 +- Src/Zle/comp.h | 202 +- Src/Zle/compctl.c | 3254 ++++++++++++++++++---------- Src/Zle/compctl.h | 160 ++ Src/Zle/compctl.mdd | 9 +- Src/Zle/complete.c | 1332 ++++++++++++ Src/Zle/complete.mdd | 11 + Src/Zle/complist.c | 2 +- Src/Zle/complist.mdd | 2 +- Src/Zle/computil.mdd | 2 +- Src/Zle/zle.h | 17 +- Src/Zle/zle.mdd | 70 + Src/Zle/zle_keymap.c | 3 +- Src/Zle/zle_main.c | 135 +- Src/Zle/zle_thingy.c | 8 +- Src/Zle/zle_tricky.c | 2361 ++------------------ Src/Zle/zleparameter.c | 257 +++ Src/Zle/zleparameter.mdd | 5 + Src/hashtable.c | 109 +- Src/module.c | 17 +- Src/params.c | 4 +- Src/subst.c | 4 + Src/xmods.conf | 2 +- 96 files changed, 5857 insertions(+), 3959 deletions(-) create mode 100644 Completion/Base/_job create mode 100644 Doc/Zsh/mod_complete.yo create mode 100644 Doc/Zsh/mod_zleparameter.yo create mode 100644 Src/Zle/compctl.h create mode 100644 Src/Zle/complete.c create mode 100644 Src/Zle/complete.mdd create mode 100644 Src/Zle/zleparameter.c create mode 100644 Src/Zle/zleparameter.mdd diff --git a/Completion/Base/_arguments b/Completion/Base/_arguments index 18ddf0327..ca8f42c61 100644 --- a/Completion/Base/_arguments +++ b/Completion/Base/_arguments @@ -3,8 +3,6 @@ # Complete the arguments of the current command according to the # descriptions given as arguments to this function. -setopt localoptions extendedglob - local long cmd="$words[1]" descr mesg long=$argv[(I)--] diff --git a/Completion/Base/_brace_parameter b/Completion/Base/_brace_parameter index 2bf7b6a6d..9ed4867ef 100644 --- a/Completion/Base/_brace_parameter +++ b/Completion/Base/_brace_parameter @@ -6,8 +6,6 @@ _parameters -e # Without the `-e' option, we would use the following (see the file # Core/_parameters for more enlightenment). -# setopt localoptions extendedglob - # local lp ls n q # if [[ "$SUFFIX" = *\}* ]]; then diff --git a/Completion/Base/_command_names b/Completion/Base/_command_names index 2218aac21..79bd46e84 100644 --- a/Completion/Base/_command_names +++ b/Completion/Base/_command_names @@ -4,24 +4,34 @@ # complete only external commands and executable files. This and a # `-' as the first argument is then removed from the arguments. -local nm=$compstate[nmatches] ret=1 expl type=-c +local nm=$compstate[nmatches] ret=1 expl ext if [[ "$1" = -e ]]; then - type=-m + ext=yes shift elif [[ "$1" = - ]]; then shift fi # Complete jobs in implicit fg and bg -if [[ $type = -c && "$PREFIX[1]" = "%" ]]; then - _description expl job - compgen "$expl[@]" "$@" -j -P '%' +if [[ -z "$ext" && "$PREFIX[1]" = "%" ]]; then + _job -P '%' [[ nm -ne compstate[nmatches] ]] && return fi -_description expl command -compgen "$expl[@]" "$@" $type && ret=0 +_description expl 'external command' +compadd "$expl[@]" "$@" - "${(k@)commands}" && ret=0 + +if [[ -z "$ext" ]]; then + _description expl 'builtin command' + compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" && ret=0 + _description expl 'shell function' + compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" && ret=0 + _description expl 'alias' + compadd "$expl[@]" "$@" - "${(k@)raliases[(R)^?disabled*]}" && ret=0 + _description expl 'reserved word' + compadd "$expl[@]" "$@" - "${(k@)reswords[(R)^?disabled*]}" && ret=0 +fi if [[ nm -eq compstate[nmatches] ]]; then _description expl 'executable file or directory' diff --git a/Completion/Base/_describe b/Completion/Base/_describe index 2bb32ad35..e59dc1593 100644 --- a/Completion/Base/_describe +++ b/Completion/Base/_describe @@ -2,8 +2,6 @@ # This can be used to add options or values with descriptions as matches. -setopt localoptions extendedglob - local isopt cmd opt expl tmps tmpd tmpmd tmpms ret=1 showd _nm hide cmd="$words[1]" diff --git a/Completion/Base/_equal b/Completion/Base/_equal index 14f28703c..1ba92cdc6 100644 --- a/Completion/Base/_equal +++ b/Completion/Base/_equal @@ -3,6 +3,6 @@ local expl _description expl alias -compgen "$expl[@]" -a +compadd "$@" "$expl[@]" - "${(@k)aliases[(R)^?disabled*]}" _description expl command -compgen "$expl[@]" -m +compadd "$@" "$expl[@]" - "${(k@)commands}" diff --git a/Completion/Base/_first b/Completion/Base/_first index ba5ef6c24..d259824cc 100644 --- a/Completion/Base/_first +++ b/Completion/Base/_first @@ -55,7 +55,7 @@ # else # _description -V expl "history ($n)" # fi -# if compgen "$expl[@]" -Q -H $(( i*10 )) ''; then +# if compadd "$expl[@]" -Q - "${(@)historywords:#[\$'\"]*}"; then # # We have found at least one matching word, so we switch # # on menu-completion and make sure that no other # # completion function is called by setting _compskip. diff --git a/Completion/Base/_job b/Completion/Base/_job new file mode 100644 index 000000000..081956c51 --- /dev/null +++ b/Completion/Base/_job @@ -0,0 +1,26 @@ +#autoload + +local expl disp jobs job jids + +if [[ "$1" = -r ]]; then + jids=( "${(@k)jobstates[(R)running*]}" ) + shift + _description expl 'running job' +elif [[ "$1" = -s ]]; then + jids=( "${(@k)jobstates[(R)running*]}" ) + shift + _description expl 'suspended job' +else + [[ "$1" = - ]] && shift + jids=( "${(@k)jobtexts}" ) + _description expl job +fi + +disp=() +jobs=() +for job in "$jids[@]"; do + disp=( "$disp[@]" "[${(l:2:: :)job}] ${jobtexts[$job]}" ) + jobs=( "$jobs[@]" "$job" ) +done + +compadd "$@" "$expl[@]" -ld disp - "$jobs[@]" diff --git a/Completion/Base/_regex_arguments b/Completion/Base/_regex_arguments index 2608fda11..e2858e66c 100644 --- a/Completion/Base/_regex_arguments +++ b/Completion/Base/_regex_arguments @@ -206,7 +206,6 @@ _ra_gen_func () { print -lr - \ "$funcname () {" \ - 'setopt localoptions extendedglob' \ 'local _ra_state _ra_left _ra_right _ra_actions' \ "_ra_state=$start" \ '_ra_left=' \ @@ -323,8 +322,6 @@ _ra_gen_parse_state () { } _regex_arguments () { - setopt localoptions extendedglob - local funcname="_regex_arguments_tmp" local funcdef diff --git a/Completion/Base/_tilde b/Completion/Base/_tilde index d871ddc11..0b81f75a1 100644 --- a/Completion/Base/_tilde +++ b/Completion/Base/_tilde @@ -2,12 +2,7 @@ # We use all named directories and user names here. If this is too slow # for you or if there are too many of them, you may want to use -# `compgen -k friends -qS/' or something like that. To get all user names -# if there are no matches in the `friends' array, add -# `(( compstate[nmatches] )) || compgen -nu -qS/' -# below that. - -setopt localoptions extendedglob +# `compadd -qS/ - "$friends[@]"' or something like that. local d s dirs list lines revlines i @@ -20,21 +15,20 @@ else fi if [[ -prefix [-+] ]]; then - lines=(${(f)"$(dirs -v)"}) + lines=("$PWD" "$dirstack[@]") integer i if [[ ( -prefix - && ! -o pushdminus ) || ( -prefix + && -o pushdminus ) ]]; then revlines=( $lines ) for (( i = 1; i <= $#lines; i++ )); do - lines[$i]="$((i-1)) -- ${revlines[-$i]##[0-9]#[ ]#}" + lines[$i]="$((i-1)) -- ${revlines[-$i]}" done else for (( i = 1; i <= $#lines; i++ )); do - lines[$i]="$((i-1)) -- ${lines[$i]##[0-9]#[ ]#}" + lines[$i]="$((i-1)) -- ${lines[$i]}" done fi list=(${lines%% *}) - compset -P '[-+]' _description d 'directory stack' compadd "$d[@]" -V dirs -S/ -ld lines -Q - "$list[@]" @@ -45,6 +39,5 @@ else else _description d 'named directory' fi - compgen "$d[@]" -n "$s[@]" + compadd "$d[@]" "$s[@]" - "${(@k)nameddirs}" fi - diff --git a/Completion/Base/_values b/Completion/Base/_values index fb70f6e7f..e4ef8af68 100644 --- a/Completion/Base/_values +++ b/Completion/Base/_values @@ -1,7 +1,5 @@ #autoload -setopt localoptions extendedglob - if compvalues -i "$@"; then local noargs args opts descr action expl sep diff --git a/Completion/Builtins/_aliases b/Completion/Builtins/_aliases index 3e4b789b6..0189d6cf3 100644 --- a/Completion/Builtins/_aliases +++ b/Completion/Builtins/_aliases @@ -2,5 +2,7 @@ local expl -_description expl alias -compgen "$expl[@]" -a +_description expl 'regular alias' +compadd "$expl[@]" - "${(@k)raliases[(R)^?disabled*]}" +_description expl 'global alias' +compadd "$expl[@]" - "${(@k)galiases[(R)^?disabled*]}" diff --git a/Completion/Builtins/_arrays b/Completion/Builtins/_arrays index 20681f527..a2aa813b6 100644 --- a/Completion/Builtins/_arrays +++ b/Completion/Builtins/_arrays @@ -3,4 +3,4 @@ local expl _description expl array -compgen "$expl[@]" -A +compadd "$expl[@]" - "${(@k)parameters[(R)*array*]}" diff --git a/Completion/Builtins/_bg_jobs b/Completion/Builtins/_bg_jobs index 65f21b483..6d6a8fcc4 100644 --- a/Completion/Builtins/_bg_jobs +++ b/Completion/Builtins/_bg_jobs @@ -1,6 +1,3 @@ #compdef bg -local expl - -_description expl 'suspended job' -compgen "$expl[@]" -z -P '%' +_job -s -P '%' diff --git a/Completion/Builtins/_bindkey b/Completion/Builtins/_bindkey index 891e2cedb..3f964e704 100644 --- a/Completion/Builtins/_bindkey +++ b/Completion/Builtins/_bindkey @@ -3,7 +3,7 @@ # Normally, this completes names of zle widgets, whether the builtin ones # or ones defined by the user. Note that a - allows a wildcard before it, # so h-b-s-b will complete to history-beginning-search-backward. You -# can alter this by removing the -M ... from the second compgen. +# can alter this by removing the -M ... from the second compadd. # # Where appropriate, will complete keymaps instead of widgets. @@ -11,8 +11,8 @@ local expl if [[ "$words[2]" = -*[DAN]* || "$words[CURRENT-1]" = -*M ]]; then _description expl keymap - compadd "$expl[@]" - $(bindkey -l) + compadd "$expl[@]" - "$zlekeymaps[@]" else _description expl widget - compgen "$expl[@]" -b -M 'r:|-=* r:|=*' + compadd "$expl[@]" -M 'r:|-=* r:|=*' - "${(@k)zlewidgets}" fi diff --git a/Completion/Builtins/_builtin b/Completion/Builtins/_builtin index f925b88a8..06ef1c246 100644 --- a/Completion/Builtins/_builtin +++ b/Completion/Builtins/_builtin @@ -8,5 +8,5 @@ else local expl _description expl 'builtin command' - compgen "$expl[@]" -eB + compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" fi diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd index 838b1af24..b407b5a85 100644 --- a/Completion/Builtins/_cd +++ b/Completion/Builtins/_cd @@ -11,8 +11,7 @@ # it's not a lot of use. If you don't type the + or - it will # complete directories as normal. -emulate -L zsh -setopt extendedglob nonomatch +setopt localoptions nonomatch local expl diff --git a/Completion/Builtins/_command b/Completion/Builtins/_command index 4ae274f1f..9f54aae80 100644 --- a/Completion/Builtins/_command +++ b/Completion/Builtins/_command @@ -6,6 +6,6 @@ if [[ CURRENT -ge 3 ]]; then else local expl - _description expl command - compgen "$expl[@]" -em + _description expl 'external command' + compadd "$expl[@]" "$@" - "${(k@)commands}" fi diff --git a/Completion/Builtins/_compdef b/Completion/Builtins/_compdef index 1df5758b3..4208c2689 100644 --- a/Completion/Builtins/_compdef +++ b/Completion/Builtins/_compdef @@ -1,6 +1,6 @@ #compdef compdef -local func base=2 +local expl func base=2 while [[ $words[base] = -* ]]; do case $words[base] in @@ -12,11 +12,13 @@ while [[ $words[base] = -* ]]; do done if [ "$delete" ]; then - compadd ${(k)_comps} + _description expl 'completed command' + compadd "$expl[@]" - ${(k)_comps} else - if [[ CURRENT -eq base ]]; then - for func in ${^~fpath:/.}/_(|*[^~])(N:t); compadd -P_ - ${func#_} + if [[ CURRENT -eq base ]]; then + _description expl 'completion function' + compadd "$expl[@]" - ${^fpath:/.}/_(|*[^~])(N:t) else - compgen -c + _command_names fi fi diff --git a/Completion/Builtins/_disable b/Completion/Builtins/_disable index b219f4b7e..fe6b991b2 100644 --- a/Completion/Builtins/_disable +++ b/Completion/Builtins/_disable @@ -4,19 +4,19 @@ local prev="$words[CURRENT-1]" ret=1 expl if [[ "$prev" = -*a* ]]; then _description expl alias - compgen "$expl[@]" -ea && ret=0 + compadd "$expl[@]" "$@" - "${(k@)aliases[(R)^?disabled*]}" && ret=0 fi if [[ "$prev" = -*f* ]]; then _description expl 'shell function' - compgen "$expl[@]" -eF && ret=0 + compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" && ret=0 fi if [[ "$prev" = -*r* ]]; then _description expl 'reserved word' - compgen "$expl[@]" -ew && ret=0 + compadd "$expl[@]" "$@" - "${(k@)reswords[(R)^?disabled*]}" && ret=0 fi if [[ "$prev" != -* ]]; then _description expl 'builtin command' - compgen "$expl[@]" -eB && ret=0 + compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" && ret=0 fi return ret diff --git a/Completion/Builtins/_enable b/Completion/Builtins/_enable index a698a3895..b4f0356bc 100644 --- a/Completion/Builtins/_enable +++ b/Completion/Builtins/_enable @@ -4,19 +4,19 @@ local prev="$words[CURRENT-1]" ret=1 expl if [[ "$prev" = -*a* ]]; then _description expl alias - compgen "$expl[@]" -da && ret=0 + compadd "$expl[@]" "$@" - "${(k@)aliases[(R)?disabled*]}" && ret=0 fi if [[ "$prev" = -*f* ]]; then _description expl 'shell function' - compgen "$expl[@]" -dF && ret=0 + compadd "$expl[@]" "$@" - "${(k@)functions[(R)?disabled*]}" && ret=0 fi if [[ "$prev" = -*r* ]]; then _description expl 'reserved word' - compgen "$expl[@]" -dw && ret=0 + compadd "$expl[@]" "$@" - "${(k@)reswords[(R)?disabled*]}" && ret=0 fi if [[ "$prev" != -* ]]; then _description expl 'builtin command' - compgen "$expl[@]" -dB && ret=0 + compadd "$expl[@]" "$@" - "${(k@)builtins[(R)?disabled*]}" && ret=0 fi return ret diff --git a/Completion/Builtins/_fc b/Completion/Builtins/_fc index 8d5c82763..c2eaab7bb 100644 --- a/Completion/Builtins/_fc +++ b/Completion/Builtins/_fc @@ -1,10 +1,9 @@ #compdef fc -local prev="$words[CURRENT-1]" expl +local prev="$words[CURRENT-1]" if [[ "$prev" = -*e ]]; then - _description expl command - compgen "$expl[@]" -c + _command_names -e elif [[ "$prev" = -[ARWI]## ]]; then _files fi diff --git a/Completion/Builtins/_functions b/Completion/Builtins/_functions index 7aa30b7bc..d5f32d2e3 100644 --- a/Completion/Builtins/_functions +++ b/Completion/Builtins/_functions @@ -3,4 +3,4 @@ local expl _description expl 'shell function' -compgen "$expl[@]" -F +compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" diff --git a/Completion/Builtins/_hash b/Completion/Builtins/_hash index c8169c590..c06d63699 100644 --- a/Completion/Builtins/_hash +++ b/Completion/Builtins/_hash @@ -7,11 +7,11 @@ if [[ "$words[2]" = -*d* ]]; then _path_files -g '*(-/)' else _description expl 'named directory' - compgen "$expl[@]" -n -q -S '=' + compadd "$expl[@]" -q -S '=' - "${(@k)nameddirs}" fi elif compset -P 1 '*\='; then _files -/g '*(*)' else _description expl command - compgen "$expl[@]" -m -q -S '=' + compadd "$expl[@]" -q -S '=' - "${(@k)commands}" fi diff --git a/Completion/Builtins/_jobs b/Completion/Builtins/_jobs index b1ff31b4a..c17b73c92 100644 --- a/Completion/Builtins/_jobs +++ b/Completion/Builtins/_jobs @@ -1,6 +1,3 @@ #compdef disown fg jobs -local expl - -_description expl job -compgen "$expl[@]" -j -P '%' +_job -P '%' diff --git a/Completion/Builtins/_kill b/Completion/Builtins/_kill index da70cf753..b79bfd6c0 100644 --- a/Completion/Builtins/_kill +++ b/Completion/Builtins/_kill @@ -8,9 +8,9 @@ if compset -P 1 -; then else local ret=1 - _description expl job - compgen "$expl[@]" -P '%' -j && ret=0 - list=("${(@Mr:COLUMNS-1:)${(f@)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ ]#${PREFIX}[0-9]#${SUFFIX}[ ]*}") + _job && ret=0 + + list=("${(@M)${(f@)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ ]#${PREFIX}[0-9]#${SUFFIX}[ ]*}") _description expl 'process ID' compadd "$expl[@]" -ld list - ${${${(f)"$(ps $=compconfig[ps_args] 2>/dev/null)"}[2,-1]## #}%% *} && ret=0 diff --git a/Completion/Builtins/_popd b/Completion/Builtins/_popd index 9054befb7..07dc4a87f 100644 --- a/Completion/Builtins/_popd +++ b/Completion/Builtins/_popd @@ -5,6 +5,45 @@ # way round if pushdminus is set). Note that this function is also called # from _cd for cd and pushd. +setopt localoptions nonomatch + +[[ $PREFIX = [-+]* ]] || return 1 + +local expl list lines revlines ret=1 i + +IPREFIX=$PREFIX[1] +PREFIX=$PREFIX[2,-1] + +# get the list of directories with their canonical number +# and turn the lines into an array, removing the current directory +lines=( "$dirstack[@]" ) +if [[ ( $IPREFIX = - && ! -o pushdminus ) || + ( $IPREFIX = + && -o pushdminus ) ]]; then + integer i + revlines=( $lines ) + for (( i = 1; i <= $#lines; i++ )); do + lines[$i]="$((i-1)) -- ${revlines[-$i]}" + done +else + for (( i = 1; i <= $#lines; i++ )); do + lines[$i]="$i -- ${lines[$i]}" + done +fi +# get the array of numbers only +list=(${lines%% *}) +_description expl 'directory stack index' +compadd "$expl[@]" -ld lines -V dirs -Q - "$list[@]" && ret=0 +[[ -z $compstate[list] ]] && compstate[list]=list && ret=0 +[[ -n $compstate[insert] ]] && compstate[insert]=menu && ret=0 + +return ret +#compdef popd + +# This just completes the numbers after +, showing the full directory list +# with numbers. For - we do the same thing, but reverse the numbering (other +# way round if pushdminus is set). Note that this function is also called +# from _cd for cd and pushd. + emulate -L zsh setopt extendedglob nonomatch diff --git a/Completion/Builtins/_set b/Completion/Builtins/_set index 297a15d3b..b96eb0efb 100644 --- a/Completion/Builtins/_set +++ b/Completion/Builtins/_set @@ -1,11 +1,9 @@ #compdef set -local prev="$words[CURRENT-1]" expl +local prev="$words[CURRENT-1]" if [[ "$prev" = [-+]o ]]; then - _description expl 'zsh option' - compgen "$expl[@]" -o + _options elif [[ "$prev" = -A ]]; then - _description expl array - compgen "$expl[@]" -A + _arrays fi diff --git a/Completion/Builtins/_stat b/Completion/Builtins/_stat index 5a39e56e5..322f042ec 100644 --- a/Completion/Builtins/_stat +++ b/Completion/Builtins/_stat @@ -3,8 +3,7 @@ local expl if [[ "$words[CURRENT-1]" = -[AH] ]]; then - _description expl array - compgen "$expl[@]" -A + _arrays else _description expl 'inode element' [[ "$PREFIX[1]" = + ]] && diff --git a/Completion/Builtins/_trap b/Completion/Builtins/_trap index d6fc7b388..0f0209914 100644 --- a/Completion/Builtins/_trap +++ b/Completion/Builtins/_trap @@ -3,9 +3,9 @@ local expl if [[ CURRENT -eq 2 ]]; then - _description expl command - compgen "$expl[@]" -c + compset -q + _normal else _description expl signal - compgen "$expl[@]" -k signals + compadd "$expl[@]" - "$signals[@]" fi diff --git a/Completion/Builtins/_unhash b/Completion/Builtins/_unhash index eb1787936..dfebd5399 100644 --- a/Completion/Builtins/_unhash +++ b/Completion/Builtins/_unhash @@ -4,19 +4,18 @@ local fl="$words[2]" ret=1 expl if [[ "$fl" = -*d* ]]; then _description expl 'named directory' - compgen "$expl[@]" -n && ret=0 + compadd "$expl[@]" - "${(@k)nameddirs}" && ret=0 fi if [[ "$fl" = -*a* ]]; then _description expl alias - compgen "$expl[@]" -a && ret=0 + compadd "$expl[@]" - "${(@k)aliases}" && ret=0 fi if [[ "$fl" = -*f* ]]; then _description expl 'shell function' - compgen "$expl[@]" -F && ret=0 + compadd "$expl[@]" - "${(@k)functions}" && ret=0 fi if [[ "$fl" != -* ]]; then - _description expl command - compgen "$expl[@]" -m && ret=0 + _command_names -e && ret=0 fi return ret diff --git a/Completion/Builtins/_wait b/Completion/Builtins/_wait index 17a030dad..68ca187e8 100644 --- a/Completion/Builtins/_wait +++ b/Completion/Builtins/_wait @@ -1,21 +1,10 @@ #compdef wait -# This uses two configuration keys: -# -# ps_args -# This can be set to options of the ps(1) command that should be -# used when invoking it to get the pids to complete. -# -# ps_listargs -# This defaults to the value of the `ps_args' key and defines -# options for the ps command that are to be used when creating -# the list to display during completion. - local list ret=1 expl -_description expl job -compgen "$expl[@]" -P '%' -j && ret=0 -list=("${(@Mr:COLUMNS-1:)${(f)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ ]#${PREFIX}[0-9]#${SUFFIX}[ ]*}") +_job -P '%' && ret=0 + +list=("${(@M)${(f)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ ]#${PREFIX}[0-9]#${SUFFIX}[ ]*}") _description expl 'process ID' compadd "$expl[@]" -ld list - ${${${(f)"$(ps $=compconfig[ps_args] 2>/dev/null)"}[2,-1]## #}%% *} && ret=0 diff --git a/Completion/Builtins/_which b/Completion/Builtins/_which index d67af7316..30d20ab36 100644 --- a/Completion/Builtins/_which +++ b/Completion/Builtins/_which @@ -2,9 +2,13 @@ local expl -_description expl command -compgen "$expl[@]" -c -_description expl alias -compgen "$expl[@]" -a +_description expl 'external command' +compadd "$expl[@]" "$@" - "${(k@)commands}" && ret=0 +_description expl 'builtin command' +compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" && ret=0 _description expl 'shell function' -compgen "$expl[@]" -F +compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" && ret=0 +_description expl 'alias' +compadd "$expl[@]" "$@" - "${(k@)raliases[(R)^?disabled*]}" && ret=0 +_description expl 'reserved word' +compadd "$expl[@]" "$@" - "${(k@)reswords[(R)^?disabled*]}" && ret=0 diff --git a/Completion/Builtins/_zle b/Completion/Builtins/_zle index 06e8fcad3..327d67c62 100644 --- a/Completion/Builtins/_zle +++ b/Completion/Builtins/_zle @@ -4,8 +4,8 @@ local expl if [[ "$words[2]" = -N && CURRENT -eq 3 ]]; then _description expl 'widget shell function' - compgen "$expl[@]" -F + compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" && ret=0 else _description expl widget - compgen "$expl[@]" -b + compadd "$expl[@]" - "${(@k)zlewidgets}" fi diff --git a/Completion/Builtins/_zmodload b/Completion/Builtins/_zmodload index 429128517..a0d5987c4 100644 --- a/Completion/Builtins/_zmodload +++ b/Completion/Builtins/_zmodload @@ -4,10 +4,10 @@ local fl="$words[2]" expl if [[ "$fl" = -*(a*u|u*a)* || "$fl" = -*a* && CURRENT -ge 4 ]]; then _description expl 'builtin command' - compgen "$expl[@]" -B + compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" && ret=0 elif [[ "$fl" = -*u* ]]; then _description expl module - compadd "$expl[@]" - $(zmodload) + compadd "$expl[@]" - "${(@k)modules}" else _description expl 'module file' compadd "$expl[@]" - ${^module_path}/*.s[ol](N:t:r) diff --git a/Completion/Commands/_bash_completions b/Completion/Commands/_bash_completions index 5f3adc1e0..ee663fc30 100644 --- a/Completion/Commands/_bash_completions +++ b/Completion/Commands/_bash_completions @@ -30,7 +30,7 @@ local key=$KEYS[-1] case $key in '!') _main_complete _command_names ;; - '$') compgen -E + '$') compadd - "${(@k)parameters[(R)*export*]}" ;; '@') _main_complete _hosts ;; diff --git a/Completion/Commands/_history_complete_word b/Completion/Commands/_history_complete_word index f459046d2..1112339af 100644 --- a/Completion/Commands/_history_complete_word +++ b/Completion/Commands/_history_complete_word @@ -1,31 +1,108 @@ -#compdef -k complete-word \e/ +#compdef -K _history-complete-older complete-word \e/ _history-complete-newer complete-word \e, +# +# Complete words from the history +# +# by Adam Spiers, with help gratefully received from +# Sven Wischnowsky and Bart Schaefer +# +# Available configuration keys: +# +# history_list -- display lists of available matches +# history_stop -- prevent looping at beginning and end of matches +# during menu-completion +# history_sort -- sort matches lexically (default is to sort by age) +# history_remove_all_dups -- +# remove /all/ duplicate matches rather than just +# consecutives +# -local expl +_history_complete_word () { + local expl direction -if [[ -n "$compstate[old_list]" && -n "$compconfig[history_stop]" ]]; then - if [[ -z "$_hist_menu_end" && - compstate[old_insert] -lt _hist_menu_length ]]; then - compstate[old_list]=keep - compstate[insert]=$((compstate[old_insert]+1)) + if [[ $WIDGET = *newer ]]; then + direction=older else - _hist_menu_end=yes - if [[ "$compconfig[history_stop]" = verbose ]]; then - _message 'end of history reached' - else - compstate[old_list]=keep - compstate[insert]=_hist_menu_length + direction=newer + fi + + [[ -z "$compconfig[history_list]" ]] && compstate[list]='' + + if [[ -n "$compstate[old_list]" && + ( -n "$compconfig[history_stop]" || "$compstate[insert]" = menu ) ]]; then + # array of matches is newest -> oldest (reverse of history order) + if [[ "$direction" == 'older' ]]; then + if [[ compstate[old_insert] -eq $_hist_menu_length || + "$_hist_stop" == 'oldest' ]]; then + _hist_stop='oldest' + [[ "$compconfig[history_stop]" = verbose ]] && + _message 'beginning of history reached' + elif [[ "$_hist_stop" == 'newest' ]]; then + zle -Rc + _history_complete_word_gen_matches + else + compstate[old_list]=keep + (( compstate[insert] = compstate[old_insert] + 1 )) + fi + elif [[ "$direction" == 'newer' ]]; then + if [[ compstate[old_insert] -eq 1 || "$_hist_stop" == 'newest' ]]; then + _hist_stop='newest' + [[ "$compconfig[history_stop]" = verbose ]] && + _message 'end of history reached' + elif [[ "$_hist_stop" == 'oldest' ]]; then + zle -Rc + _history_complete_word_gen_matches + else + compstate[old_list]=keep + (( compstate[insert] = compstate[old_insert] - 1 )) + fi fi + else + _hist_stop='' + _hist_old_prefix="$PREFIX" + _history_complete_word_gen_matches fi -else - if [[ -n "$compconfig[history_sort]" ]]; then - _description expl 'history word' + + [[ -n "$compstate[nmatches]" ]] +} + +_history_complete_word_gen_matches () { + if [[ -n "$compconfig[history_list]" ]]; then + if [[ -n "$compconfig[history_sort]" ]]; then + _description expl 'history word' + else + _description -V expl 'history word' + fi else - _description -V expl 'history word' + if [[ -n "$compconfig[history_sort]" ]]; then + expl=() + else + expl=('-V' '') + fi fi - compgen "$expl[@]" -Q -H 0 '' - if [[ -n "$compconfig[history_stop]" ]]; then - compstate[insert]=1 - _hist_menu_length="$compstate[nmatches]" - _hist_menu_end='' + + [[ -n "$_hist_stop" ]] && PREFIX="$_hist_old_prefix" + + local rem_dups + if [[ -n "$compconfig[history_remove_all_dups]" ]]; then + rem_dups='' + else + rem_dups='-1' fi -fi + + compadd "$expl[@]" $rem_dups -Q - "${(@)historywords:#[\$'\"]*}" + + _hist_menu_length="$compstate[nmatches]" + + case "$direction" in + newer) compstate[insert]=$_hist_menu_length + [[ -n "$_hist_stop" ]] && (( compstate[insert]-- )) + ;; + older) compstate[insert]=1 + [[ -n "$_hist_stop" ]] && (( compstate[insert]++ )) + ;; + esac + + [[ -n "$_hist_stop" ]] && _hist_stop='' +} + +_history_complete_word "$@" diff --git a/Completion/Commands/_read_comp b/Completion/Commands/_read_comp index a32879b56..8a46d8af2 100644 --- a/Completion/Commands/_read_comp +++ b/Completion/Commands/_read_comp @@ -6,9 +6,9 @@ # evaluate to generate the completions; unambiguous strings in the function # name are automatically completed. # -# Else it is taken to be a set of arguments for compgen to generate a list +# Else it is taken to be a set of arguments for compadd to generate a list # of choices. The possibilities are the same as the flags for generating -# completions given in the zshcompctl manual page. Note the arguments are +# completions given in the zshcompwid manual page. Note the arguments are # verbatim: include minus signs, spaces, quotes, etc. # # On subsequent calls, the same completion will be re-performed. To @@ -37,7 +37,7 @@ if [[ compstate[matcher] -gt 1 || if [[ $_read_comp = _* ]]; then eval $_read_comp else - eval "compgen $_read_comp" + eval "compadd $_read_comp" fi return fi @@ -154,5 +154,5 @@ zle -cR '' if [[ $str = _* ]]; then eval $str else - eval "compgen $str" + eval "compadd $str" fi diff --git a/Completion/Core/_approximate b/Completion/Core/_approximate index 57b327e64..235e324f7 100644 --- a/Completion/Core/_approximate +++ b/Completion/Core/_approximate @@ -70,22 +70,6 @@ compadd() { fi } -compgen() { - [[ "$*" != *-([a-zA-Z/]#|)U* && - "${#:-$PREFIX$SUFFIX}" -le _comp_correct ]] && return - - if [[ "$PREFIX" = \~*/* ]]; then - PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}" - else - PREFIX="(#a${_comp_correct})$PREFIX" - fi - if [[ -n "$_correct_prompt" ]]; then - builtin compgen "$@" -X "$_correct_prompt" -J _correct - else - builtin compgen "$@" -J _correct - fi -} - # Now initialise our counter. We also set `compstate[matcher]' # to `-1'. This allows completion functions to use the simple # `[[ compstate[matcher] -gt 1 ]] && return' to avoid being @@ -99,11 +83,6 @@ compstate[matcher]=-1 _correct_prompt="${cfgps//\\%e/1}" -# We also need to set `extendedglob' and make the completion -# code behave as if globcomplete were set. - -setopt extendedglob - [[ -z "$compstate[pattern_match]" ]] && compstate[pattern_match]='*' while [[ _comp_correct -le comax ]]; do @@ -135,7 +114,7 @@ while [[ _comp_correct -le comax ]]; do compstate[force_list]=list fi compstate[matcher]="$compstate[total_matchers]" - unfunction compadd compgen + unfunction compadd return 0 fi @@ -147,6 +126,6 @@ while [[ _comp_correct -le comax ]]; do done compstate[matcher]="$compstate[total_matchers]" -unfunction compadd compgen +unfunction compadd return 1 diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete index 304a97828..72233a59b 100644 --- a/Completion/Core/_main_complete +++ b/Completion/Core/_main_complete @@ -9,13 +9,8 @@ # # local _set_options _unset_options # -# if zmodload -e parameter; then -# _set_options=(${(k)options[(R)on]}) -# _unset_options=(${(k)options[(R)off]}) -# else -# _set_options=("${(@f)$({ unsetopt kshoptionprint; setopt } 2>/dev/null)}") -# _unset_options=("${(@f)$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)}") -# fi +# _set_options=(${(k)options[(R)on]}) +# _unset_options=(${(k)options[(R)off]}) # # This is needed because completion functions may set options locally # which makes the output of setopt and unsetopt reflect a different @@ -26,7 +21,7 @@ local comp post ret=1 _compskip typeset -U _lastdescr -setopt localoptions nullglob rcexpandparam +setopt localoptions nullglob rcexpandparam extendedglob unsetopt markdirs globsubst shwordsplit nounset ksharrays # Special completion contexts after `~' and `='. diff --git a/Completion/Core/_normal b/Completion/Core/_normal index 085c738d2..79efaeb97 100644 --- a/Completion/Core/_normal +++ b/Completion/Core/_normal @@ -27,11 +27,7 @@ elif [[ "$command" == */* ]]; then cmd2="${command:t}" else cmd1="$command" - if zmodload -e parameter; then - cmd2="$commands[$command]" - else - cmd2=$(whence -p - $command) - fi + cmd2="$commands[$command]" fi # See if there are any matching pattern completions. diff --git a/Completion/Core/_options b/Completion/Core/_options index 356cd70db..0232db857 100644 --- a/Completion/Core/_options +++ b/Completion/Core/_options @@ -5,4 +5,5 @@ local expl _description expl 'zsh option' -compgen "$expl[@]" "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o +compadd "$expl[@]" "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' - \ + "${(@k)options}" diff --git a/Completion/Core/_parameters b/Completion/Core/_parameters index 34a8c3e9b..fdb786231 100644 --- a/Completion/Core/_parameters +++ b/Completion/Core/_parameters @@ -3,17 +3,11 @@ # This should be used to complete parameter names if you need some of the # extra options of compadd. It completes only non-local parameters. -setopt localoptions extendedglob - local pars expl _description expl parameter -if zmodload -e parameter; then - pars=( ${(k)parameters[(R)^*local*]} ) -else - pars=( ${${${(f)"$(typeset +)"}:#*local *}##* } ) -fi +pars=( ${(k)parameters[(R)^*local*]} ) compadd "$expl[@]" "$@" - $pars diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files index 9d44180c8..cbec82c8a 100644 --- a/Completion/Core/_path_files +++ b/Completion/Core/_path_files @@ -10,7 +10,7 @@ local nm=$compstate[nmatches] menu match matcher typeset -U prepaths exppaths -setopt localoptions nullglob rcexpandparam extendedglob +setopt localoptions nullglob rcexpandparam unsetopt markdirs globsubst shwordsplit nounset local sopt='-' gopt='' opt @@ -110,36 +110,6 @@ orig="${PREFIX}${SUFFIX}" ( $#compstate[pattern_match] -ne 0 && "${orig#\~}" != "${${orig#\~}:q}" ) ]] && menu=yes -# We will first try normal completion called with `compgen', but only if we -# weren't given a `-F', `-r', or `-R' option or we are in the string. - -if [[ -z "$suf" && $#ignore -eq 0 && $#remsfx -eq 0 && - -z "$_comp_correct" ]]; 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 "$prepaths[1]" || "$pre[1]" = [~/] || "$pre" = (.|..)/* ]]; then - tmp1=() - else - tmp1=(-W "( $prepaths )") - fi - - # Now call compgen. - - if [[ -z "$gopt" ]]; then - compgen "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \ - "$tmp1[@]" "$matcher[@]" $sopt - else - compgen "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \ - "$tmp1[@]" "$matcher[@]" $sopt -g "$pats" - fi - - # If this generated any matches, we don't want to do in-path completion. - - [[ compstate[nmatches] -eq nm ]] || return 0 -fi - # If given no `-F' option, we want to use `fignore'. (( $#ignore )) || ignore=(-F fignore) diff --git a/Completion/Core/compinit b/Completion/Core/compinit index ca9240f92..b3472ca0e 100644 --- a/Completion/Core/compinit +++ b/Completion/Core/compinit @@ -78,6 +78,10 @@ while [[ $# -gt 0 && $1 = -[dDf] ]]; do fi done +# We need the parameter modules. + +zmodload -i parameter zleparameter + # The associative array containing the definitions for the commands. # Definitions for patterns will be stored in the normal arrays `_patcomps' # and `_postpatcomps'. diff --git a/Completion/Debian/_apt b/Completion/Debian/_apt index e4593d54e..352dd3f2d 100644 --- a/Completion/Debian/_apt +++ b/Completion/Debian/_apt @@ -473,7 +473,7 @@ _apt-config () { -- \ /$'shell\0'/ \ \( \ - /$'[^\0]#\0'/ :'compgen "$expl_shell_var[@]" -v' \ + /$'[^\0]#\0'/ :'compadd "$expl_shell_var[@]" - "${(@k)parameters}' \ /$'[^\0]#\0'/ :'compadd "$expl_config_key[@]" - ${${(f)"$(apt-config dump 2>&1)"}% *}' \ \) \# \| \ /$'dump\0'/ \| \ diff --git a/Completion/User/_cvs b/Completion/User/_cvs index 24a21f8be..ba732cb63 100644 --- a/Completion/User/_cvs +++ b/Completion/User/_cvs @@ -514,7 +514,7 @@ builtin functions _cvs_directories >&- || _cvs_directories () { if [[ -d ${pref}CVS ]]; then _cvs_setup_direntries - (( $#entries )) && compgen "$@" -g "${(j:|:)${(@)entries:q}}" + (( $#entries )) && _files "$@" -g "${(j:|:)${(@)entries:q}}" else _files "$@" fi @@ -526,7 +526,7 @@ _cvs_files () { _cvs_setup_prefix if [[ -d ${pref}CVS ]]; then _cvs_setup_allentries - (( $#entries )) && compgen "$@" -g "${(j:|:)${(@)entries:q}}" + (( $#entries )) && _files "$@" -g "${(j:|:)${(@)entries:q}}" else _files "$@" fi @@ -538,7 +538,7 @@ _cvs_files_modified () { _cvs_setup_prefix if [[ -d ${pref}CVS ]]; then _cvs_setup_modentries - (( $#entries )) && compgen "$@" -g "${(j:|:)${(@)entries:q}}" + (( $#entries )) && _files "$@" -g "${(j:|:)${(@)entries:q}}" else _files "$@" fi @@ -572,8 +572,8 @@ _cvs_files_unmaintained () { omit=($_cvs_ignore_default ${entries:q} ${=cvsignore}) [[ -r ~/.cvsignore ]] && omit=($omit $(<~/.cvsignore)) [[ -r ${pref}.cvsignore ]] && omit=($omit $(<${pref}.cvsignore)) - compgen "$@" -g '*~(*/|)('${(j:|:)omit}')(D)' || - compgen "$@" -g '*~(*/|)('${(j:|:)${(@)entries:q}}')(D)' || + _files "$@" -g '*~(*/|)('${(j:|:)omit}')(D)' || + _files "$@" -g '*~(*/|)('${(j:|:)${(@)entries:q}}')(D)' || _cvs_directories "$@" else _files "$@" diff --git a/Completion/User/_gdb b/Completion/User/_gdb index 4e21d7448..fc882ff90 100644 --- a/Completion/User/_gdb +++ b/Completion/User/_gdb @@ -47,8 +47,7 @@ else if [[ $#w -gt 1 ]]; then _files && ret=0 _description expl 'process ID' - list=("${(F)${(@Mr:COLUMNS-1:)${(f)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ ]#${PREFIX}[0-9]#${SUFFIX}[ ]*${w[1]:t}}} -") + list=("${(@M)${(f)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ ]#${PREFIX}[0-9]#${SUFFIX}[ ]*${w[1]:t}*}") compadd "$expl[@]" -ld list - ${${${(M)${(f)"$(ps $=compconfig[ps_args] 2>/dev/null)"}:#*${w[1]:t}*}## #}%% *} && ret=0 return ret diff --git a/Completion/User/_man b/Completion/User/_man index fd5702227..50445edf6 100644 --- a/Completion/User/_man +++ b/Completion/User/_man @@ -1,6 +1,6 @@ #compdef man apropos whatis -setopt localoptions rcexpandparam extendedglob +setopt localoptions rcexpandparam local rep expl star approx diff --git a/Completion/User/_mh b/Completion/User/_mh index 3a2e53794..87ab8a18e 100644 --- a/Completion/User/_mh +++ b/Completion/User/_mh @@ -39,8 +39,7 @@ elif compset -P 1 '[+@]' || [[ "$prev" = -draftfolder ]]; then _description expl 'MH folder' _path_files "$expl[@]" -W mhpath -/ elif [[ "$prev" = -(editor|(whatnow|rmm|show|more)proc) ]]; then - _description expl command - compgen "$expl[@]" -c + _command_names -e elif [[ "$prev" = -file ]]; then _files elif [[ "$prev" = -(form|audit|filter) ]]; then @@ -52,7 +51,7 @@ elif [[ "$prev" = -(form|audit|filter) ]]; then mhfpath=($mymhdir $mhlib) _description expl 'MH template file' - compgen "$expl[@]" -W mhfpath -g '*(.)' + _files "$expl[@]" -W mhfpath -g '*(.)' elif [[ "$prev" = -(no|)cc ]]; then _description expl 'CC address' compadd "$expl[@]" all to cc me @@ -79,7 +78,7 @@ else compadd "$expl[@]" $(mark $foldnam 2>/dev/null | awk -F: '{ print $1 }') && ret=0 compadd "$expl[@]" reply next cur prev first last all unseen && ret=0 - compgen "$expl[@]" -W folddir -g '<->' && ret=0 + _files "$expl[@]" -W folddir -g '<->' && ret=0 return ret fi diff --git a/Completion/User/_nslookup b/Completion/User/_nslookup index 6b2585f3d..7bf97a8ad 100644 --- a/Completion/User/_nslookup +++ b/Completion/User/_nslookup @@ -19,8 +19,6 @@ # other characters than lower case letters, we try to call the function # `_nslookup_host'. -setopt localoptions extendedglob - local state expl ret=1 setopts setopts=( diff --git a/Completion/User/_tar b/Completion/User/_tar index 63838e83b..d779f6cf1 100644 --- a/Completion/User/_tar +++ b/Completion/User/_tar @@ -13,9 +13,6 @@ # tar itself (GNU tar) # - Things like --directory=... are also completed correctly. -emulate -LR zsh -setopt extendedglob - local _tar_cmd tf tmp del # First we collect in `_tar_cmd' single letter options describing what diff --git a/Completion/User/_urls b/Completion/User/_urls index 3b9afc4fd..1bc1ac76d 100644 --- a/Completion/User/_urls +++ b/Completion/User/_urls @@ -37,8 +37,6 @@ # name used by a user placing web pages within their home area. # e.g. compconf urls_localhttp=www:/usr/local/apache/htdocs:public_html -setopt localoptions extendedglob - local ipre scheme host user hosts ret=1 expl local urls_path="${compconfig[urls_path]:-${ZDOTDIR:-$HOME}/.zsh/urls}" local localhttp_servername="${${(@s.:.)compconfig[urls_localhttp]}[1]}" diff --git a/Completion/User/_users b/Completion/User/_users index a054ccc66..6d0b1ce2f 100644 --- a/Completion/User/_users +++ b/Completion/User/_users @@ -10,4 +10,4 @@ _description expl user [[ "${(t)users}" = *array* ]] && compadd "$expl[@]" "$@" - "$users[@]" && return 0 -compgen "$@" "$expl[@]" -u +compadd "$@" "$expl[@]" - "${(@k)userdirs}" diff --git a/Completion/User/_whereis b/Completion/User/_whereis index 42fc30969..6f11b516c 100644 --- a/Completion/User/_whereis +++ b/Completion/User/_whereis @@ -1,6 +1,3 @@ #compdef whereis -local expl - -_description expl command -compgen "$expl[@]" -m +_command_names -e diff --git a/Completion/User/_whois b/Completion/User/_whois index 827ebe627..580529a72 100644 --- a/Completion/User/_whois +++ b/Completion/User/_whois @@ -1,7 +1,6 @@ #compdef whois _whois () { - setopt localoptions extendedglob _whois_setup $_whois_comp } diff --git a/Completion/X/_x_font b/Completion/X/_x_font index f0b13c7a8..4ba21ddff 100644 --- a/Completion/X/_x_font +++ b/Completion/X/_x_font @@ -11,4 +11,4 @@ if (( ! $+_font_cache )); then fi _description expl font -compgen -M 'r:|-=* r:|=*' "$expl[@]" "$@" -S '' -k _font_cache +compadd -M 'r:|-=* r:|=*' "$expl[@]" "$@" -S '' - "$_font_cache[@]" diff --git a/Completion/X/_x_window b/Completion/X/_x_window index 118c7f131..65d2b72e2 100644 --- a/Completion/X/_x_window +++ b/Completion/X/_x_window @@ -1,7 +1,5 @@ #autoload -setopt localoptions extendedglob - local list expl list=( "${(@)${(M@)${(@f)$(xwininfo -root -tree)}:#[ ]#0x[0-9a-f]# \"*}##[ ]#}" ) diff --git a/Completion/X/_xmodmap b/Completion/X/_xmodmap index 29e584623..4e7a8bfc8 100644 --- a/Completion/X/_xmodmap +++ b/Completion/X/_xmodmap @@ -1,7 +1,5 @@ #compdef xmodmap -setopt localoptions extendedglob - local state line ret=1 typeset -A opt_args diff --git a/Doc/Makefile.in b/Doc/Makefile.in index 03ef6bbc0..a1443f2d5 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -57,11 +57,11 @@ Zsh/exec.yo Zsh/expn.yo \ Zsh/filelist.yo Zsh/files.yo Zsh/func.yo Zsh/grammar.yo Zsh/manual.yo \ Zsh/index.yo Zsh/intro.yo Zsh/invoke.yo Zsh/jobs.yo Zsh/metafaq.yo \ Zsh/modules.yo Zsh/mod_cap.yo \ -Zsh/mod_clone.yo Zsh/mod_comp1.yo Zsh/mod_compctl.yo Zsh/mod_complist.yo \ +Zsh/mod_clone.yo Zsh/mod_complete.yo Zsh/mod_compctl.yo Zsh/mod_complist.yo \ Zsh/mod_deltochar.yo Zsh/mod_example.yo Zsh/mod_files.yo \ Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo Zsh/mod_parameter.yo Zsh/mod_sched.yo \ -Zsh/mod_stat.yo Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/options.yo \ -Zsh/params.yo Zsh/prompt.yo Zsh/redirect.yo Zsh/restricted.yo \ +Zsh/mod_stat.yo Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/mod_zleparameter.yo \ +Zsh/options.yo Zsh/params.yo Zsh/prompt.yo Zsh/redirect.yo Zsh/restricted.yo \ Zsh/seealso.yo Zsh/zftpsys.yo Zsh/zle.yo # ========== DEPENDENCIES FOR BUILDING ========== diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo index 85993e917..b55aa91be 100644 --- a/Doc/Zsh/compctl.yo +++ b/Doc/Zsh/compctl.yo @@ -141,7 +141,7 @@ startlist() list([ tt(-fcFBdeaRGovNAIOPZEnbjrzu/12) ]) list([ tt(-k) var(array) ] [ tt(-g) var(globstring) ] \ [ tt(-s) var(subststring) ]) -list([ tt(-K) var(function) ] [ tt(-i) var(function) ]) +list([ tt(-K) var(function) ]) list([ tt(-Q) ] [ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ]) list([ tt(-W) var(file-prefix) ] [ tt(-H) var(num pattern) ]) list([ tt(-q) ] [ tt(-X) var(explanation) ] [ tt(-Y) var(explanation) ]) @@ -314,13 +314,6 @@ compctl -K whoson talk) completes only logged-on users after `tt(talk)'. Note that `tt(whoson)' must return an array, so `tt(reply=`users`)' would be incorrect. ) -item(tt(-i) var(function))( -Like tt(-K), but the function is invoked in a context like that for -completion widgets, see -ifzman(zmanref(zshzle))\ -ifnzman(noderef(The zle Module)) -for more information. -) item(tt(-H) var(num pattern))( The possible completions are taken from the last var(num) history lines. Only words matching var(pattern) are taken. If var(num) is diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index 5e313e904..f2f551639 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -13,7 +13,7 @@ completion behaviour and which may be bound to keystrokes, are referred to as `widgets'. Note that with the function-based completions described here, it -is also possible to use the `tt(compctl -M ...)' mechanism and the +is also possible to use the tt(compmatchers) special array to specify global matching control, such as case-insensitivity (`tt(abc)' will complete to a string beginning `tt(ABC)'), or wildcard behaviour on @@ -21,10 +21,11 @@ certain anchors (`tt(a-d)' will complete to abc-def as if there were a `tt(*)' after the `a'). See ifzman(the section `Matching Control' in zmanref(zshcompctl))\ ifnzman(noderef(Matching Control)) -for further details. Note that it is recommended to use the the -tt(compmatchers) array instead of tt(compctl) to define global -matchers when using the function based completion system, although -using tt(compctl -M) still works. +for further details. + +Also note that this completion system requires the tt(parameter) and +tt(zleparameter) modules to be linked into the shell or to be +dynamically loadable. startmenu() menu(Initialization) @@ -595,7 +596,7 @@ item(tt(_description))( This function gets two arguments: the name of an array and a string. It tests if the configuration key tt(description_format) is set and if it is, it stores some options in the array that can then be -given to the tt(compadd) and tt(compgen) builtin commands to make the +given to the tt(compadd) builtin command to make the value of the tt(description_format) key (with the sequence `tt(%d)' replaced by the string given as the second argument) be displayed above the matches added. These options also will make sure that the @@ -704,7 +705,7 @@ completion functions is that it allows completion of partial paths. For example, the string `tt(/u/i/s/sig)' may be completed to `tt(/usr/include/sys/signal.h)'. The options `tt(-/)', `tt(-f)', `tt(-g)', and `tt(-W)' are available as for the tt(compctl) -and tt(compgen) builtins; tt(-f) is the default. Additionally, the `tt(-F)' +and builtin command; tt(-f) is the default. Additionally, the `tt(-F)' option from the tt(compadd) builtin is supported, giving direct control over which filenames should be ignored as done by the tt(fignore) parameter in normal completion. @@ -744,12 +745,11 @@ extra options of tt(compadd). All arguments are passed unchanged to the tt(compadd) builtin. ) item(tt(_options))( -This can be used to complete option names. The difference to the -`tt(-o)' option of tt(compgen) is that this function uses a matching +This can be used to complete option names. It uses a matching specification that ignores a leading `tt(no)', ignores underscores and allows the user to type upper-case letters, making them match their lower-case counterparts. All arguments passed to this function are -propagated unchanged to the tt(compgen) builtin. +propagated unchanged to the tt(compadd) builtin. ) item(tt(_set_options) and tt(_unset_options))( These functions complete only set or unset options, with the same @@ -897,8 +897,8 @@ var(action) does not begin with an opening parentheses or brace, it will be split into separate words and executed. If the var(action) starts with a space, this list of words will be invoked unchanged, otherwise it will be invoked with some extra string placed after the -first word which can be given as arguments to the tt(compadd) and -tt(compgen) builtins and which make sure that the var(message) given +first word which can be given as arguments to the tt(compadd) builtin +command and which make sure that the var(message) given in the description will be shown above the matches. These arguments are taken from the array parameter tt(expl) which will be set up before executing the var(action) and hence may be used in it (normally @@ -1273,9 +1273,9 @@ completions. Unambiguous parts of the function name will be completed automatically (normal completion is not available at this point) until a space is typed. -Otherwise, any other string, for example `tt(-b)', will be passed as -arguments to tt(compgen) and should hence be a set of flags specifying the -type of completion. +Otherwise, any other string, will be passed as +arguments to tt(compadd) and should hence be an expression specifying +what should be completed. A very restricted set of editing commands is available when reading the string: `tt(DEL)' and `tt(^H)' delete the last character; `tt(^U)' deletes diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo index 605a6d35a..c22a7bb14 100644 --- a/Doc/Zsh/compwid.yo +++ b/Doc/Zsh/compwid.yo @@ -199,7 +199,7 @@ item(tt(matcher))( When completion is performed with a global match specification as defined by -indent(tt(compctl -M) var(spec1 ... specN ...)) +indent(tt(compmatchers=)tt(LPAR()) var(spec1 ... specN ...) tt(RPAR())) this gives the number of the specification string currently in use. In this case, matching is performed with each specification in turn. @@ -358,32 +358,6 @@ enditem() texinode(Builtin Commands)(Condition Codes)(Special Parameters)(Completion Widgets) sect(Builtin Commands) startitem() -cindex(completion widgets, generating matches with flags) -findex(compgen) -item(tt(compgen) var(flags ...))( - -Generate matches according to the given var(flags). These can be any of -the normal option flags (not those for extended completion) supported by -the tt(compctl) builtin command (see -ifzman(zmanref(zshcompctl))\ -ifnzman(noderef(Programmable Completion Using compctl))\ -) except for the tt(-t) and tt(-l) flags. However, when using the tt(-K) -flag, the function given as argument to it cannot access the command -line with the tt(read) builtin command. - -The matches will be generated in the same way as if the completion code -generated them directly from a tt(compctl)-definition with the same -flags. The completion code will consider only those matches as -possible completions that match the prefix and suffix from the special -parameters described above. These strings will be compared with the -generated matches using the normal matching rules and any matching -specifications given with the tt(-M) flag to tt(compgen) and the -global matching specifications given via the tt(compctl -M )var(spec1 ...) -builtin command. - -The return value is zero if at least one match was added and non-zero -otherwise. -) findex(compadd) cindex(completion widgets, adding specified matches) xitem(tt(compadd) [ tt(-qQfenUaml12) ] [ tt(-F) var(array) ]) @@ -425,8 +399,7 @@ The supported flags are: startitem() item(tt(-P) var(prefix))( -As for tt(compctl) and tt(compgen), it gives a string to -be inserted before the given var(words). The +This gives a string to be inserted before the given var(words). The string given is not considered as part of the match. ) item(tt(-S) var(suffix))( @@ -450,12 +423,6 @@ match. item(tt(-I) var(ignored-suffix))( Like tt(-i), but gives an ignored suffix. ) -item(tt(-y) var(array))( -This gives a number of strings to display instead of the matches. This -is like the tt(-y) option of the tt(compctl) builtin command but the -var(array) argument may only be the name of an array parameter or a -literal array in parentheses containing the strings to display. -) item(tt(-d) var(array))( This adds per-match display strings. The var(array) should contain one element per var(word) given. The completion code will then display the @@ -474,8 +441,7 @@ options. If it is given, the display strings are listed one per line, not arrayed in columns. ) item(tt(-J) var(name))( -As for tt(compctl) and tt(compgen), this gives the name of the group -of matches the words should be stored in. +Gives the name of the group of matches the words should be stored in. ) item(tt(-V) var(name))( Like tt(-J) but naming a unsorted group. @@ -491,12 +457,10 @@ duplicates be kept. Again, groups with and without this flag are in different name spaces. ) item(tt(-X) var(explanation))( -As for tt(compctl) and tt(compgen), the var(explanation) string will be -printed with the list of matches. +The var(explanation) string will be printed with the list of matches. ) item(tt(-q))( -As for tt(compctl) and tt(compgen), -the suffix given with tt(-S) will be automatically removed if +The suffix given with tt(-S) will be automatically removed if the next character typed is a blank or does not insert anything, or if the suffix consists of only one character and the next character typed is the same character. @@ -540,17 +504,15 @@ the tt(AUTO_PARAM_SLASH) and tt(AUTO_PARAM_KEYS) options be used for the matches. ) item(tt(-W) var(file-prefix))( -This option has the same meaning as for the tt(compctl) and -tt(compgen) builtin commands. Here, however, only one string may be -given, not an array. This string is a pathname that will be +This string is a pathname that will be prepended to each of the matches formed by the given var(words) together with any prefix specified by the tt(-p) option to form a complete filename for testing. Hence it is only useful if combined with the tt(-f) flag, as the tests will not otherwise be performed. ) item(tt(-a))( -In the tt(compctl) or tt(compgen) commands, the completion code normally -builds two sets of matches: the normal one where words with one of the +The completion code may +build two sets of matches: the normal one where words with one of the suffixes in the array parameter tt(fignore) are not considered possible matches, and the alternate set where the words excluded from the first set are stored. Normally only the matches in the first @@ -574,12 +536,12 @@ literal suffixes enclosed in parentheses and quoted, as in `tt(-F "(.o taken as the suffixes. ) item(tt(-Q))( -As for tt(compctl) and tt(compgen), this flag instructs the completion +This flag instructs the completion code not to quote any metacharacters in the words when inserting them into the command line. ) item(tt(-M) var(match-spec))( -As for tt(compctl) and tt(compgen), this gives local match specifications. +This gives local match specifications. Note that they will only be used if the tt(-U) option is not given. ) item(tt(-n))( @@ -593,7 +555,7 @@ functions that do the matching themselves. Note that with tt(compadd) this option does not automatically turn on menu completion if tt(AUTO_LIST) is set, unlike the corresponding option of -tt(compctl) and tt(compgen) commands. +tt(compctl). ) item(tt(-O) var(array))( If this option is given, the var(words) are em(not) added to the set of @@ -714,7 +676,6 @@ This forces anything up to and including the last equal sign to be ignored by the completion code. ) item(tt(compcall) [ tt(-TD) ])( - This allows the use of completions defined with the tt(compctl) builtin from within completion widgets. The list of matches will be generated as if one of the non-widget completion function (tt(complete-word), etc.) @@ -727,6 +688,8 @@ tt(-T) and/or tt(-D) flags can be passed to tt(compcall). The return value can be used to test if a matching tt(compctl) definition was found. It is non-zero if a tt(compctl) was found and zero otherwise. + +Note that this builtin is defined by the tt(compctl) module. ) enditem() @@ -773,7 +736,7 @@ After that the shell function tt(complete-history) will be invoked after typing control-X and TAB. The function should then generate the matches, e.g.: -example(complete-history LPAR()RPAR() { compgen -H 0 '' }) +example(complete-history LPAR()RPAR() { compadd - $historywords }) This function will complete words from the history matching the current word. diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 6b33c9a81..2220b8e45 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -706,6 +706,12 @@ for exported parameters item(tt(unique))( for arrays which keep only the first occurrence of duplicated values ) +item(tt(hide))( +for parameters with the `hide' flag +) +item(tt(special))( +for special parameters defined by the shell +) enditem() ) enditem() diff --git a/Doc/Zsh/manual.yo b/Doc/Zsh/manual.yo index c4691b89d..16a1bdec6 100644 --- a/Doc/Zsh/manual.yo +++ b/Doc/Zsh/manual.yo @@ -117,7 +117,7 @@ Zsh Modules menu(The cap Module) menu(The clone Module) -menu(The comp1 Module) +menu(The complete Module) menu(The compctl Module) menu(The complist Module) menu(The deltochar Module) @@ -130,6 +130,7 @@ menu(The sched Module) menu(The stat Module) menu(The zftp Module) menu(The zle Module) +menu(The zleparameter Module) endmenu() texinode(The Z Shell Manual)(Introduction)(Top)(Top) chapter(The Z Shell Manual) diff --git a/Doc/Zsh/mod_clone.yo b/Doc/Zsh/mod_clone.yo index 9bb7dc255..3914fffce 100644 --- a/Doc/Zsh/mod_clone.yo +++ b/Doc/Zsh/mod_clone.yo @@ -1,4 +1,4 @@ -texinode(The clone Module)(The comp1 Module)(The cap Module)(Zsh Modules) +texinode(The clone Module)(The complete Module)(The cap Module)(Zsh Modules) sect(The clone Module) The tt(clone) module makes available one builtin command: diff --git a/Doc/Zsh/mod_compctl.yo b/Doc/Zsh/mod_compctl.yo index 785767e45..b69ae1e7d 100644 --- a/Doc/Zsh/mod_compctl.yo +++ b/Doc/Zsh/mod_compctl.yo @@ -1,12 +1,12 @@ -texinode(The compctl Module)(The complist Module)(The comp1 Module)(Zsh Modules) +texinode(The compctl Module)(The complist Module)(The complete Module)(Zsh Modules) sect(The compctl Module) -The tt(compctl) module makes available several builtin commands. tt(compctl), -is the standard way to control completions for ZLE. See +The tt(compctl) module makes available two builtin commands. tt(compctl), +is the old, deprecated way to control completions for ZLE. See ifzman(zmanref(zshcompctl))\ ifnzman(noderef(Programmable Completion Using compctl))\ . -The other builtin commands can be used in user-defined completion widgets, -see +The other builtin command, tt(compcall) can be used in user-defined +completion widgets, see ifzman(zmanref(zshcompwid))\ ifnzman(noderef(Completion Widgets))\ . diff --git a/Doc/Zsh/mod_complete.yo b/Doc/Zsh/mod_complete.yo new file mode 100644 index 000000000..558bbc4ae --- /dev/null +++ b/Doc/Zsh/mod_complete.yo @@ -0,0 +1,7 @@ +texinode(The complete Module)(The compctl Module)(The clone Module)(Zsh Modules) +sect(The complete Module) +The tt(compctl) module makes available several builtin commands which +can be used in user-defined completion widgets, see +ifzman(zmanref(zshcompwid))\ +ifnzman(noderef(Completion Widgets))\ +. diff --git a/Doc/Zsh/mod_computil.yo b/Doc/Zsh/mod_computil.yo index fc1959bf1..cc0abc38e 100644 --- a/Doc/Zsh/mod_computil.yo +++ b/Doc/Zsh/mod_computil.yo @@ -12,7 +12,7 @@ completion functions. In short, these builtin commands are: startitem() item(tt(compdisplay) var(name) var(string) var(defs) ...)( -The var(defs) are strings should be of the form +The var(defs) are strings which should be of the form `var(str)tt(:)var(descr)' (the intended use is that the var(descr) describes the var(str)) and tt(compdisplay) will convert them to strings in which the colon is replaced by the var(string) given as the diff --git a/Doc/Zsh/mod_parameter.yo b/Doc/Zsh/mod_parameter.yo index 893bd03d9..0cf44d4b1 100644 --- a/Doc/Zsh/mod_parameter.yo +++ b/Doc/Zsh/mod_parameter.yo @@ -2,7 +2,7 @@ texinode(The parameter Module)(The sched Module)(The mathfunc Module)(Zsh Module sect(The parameter Module) cindex(parameters, special) The tt(parameter) module gives access to some of the internal hash -tables used by the shell, by defining four special associative arrays. +tables used by the shell by defining some special parameters. startitem() vindex(options) @@ -32,6 +32,31 @@ key in it is like defining a function with the name given by the key and the body given by the value. Unsetting a key removes the definition for the function named by the key. ) +vindex(builtins) +item(tt(builtins))( +This association gives information about the builtin commands +currently known. The keys are the names of the builtin commands and +the values are either `tt(undefined)' for builtin commands that will +automatically be loaded from a module if invoked or `tt(defined)' for +builtin commands that are already loaded. Also, the string +`tt( )' will be prepended to the value if the builtin +command is currently disabled. +) +vindex(reswords) +item(tt(reswords))( +This association maps the reserved words to one of `tt()' or +`tt()' for enabled and disabled reserved words, respectively. +) +vindex(raliases) +item(tt(raliases))( +This maps the names of the regular aliases currently defined to their +expansions. For disabled aliases the string `tt()' is +prepended to their value. +) +vindex(galiases) +item(tt(galiases))( +Like tt(raliases), but for global aliases. +) vindex(parameters) item(tt(parameters))( The keys in this associative array are the names of the parameters @@ -61,4 +86,39 @@ A normal array holding the elements of the directory stack. Note that the output of the tt(dirs) builtin command includes one more directory, the current working directory. ) +vindex(history) +item(tt(history))( +This association maps history event numbers to the full history lines. +) +vindex(historywords) +item(tt(historywords))( +A special array containing the words stored in the history. +) +vindex(jobtexts) +item(tt(jobtexts))( +This association maps job numbers to the texts of the command lines +that were used to start the jobs. +) +vindex(jobstates) +item(tt(jobstates))( +This association gives information about the states of the jobs +currently known. The keys are the job numbers and the values are +strings of the form +`var(job-state):var(pid)tt(=)var(state)tt(...)'. The var(job-state) +gives the state the whole job is currently in, one of `tt(running)', +`tt(suspended)', or `tt(done)'. This is follwed by one +`var(pid)tt(=)var(state)' for every process in the job. The var(pid)s +are, of course, the process IDs and the var(state) describes the state +of that process. +) +vindex(nameddirs) +item(tt(nameddirs))( +This association maps the names of named directories to the pathnames +they stand for. +) +vindex(userdirs) +item(tt(userdirs))( +This association maps user names to the pathnames of their home +directories. +) enditem() diff --git a/Doc/Zsh/mod_zle.yo b/Doc/Zsh/mod_zle.yo index 95aa27535..a346ab279 100644 --- a/Doc/Zsh/mod_zle.yo +++ b/Doc/Zsh/mod_zle.yo @@ -1,4 +1,4 @@ -texinode(The zle Module)()(The zftp Module)(Zsh Modules) +texinode(The zle Module)(The zleparameter Module)(The zftp Module)(Zsh Modules) sect(The zle Module) The tt(zle) module contains the Zsh Line Editor. See ifzman(zmanref(zshzle))\ diff --git a/Doc/Zsh/mod_zleparameter.yo b/Doc/Zsh/mod_zleparameter.yo new file mode 100644 index 000000000..cf79668e4 --- /dev/null +++ b/Doc/Zsh/mod_zleparameter.yo @@ -0,0 +1,28 @@ +texinode(The zleparameter Module)()(The zle Module)(Zsh Modules) +sect(The zleparameter Module) +cindex(parameters, special) +The tt(zleparameter) module defines two special parameters that can be +used to access internal information of the Zsh Line Editor (see +ifzman(zmanref(zshzle))\ +ifnzman(noderef(Zsh Line Editor))\ +). + +startitem() +vindex(zlekeymaps) +item(tt(zlekeymaps))( +This array contains the names of the keymaps currently defined. +) +vindex(zlewidgets) +item(tt(zlewidgets))( +This associative array contains one entry per widget defined. The name +of the widget is the key and the value gives information about the +widget. It is either the string `tt(builtin)' for builtin widgets, a +string of the form `tt(user:)var(name)' for user-defined widgets, +where var(name) is the name of the shell function implementing the +widget, or it is a string of the form +`tt(completion:)var(type)tt(:)var(name)', for completion widgets. In +the last case var(type) is the name of the builtin widgets the +completion widget imitates in its behavior and var(name) is the name +of the shell function implementing the completion widget. +) +enditem() diff --git a/Doc/Zsh/modules.yo b/Doc/Zsh/modules.yo index b06edfda2..9ce81c264 100644 --- a/Doc/Zsh/modules.yo +++ b/Doc/Zsh/modules.yo @@ -15,9 +15,6 @@ Builtins for manipulating POSIX.1e (POSIX.6) capability (privilege) sets. item(tt(clone))( A builtin that can clone a running shell onto another terminal. ) -item(tt(comp1))( -Base of the completion system. Used by the tt(compctl) and tt(zle) modules. -) item(tt(compctl))( The tt(compctl) builtin for controlling completion and the builtins for completion widgets. @@ -59,11 +56,14 @@ A builtin FTP client. item(tt(zle))( The Zsh Line Editor, including the tt(bindkey) and tt(vared) builtins. ) +item(tt(zleparameter))( +Access to internals of the Zsh Line Editor via parameters. +) enditem() startmenu() menu(The cap Module) menu(The clone Module) -menu(The comp1 Module) +menu(The complete Module) menu(The compctl Module) menu(The complist Module) menu(The computil Module) @@ -77,10 +77,11 @@ menu(The sched Module) menu(The stat Module) menu(The zftp Module) menu(The zle Module) +menu(The zleparameter Module) endmenu() includefile(Zsh/mod_cap.yo) includefile(Zsh/mod_clone.yo) -includefile(Zsh/mod_comp1.yo) +includefile(Zsh/mod_complete.yo) includefile(Zsh/mod_compctl.yo) includefile(Zsh/mod_complist.yo) includefile(Zsh/mod_computil.yo) @@ -94,3 +95,4 @@ includefile(Zsh/mod_sched.yo) includefile(Zsh/mod_stat.yo) includefile(Zsh/mod_zftp.yo) includefile(Zsh/mod_zle.yo) +includefile(Zsh/mod_zleparameter.yo) diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 91e993c73..edad0e468 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -30,6 +30,10 @@ #include "parameter.mdh" #include "parameter.pro" +/* This says if we are cleaning up when the module is unloaded. */ + +static int incleanup; + /* Empty dummy function for special hash parameters. */ /**/ @@ -116,6 +120,10 @@ paramtypestr(Param pm) val = dyncat(val, "-export"); if (f & PM_UNIQUE) val = dyncat(val, "-unique"); + if (f & PM_HIDE) + val = dyncat(val, "-hide"); + if (f & PM_SPECIAL) + val = dyncat(val, "-special"); } else val = dupstring(""); @@ -144,7 +152,7 @@ getpmparameter(HashTable ht, char *name) !(rpm->flags & PM_UNSET)) pm->u.str = paramtypestr(rpm); else { - pm->u.str = ""; + pm->u.str = dupstring(""); pm->flags |= PM_UNSET; } } LASTALLOC; @@ -186,7 +194,7 @@ static void setpmcommand(Param pm, char *value) { if (isset(RESTRICTED)) - zwarnnam(NULL, "restricted: %s", value, 0); + zwarn("restricted: %s", value, 0); else { Cmdnam cn = zcalloc(sizeof(*cn)); @@ -270,7 +278,7 @@ getpmcommand(HashTable ht, char *name) strcat(pm->u.str, name); } } else { - pm->u.str = ""; + pm->u.str = dupstring(""); pm->flags |= PM_UNSET; } } LASTALLOC; @@ -328,8 +336,12 @@ setfunction(char *name, char *val) char *value = dupstring(val); Shfunc shf; List list; - int sn; + int sn, dis = 0; + if (strpfx(" ", val)) { + strcpy(val, val + 11); + dis = DISABLED; + } val = metafy(val, strlen(val), META_REALLOC); HEAPALLOC { @@ -337,14 +349,14 @@ setfunction(char *name, char *val) } LASTALLOC; if (!list || list == &dummy_list) { - zwarnnam(NULL, "invalid function definition", value, 0); + zwarn("invalid function definition", value, 0); zsfree(val); return; } PERMALLOC { shf = (Shfunc) zalloc(sizeof(*shf)); shf->funcdef = (List) dupstruct(list); - shf->flags = 0; + shf->flags = dis; if (!strncmp(name, "TRAP", 4) && (sn = getsignum(name + 4)) != -1) { @@ -422,23 +434,25 @@ getpmfunction(HashTable ht, char *name) pm->old = NULL; pm->level = 0; - if ((shf = (Shfunc) shfunctab->getnode(shfunctab, name))) { - if (shf->flags & PM_UNDEFINED) - pm->u.str = tricat("builtin autoload -X", - ((shf->flags & PM_UNALIASED)? "U" : ""), - ((shf->flags & PM_TAGGED)? "t" : "")); - else { + if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name))) { + if (shf->flags & PM_UNDEFINED) { + pm->u.str = dyncat("builtin autoload -X", + ((shf->flags & PM_UNALIASED) ? + ((shf->flags & PM_TAGGED) ? "Ut" : "U") : + ((shf->flags & PM_TAGGED) ? "t" : ""))); + } else { char *t = getpermtext((void *) dupstruct((void *) shf->funcdef)), *h; - h = dupstring(t); + h = ((shf->flags & DISABLED) ? + dyncat(" ", t) : dupstring(t)); zsfree(t); unmetafy(h, NULL); pm->u.str = h; } } else { - pm->u.str = ""; + pm->u.str = dupstring(""); pm->flags |= PM_UNSET; } } LASTALLOC; @@ -471,14 +485,19 @@ scanpmfunctions(HashTable ht, ScanFunc func, int flags) if (func != scancountparams) { if (((Shfunc) hn)->flags & PM_UNDEFINED) { Shfunc shf = (Shfunc) hn; - pm.u.str = tricat("builtin autoload -X", - ((shf->flags & PM_UNALIASED)? "U" : ""), - ((shf->flags & PM_TAGGED)? "t" : "")); + pm.u.str = + dyncat("builtin autoload -X", + ((shf->flags & PM_UNALIASED) ? + ((shf->flags & PM_TAGGED) ? "Ut" : "U") : + ((shf->flags & PM_TAGGED) ? "t" : ""))); } else { char *t = getpermtext((void *) dupstruct((void *) ((Shfunc) hn)->funcdef)); - unmetafy((pm.u.str = dupstring(t)), NULL); + pm.u.str = ((hn->flags & DISABLED) ? + dyncat(" ", t) : + dupstring(t)); + unmetafy(pm.u.str, NULL); zsfree(t); } } @@ -487,6 +506,136 @@ scanpmfunctions(HashTable ht, ScanFunc func, int flags) } } +/* Functions for the builtins special parameter. */ + +/**/ +static HashNode +getpmbuiltin(HashTable ht, char *name) +{ + Param pm = NULL; + Builtin bn; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((bn = (Builtin) builtintab->getnode2(builtintab, name))) { + char *t = ((bn->handlerfunc || (bn->flags & BINF_PREFIX)) ? + "defined" : "undefined"); + + pm->u.str = ((bn->flags & DISABLED) ? + dyncat(" ", t) : dupstring(t)); + } else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; +} + +/**/ +static void +scanpmbuiltins(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i; + HashNode hn; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < builtintab->hsize; i++) + for (hn = builtintab->nodes[i]; hn; hn = hn->next) { + pm.nam = hn->nam; + if (func != scancountparams) { + char *t = ((((Builtin) hn)->handlerfunc || + (hn->flags & BINF_PREFIX)) ? + "defined" : "undefined"); + + pm.u.str = ((((Builtin) hn)->flags & DISABLED) ? + dyncat(" ", t) : dupstring(t)); + } + func((HashNode) &pm, flags); + } +} + +/* Functions for the reswords special parameter. */ + +/**/ +static HashNode +getpmresword(HashTable ht, char *name) +{ + Param pm = NULL; + HashNode hn; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((hn = reswdtab->getnode2(reswdtab, name))) + pm->u.str = dupstring((hn->flags & DISABLED) ? + "" : ""); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; +} + +/**/ +static void +scanpmreswords(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i; + HashNode hn; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < reswdtab->hsize; i++) + for (hn = reswdtab->nodes[i]; hn; hn = hn->next) { + pm.nam = hn->nam; + if (func != scancountparams) + pm.u.str = dupstring((hn->flags & DISABLED) ? + "" : ""); + func((HashNode) &pm, flags); + } +} + /* Functions for the options special parameter. */ /**/ @@ -496,11 +645,11 @@ setpmoption(Param pm, char *value) int n; if (!value || (strcmp(value, "on") && strcmp(value, "off"))) - zwarnnam(NULL, "invalid value: %s", value, 0); + zwarn("invalid value: %s", value, 0); else if (!(n = optlookup(pm->nam))) - zwarnnam(NULL, "no such option: %s", pm->nam, 0); + zwarn("no such option: %s", pm->nam, 0); else if (dosetopt(n, (value && strcmp(value, "off")), 0)) - zwarnnam(NULL, "can't change option: %s", pm->nam, 0); + zwarn("can't change option: %s", pm->nam, 0); zsfree(value); } @@ -511,9 +660,9 @@ unsetpmoption(Param pm, int exp) int n; if (!(n = optlookup(pm->nam))) - zwarnnam(NULL, "no such option: %s", pm->nam, 0); + zwarn("no such option: %s", pm->nam, 0); else if (dosetopt(n, 0, 0)) - zwarnnam(NULL, "can't change option: %s", pm->nam, 0); + zwarn("can't change option: %s", pm->nam, 0); } /**/ @@ -538,10 +687,10 @@ setpmoptions(Param pm, HashTable ht) val = getstrvalue(&v); if (!val || (strcmp(val, "on") && strcmp(val, "off"))) - zwarnnam(NULL, "invalid value: %s", val, 0); + zwarn("invalid value: %s", val, 0); else if (dosetopt(optlookup(hn->nam), (val && strcmp(val, "off")), 0)) - zwarnnam(NULL, "can't change option: %s", hn->nam, 0); + zwarn("can't change option: %s", hn->nam, 0); } deleteparamtable(ht); } @@ -569,7 +718,7 @@ getpmoption(HashTable ht, char *name) if ((n = optlookup(name))) pm->u.str = dupstring(opts[n] ? "on" : "off"); else { - pm->u.str = ""; + pm->u.str = dupstring(""); pm->flags |= PM_UNSET; } } LASTALLOC; @@ -598,7 +747,7 @@ scanpmoptions(HashTable ht, ScanFunc func, int flags) for (i = 0; i < optiontab->hsize; i++) for (hn = optiontab->nodes[i]; hn; hn = hn->next) { pm.nam = hn->nam; - pm.u.str = opts[((Optname) hn)->optno] ? "on" : "off"; + pm.u.str = dupstring(opts[((Optname) hn)->optno] ? "on" : "off"); func((HashNode) &pm, flags); } } @@ -698,9 +847,9 @@ getpmmodule(HashTable ht, char *name) } #endif if (type) - pm->u.str = type; + pm->u.str = dupstring(type); else { - pm->u.str = ""; + pm->u.str = dupstring(""); pm->flags |= PM_UNSET; } } LASTALLOC; @@ -730,29 +879,29 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags) pm.old = NULL; pm.level = 0; + pm.u.str = dupstring("builtin"); for (node = firstnode(bltinmodules); node; incnode(node)) { pm.nam = (char *) getdata(node); addlinknode(done, pm.nam); - pm.u.str = "builtin"; func((HashNode) &pm, flags); } #ifdef DYNAMIC + pm.u.str = dupstring("loaded"); for (node = firstnode(modules); node; incnode(node)) { m = (Module) getdata(node); if (m->handle && !(m->flags & MOD_UNLOAD)) { pm.nam = m->nam; addlinknode(done, pm.nam); - pm.u.str = "loaded"; func((HashNode) &pm, flags); } } + pm.u.str = dupstring("autoloaded"); for (i = 0; i < builtintab->hsize; i++) for (hn = builtintab->nodes[i]; hn; hn = hn->next) { if (!(((Builtin) hn)->flags & BINF_ADDED) && !findmodnode(done, ((Builtin) hn)->optstr)) { pm.nam = ((Builtin) hn)->optstr; addlinknode(done, pm.nam); - pm.u.str = "autoloaded"; func((HashNode) &pm, flags); } } @@ -760,7 +909,6 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags) if (p->module && !findmodnode(done, p->module)) { pm.nam = p->module; addlinknode(done, pm.nam); - pm.u.str = "autoloaded"; func((HashNode) &pm, flags); } for (i = 0; i < realparamtab->hsize; i++) @@ -769,7 +917,6 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags) !findmodnode(done, ((Param) hn)->u.str)) { pm.nam = ((Param) hn)->u.str; addlinknode(done, pm.nam); - pm.u.str = "autoloaded"; func((HashNode) &pm, flags); } } @@ -781,12 +928,16 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags) static void dirssetfn(Param pm, char **x) { - PERMALLOC { - freelinklist(dirstack, freestr); - dirstack = newlinklist(); - while (x && *x) - addlinknode(dirstack, ztrdup(*x++)); - } LASTALLOC; + if (!incleanup) { + PERMALLOC { + freelinklist(dirstack, freestr); + dirstack = newlinklist(); + while (x && *x) + addlinknode(dirstack, ztrdup(*x++)); + } LASTALLOC; + } + if (x) + freearray(x); } static char ** @@ -803,95 +954,795 @@ dirsgetfn(Param pm) return ret; } -/* Names and Params for the special parameters. */ +/* Functions for the history special parameter. */ + +/**/ +static HashNode +getpmhistory(HashTable ht, char *name) +{ + Param pm = NULL; + Histent he; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((he = quietgethist(atoi(name)))) + pm->u.str = dupstring(he->text); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; +} + +/**/ +static void +scanpmhistory(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i = addhistnum(curhist, -1, HIST_FOREIGN); + Histent he = quietgethistent(i, GETHIST_UPWARD); + char buf[40]; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + while (he) { + if (func != scancountparams) { + sprintf(buf, "%d", he->histnum); + pm.nam = dupstring(buf); + pm.u.str = dupstring(he->text); + } + func((HashNode) &pm, flags); + + he = up_histent(he); + } +} + +/* Function for the historywords special parameter. */ + +static char ** +histwgetfn(Param pm) +{ + char **ret, **p, *h, *e, sav; + LinkList l = newlinklist(); + LinkNode n; + int i = addhistnum(curhist, -1, HIST_FOREIGN), iw; + Histent he = quietgethistent(i, GETHIST_UPWARD); + + while (he) { + for (iw = he->nwords - 1; iw >= 0; iw--) { + h = he->text + he->words[iw * 2]; + e = he->text + he->words[iw * 2 + 1]; + sav = *e; + *e = '\0'; + addlinknode(l, dupstring(h)); + *e = sav; + } + he = up_histent(he); + } + ret = (char **) zhalloc((countlinknodes(l) + 1) * sizeof(char *)); + + for (p = ret, n = firstnode(l); n; incnode(n), p++) + *p = (char *) getdata(n); + *p = NULL; + + return ret; +} + +/* Functions for the jobtexts special parameter. */ + +static char * +pmjobtext(int job) +{ + Process pn; + int len = 1; + char *ret; + + for (pn = jobtab[job].procs; pn; pn = pn->next) + len += strlen(pn->text) + 3; -#define PAR_NAM "parameters" -#define CMD_NAM "commands" -#define FUN_NAM "functions" -#define OPT_NAM "options" -#define MOD_NAM "modules" -#define DIR_NAM "dirstack" + ret = (char *) zhalloc(len); + ret[0] = '\0'; -static Param parpm, cmdpm, funpm, optpm, modpm, dirpm; + for (pn = jobtab[job].procs; pn; pn = pn->next) { + strcat(ret, pn->text); + if (pn->next) + strcat(ret, " | "); + } + return ret; +} /**/ -int -setup_parameter(Module m) +static HashNode +getpmjobtext(HashTable ht, char *name) { - return 0; + Param pm = NULL; + int job; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + + if ((job = atoi(name)) >= 1 && job < MAXJOB && + jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobtext(job); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; } /**/ -int -boot_parameter(Module m) +static void +scanpmjobtexts(HashTable ht, ScanFunc func, int flags) { - /* Create the special associative arrays. - * As an example for autoloaded parameters, this is probably a bad - * example, because the zsh core doesn't support creation of - * special hashes, yet. */ + struct param pm; + int job; + char buf[40]; - unsetparam(PAR_NAM); - if (!(parpm = createspecialhash(PAR_NAM, getpmparameter, - scanpmparameters))) - return 1; - parpm->flags |= PM_READONLY; - unsetparam(CMD_NAM); - if (!(cmdpm = createspecialhash(CMD_NAM, getpmcommand, - scanpmcommands))) - return 1; - cmdpm->sets.hfn = setpmcommands; - unsetparam(FUN_NAM); - if (!(funpm = createspecialhash(FUN_NAM, getpmfunction, - scanpmfunctions))) - return 1; - funpm->sets.hfn = setpmfunctions; - unsetparam(OPT_NAM); - if (!(optpm = createspecialhash(OPT_NAM, getpmoption, - scanpmoptions))) - return 1; - optpm->sets.hfn = setpmoptions; - unsetparam(MOD_NAM); - if (!(modpm = createspecialhash(MOD_NAM, getpmmodule, - scanpmmodules))) - return 1; - modpm->flags |= PM_READONLY; - unsetparam(DIR_NAM); - if (!(dirpm = createparam(DIR_NAM, - PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_REMOVABLE))) - return 1; - dirpm->sets.afn = dirssetfn; - dirpm->gets.afn = dirsgetfn; - dirpm->unsetfn = stdunsetfn; + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; - return 0; + for (job = 1; job < MAXJOB; job++) { + if (jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) { + if (func != scancountparams) { + sprintf(buf, "%d", job); + pm.nam = dupstring(buf); + pm.u.str = pmjobtext(job); + } + func((HashNode) &pm, flags); + } + } } -#ifdef MODULE +/* Functions for the jobstates special parameter. */ + +static char * +pmjobstate(int job) +{ + Process pn; + char buf[256], buf2[128], *ret, *state; + + if (jobtab[job].stat & STAT_DONE) + ret = dupstring("done"); + else if (jobtab[job].stat & STAT_STOPPED) + ret = dupstring("suspended"); + else + ret = dupstring("running"); + + for (pn = jobtab[job].procs; pn; pn = pn->next) { + + if (pn->status == SP_RUNNING) + state = "running"; + else if (WIFEXITED(pn->status)) { + if (WEXITSTATUS(pn->status)) + sprintf((state = buf2), "exit %d", (pn->status)); + else + state = "done"; + } else if (WIFSTOPPED(pn->status)) + state = sigmsg[WSTOPSIG(pn->status)]; + else if (WCOREDUMP(pn->status)) + sprintf((state = buf2), "%s (core dumped)", + sigmsg[WTERMSIG(pn->status)]); + else + state = sigmsg[WTERMSIG(pn->status)]; + + sprintf(buf, ":%d=%s", pn->pid, state); + + ret = dyncat(ret, buf); + } + return ret; +} /**/ -int -cleanup_parameter(Module m) +static HashNode +getpmjobstate(HashTable ht, char *name) { - Param pm; + Param pm = NULL; + int job; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; - /* Remove the special parameters if they are still the same. */ + if ((job = atoi(name)) >= 1 && job < MAXJOB && + jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobstate(job); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; - if ((pm = (Param) paramtab->getnode(paramtab, PAR_NAM)) && pm == parpm) { - pm->flags &= ~PM_READONLY; - unsetparam_pm(pm, 0, 1); - } - if ((pm = (Param) paramtab->getnode(paramtab, CMD_NAM)) && pm == cmdpm) - unsetparam_pm(pm, 0, 1); - if ((pm = (Param) paramtab->getnode(paramtab, FUN_NAM)) && pm == funpm) - unsetparam_pm(pm, 0, 1); - if ((pm = (Param) paramtab->getnode(paramtab, OPT_NAM)) && pm == optpm) - unsetparam_pm(pm, 0, 1); - if ((pm = (Param) paramtab->getnode(paramtab, MOD_NAM)) && pm == modpm) { - pm->flags &= ~PM_READONLY; - unsetparam_pm(pm, 0, 1); + return (HashNode) pm; +} + +/**/ +static void +scanpmjobstates(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int job; + char buf[40]; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (job = 1; job < MAXJOB; job++) { + if (jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) { + if (func != scancountparams) { + sprintf(buf, "%d", job); + pm.nam = dupstring(buf); + pm.u.str = pmjobstate(job); + } + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the nameddirs special parameter. */ + +/**/ +static void +setpmnameddir(Param pm, char *value) +{ + if (!value || *value != '/' || strlen(value) >= PATH_MAX) + zwarn("invalid value: %s", value, 0); + else + adduserdir(pm->nam, value, 0, 1); + zsfree(value); +} + +/**/ +static void +unsetpmnameddir(Param pm, int exp) +{ + HashNode hd = nameddirtab->removenode(nameddirtab, pm->nam); + + if (hd) + nameddirtab->freenode(hd); +} + +/**/ +static void +setpmnameddirs(Param pm, HashTable ht) +{ + int i; + HashNode hn, next, hd; + + for (i = 0; i < nameddirtab->hsize; i++) + for (hn = nameddirtab->nodes[i]; hn; hn = next) { + next = hn->next; + if (!(((Nameddir) hn)->flags & ND_USERNAME) && + (hd = nameddirtab->removenode(nameddirtab, hn->nam))) + nameddirtab->freenode(hd); + } + + if (!ht) + return; + + for (i = 0; i < ht->hsize; i++) + for (hn = ht->nodes[i]; hn; hn = hn->next) { + struct value v; + char *val; + + v.isarr = v.inv = v.a = 0; + v.b = -1; + v.arr = NULL; + v.pm = (Param) hn; + + if (!(val = getstrvalue(&v)) || *val != '/' || + strlen(val) >= PATH_MAX) + zwarn("invalid value: %s", val, 0); + else + adduserdir(hn->nam, val, 0, 1); + } + + /* The INTERACTIVE stuff ensures that the dirs are not immediatly removed + * when the sub-pms are deleted. */ + + i = opts[INTERACTIVE]; + opts[INTERACTIVE] = 0; + deleteparamtable(ht); + opts[INTERACTIVE] = i; +} + +/**/ +static HashNode +getpmnameddir(HashTable ht, char *name) +{ + Param pm = NULL; + Nameddir nd; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR; + pm->sets.cfn = setpmnameddir; + pm->gets.cfn = strgetfn; + pm->unsetfn = unsetpmnameddir; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) && + !(nd->flags & ND_USERNAME)) + pm->u.str = dupstring(nd->dir); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; +} + +/**/ +static void +scanpmnameddirs(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i; + HashNode hn; + Nameddir nd; + + pm.flags = PM_SCALAR; + pm.sets.cfn = setpmnameddir; + pm.gets.cfn = strgetfn; + pm.unsetfn = unsetpmnameddir; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < nameddirtab->hsize; i++) + for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) { + if (!((nd = (Nameddir) hn)->flags & ND_USERNAME)) { + pm.nam = hn->nam; + if (func != scancountparams) + pm.u.str = dupstring(nd->dir); + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the userdirs special parameter. */ + +/**/ +static HashNode +getpmuserdir(HashTable ht, char *name) +{ + Param pm = NULL; + Nameddir nd; + + nameddirtab->filltable(nameddirtab); + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) && + (nd->flags & ND_USERNAME)) + pm->u.str = dupstring(nd->dir); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; +} + +/**/ +static void +scanpmuserdirs(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i; + HashNode hn; + Nameddir nd; + + nameddirtab->filltable(nameddirtab); + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < nameddirtab->hsize; i++) + for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) { + if ((nd = (Nameddir) hn)->flags & ND_USERNAME) { + pm.nam = hn->nam; + if (func != scancountparams) + pm.u.str = dupstring(nd->dir); + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the regularaliases and globalaliases special parameters. */ + +/**/ +static void +setpmralias(Param pm, char *value) +{ + int dis = 0; + + if (strpfx(" ", value)) { + strcpy(value, value + 11); + dis = DISABLED; + } + aliastab->addnode(aliastab, ztrdup(pm->nam), createaliasnode(value, dis)); +} + +/**/ +static void +setpmgalias(Param pm, char *value) +{ + int dis = 0; + + if (strpfx(" ", value)) { + strcpy(value, value + 11); + dis = DISABLED; + } + aliastab->addnode(aliastab, ztrdup(pm->nam), + createaliasnode(value, dis | ALIAS_GLOBAL)); +} + +/**/ +static void +unsetpmalias(Param pm, int exp) +{ + HashNode hd = aliastab->removenode(aliastab, pm->nam); + + if (hd) + aliastab->freenode(hd); +} + +/**/ +static void +setpmaliases(Param pm, HashTable ht, int global) +{ + int i; + HashNode hn, next, hd; + + for (i = 0; i < aliastab->hsize; i++) + for (hn = aliastab->nodes[i]; hn; hn = next) { + next = hn->next; + if (((global && (((Alias) hn)->flags & ALIAS_GLOBAL)) || + (!global && !(((Alias) hn)->flags & ALIAS_GLOBAL))) && + (hd = aliastab->removenode(aliastab, hn->nam))) + aliastab->freenode(hd); + } + + if (!ht) + return; + + for (i = 0; i < ht->hsize; i++) + for (hn = ht->nodes[i]; hn; hn = hn->next) { + struct value v; + char *val; + + v.isarr = v.inv = v.a = 0; + v.b = -1; + v.arr = NULL; + v.pm = (Param) hn; + + if ((val = getstrvalue(&v))) + aliastab->addnode(aliastab, ztrdup(hn->nam), + createaliasnode(ztrdup(val), + (global ? ALIAS_GLOBAL : 0))); + } + deleteparamtable(ht); +} + +/**/ +static void +setpmraliases(Param pm, HashTable ht) +{ + setpmaliases(pm, ht, 0); +} + +/**/ +static void +setpmgaliases(Param pm, HashTable ht) +{ + setpmaliases(pm, ht, 1); +} + +/**/ +static HashNode +getpmalias(HashTable ht, char *name, int global) +{ + Param pm = NULL; + Alias al; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR; + pm->sets.cfn = (global ? setpmgalias : setpmralias); + pm->gets.cfn = strgetfn; + pm->unsetfn = unsetpmalias; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((al = (Alias) aliastab->getnode2(aliastab, name)) && + ((global && (al->flags & ALIAS_GLOBAL)) || + (!global && !(al->flags & ALIAS_GLOBAL)))) + pm->u.str = ((al->flags & DISABLED) ? + dyncat(" ", al->text) : + dupstring(al->text)); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; +} + +/**/ +static HashNode +getpmralias(HashTable ht, char *name) +{ + return getpmalias(ht, name, 0); +} + +/**/ +static HashNode +getpmgalias(HashTable ht, char *name) +{ + return getpmalias(ht, name, 1); +} + +/**/ +static void +scanpmaliases(HashTable ht, ScanFunc func, int flags, int global) +{ + struct param pm; + int i; + HashNode hn; + Alias al; + + pm.flags = PM_SCALAR; + pm.sets.cfn = (global ? setpmgalias : setpmralias); + pm.gets.cfn = strgetfn; + pm.unsetfn = unsetpmalias; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < aliastab->hsize; i++) + for (hn = aliastab->nodes[i]; hn; hn = hn->next) { + if ((global && ((al = (Alias) hn)->flags & ALIAS_GLOBAL)) || + (!global && !((al = (Alias) hn)->flags & ALIAS_GLOBAL))) { + pm.nam = hn->nam; + if (func != scancountparams) + pm.u.str = ((hn->flags & DISABLED) ? + dyncat(" ", al->text) : + dupstring(al->text)); + func((HashNode) &pm, flags); + } + } +} + +/**/ +static void +scanpmraliases(HashTable ht, ScanFunc func, int flags) +{ + scanpmaliases(ht, func, flags, 0); +} + +/**/ +static void +scanpmgaliases(HashTable ht, ScanFunc func, int flags) +{ + scanpmaliases(ht, func, flags, 1); +} + +/* Table for defined parameters. */ + +struct pardef { + char *name; + int flags; + GetNodeFunc getnfn; + ScanTabFunc scantfn; + void (*hsetfn) _((Param, HashTable)); + void (*setfn) _((Param, char **)); + char **(*getfn) _((Param)); + void (*unsetfn) _((Param, int)); + Param pm; +}; + +static struct pardef partab[] = { + { "parameters", PM_READONLY, + getpmparameter, scanpmparameters, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "commands", 0, + getpmcommand, scanpmcommands, setpmcommands, + NULL, NULL, stdunsetfn, NULL }, + { "functions", 0, + getpmfunction, scanpmfunctions, setpmfunctions, + NULL, NULL, stdunsetfn, NULL }, + { "builtins", PM_READONLY, + getpmbuiltin, scanpmbuiltins, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "reswords", PM_READONLY, + getpmresword, scanpmreswords, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "options", 0, + getpmoption, scanpmoptions, setpmoptions, + NULL, NULL, stdunsetfn, NULL }, + { "modules", PM_READONLY, + getpmmodule, scanpmmodules, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "dirstack", PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_REMOVABLE, + NULL, NULL, NULL, + dirssetfn, dirsgetfn, stdunsetfn, NULL }, + { "history", PM_READONLY, + getpmhistory, scanpmhistory, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "historywords", PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_READONLY, + NULL, NULL, NULL, + arrsetfn, histwgetfn, stdunsetfn, NULL }, + { "jobtexts", PM_READONLY, + getpmjobtext, scanpmjobtexts, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "jobstates", PM_READONLY, + getpmjobstate, scanpmjobstates, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "nameddirs", 0, + getpmnameddir, scanpmnameddirs, setpmnameddirs, + NULL, NULL, stdunsetfn, NULL }, + { "userdirs", PM_READONLY, + getpmuserdir, scanpmuserdirs, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "raliases", 0, + getpmralias, scanpmraliases, setpmraliases, + NULL, NULL, stdunsetfn, NULL }, + { "galiases", 0, + getpmgalias, scanpmgaliases, setpmgaliases, + NULL, NULL, stdunsetfn, NULL }, + { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +/**/ +int +setup_parameter(Module m) +{ + incleanup = 0; + + return 0; +} + +/**/ +int +boot_parameter(Module m) +{ + /* Create the special associative arrays. + * As an example for autoloaded parameters, this is probably a bad + * example, because the zsh core doesn't support creation of + * special hashes, yet. */ + + struct pardef *def; + + for (def = partab; def->name; def++) { + unsetparam(def->name); + + if (def->getnfn) { + if (!(def->pm = createspecialhash(def->name, def->getnfn, + def->scantfn))) + return 1; + def->pm->flags |= def->flags; + if (def->hsetfn) + def->pm->sets.hfn = def->hsetfn; + } else { + if (!(def->pm = createparam(def->name, def->flags))) + return 1; + def->pm->sets.afn = def->setfn; + def->pm->gets.afn = def->getfn; + def->pm->unsetfn = def->unsetfn; + } + } + return 0; +} + +#ifdef MODULE + +/**/ +int +cleanup_parameter(Module m) +{ + Param pm; + struct pardef *def; + + incleanup = 1; + + for (def = partab; def->name; def++) { + if ((pm = (Param) paramtab->getnode(paramtab, def->name)) && + pm == def->pm) { + pm->flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 1); + } } - if ((pm = (Param) paramtab->getnode(paramtab, DIR_NAM)) && pm == dirpm) - unsetparam_pm(pm, 0, 1); return 0; } diff --git a/Src/Modules/parameter.mdd b/Src/Modules/parameter.mdd index 57f975757..715b1ab55 100644 --- a/Src/Modules/parameter.mdd +++ b/Src/Modules/parameter.mdd @@ -1,3 +1,3 @@ -autoparams="parameters commands functions options modules dirstack" +autoparams="parameters commands functions builtins reswords options modules dirstack history historywords jobtexts jobstates nameddirs userdirs raliases galiases" objects="parameter.o" diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 8b31d97fa..68575cff4 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -1,5 +1,5 @@ /* - * comp.h - header file for completion + * complete.h - header file for completion * * This file is part of zsh, the Z shell. * @@ -27,143 +27,10 @@ * */ -#undef compctlread - -typedef struct compctlp *Compctlp; -typedef struct compctl *Compctl; -typedef struct compcond *Compcond; -typedef struct patcomp *Patcomp; typedef struct cmatcher *Cmatcher; typedef struct cmlist *Cmlist; typedef struct cpattern *Cpattern; typedef struct menuinfo *Menuinfo; - -/* node for compctl hash table (compctltab) */ - -struct compctlp { - HashNode next; /* next in hash chain */ - char *nam; /* command name */ - int flags; /* CURRENTLY UNUSED */ - Compctl cc; /* pointer to the compctl desc. */ -}; - -/* for the list of pattern compctls */ - -struct patcomp { - Patcomp next; - char *pat; - Compctl cc; -}; - -/* compctl -x condition */ - -struct compcond { - Compcond and, or; /* the next or'ed/and'ed conditions */ - int type; /* the type (CCT_*) */ - int n; /* the array length */ - union { /* these structs hold the data used to */ - struct { /* test this condition */ - int *a, *b; /* CCT_POS, CCT_NUMWORDS */ - } - r; - struct { /* CCT_CURSTR, CCT_CURPAT,... */ - int *p; - char **s; - } - s; - struct { /* CCT_RANGESTR,... */ - char **a, **b; - } - l; - } - u; -}; - -#define CCT_UNUSED 0 -#define CCT_POS 1 -#define CCT_CURSTR 2 -#define CCT_CURPAT 3 -#define CCT_WORDSTR 4 -#define CCT_WORDPAT 5 -#define CCT_CURSUF 6 -#define CCT_CURPRE 7 -#define CCT_CURSUB 8 -#define CCT_CURSUBC 9 -#define CCT_NUMWORDS 10 -#define CCT_RANGESTR 11 -#define CCT_RANGEPAT 12 -#define CCT_QUOTE 13 - -/* Contains the real description for compctls */ - -struct compctl { - int refc; /* reference count */ - Compctl next; /* next compctl for -x */ - unsigned long mask, mask2; /* masks of things to complete (CC_*) */ - char *keyvar; /* for -k (variable) */ - char *glob; /* for -g (globbing) */ - char *str; /* for -s (expansion) */ - char *func; /* for -K (function) */ - char *widget; /* for -i (function) */ - char *explain; /* for -X (explanation) */ - char *ylist; /* for -y (user-defined desc. for listing) */ - char *prefix, *suffix; /* for -P and -S (prefix, suffix) */ - char *subcmd; /* for -l (command name to use) */ - char *substr; /* for -1 (command name to use) */ - char *withd; /* for -w (with directory */ - char *hpat; /* for -H (history pattern) */ - int hnum; /* for -H (number of events to search) */ - char *gname; /* for -J and -V (group name) */ - Compctl ext; /* for -x (first of the compctls after -x) */ - Compcond cond; /* for -x (condition for this compctl) */ - Compctl xor; /* for + (next of the xor'ed compctls) */ - Cmatcher matcher; /* matcher control (-M) */ - char *mstr; /* matcher string */ -}; - -/* objects to complete (mask) */ -#define CC_FILES (1<<0) -#define CC_COMMPATH (1<<1) -#define CC_REMOVE (1<<2) -#define CC_OPTIONS (1<<3) -#define CC_VARS (1<<4) -#define CC_BINDINGS (1<<5) -#define CC_ARRAYS (1<<6) -#define CC_INTVARS (1<<7) -#define CC_SHFUNCS (1<<8) -#define CC_PARAMS (1<<9) -#define CC_ENVVARS (1<<10) -#define CC_JOBS (1<<11) -#define CC_RUNNING (1<<12) -#define CC_STOPPED (1<<13) -#define CC_BUILTINS (1<<14) -#define CC_ALREG (1<<15) -#define CC_ALGLOB (1<<16) -#define CC_USERS (1<<17) -#define CC_DISCMDS (1<<18) -#define CC_EXCMDS (1<<19) -#define CC_SCALARS (1<<20) -#define CC_READONLYS (1<<21) -#define CC_SPECIALS (1<<22) -#define CC_DELETE (1<<23) -#define CC_NAMED (1<<24) -#define CC_QUOTEFLAG (1<<25) -#define CC_EXTCMDS (1<<26) -#define CC_RESWDS (1<<27) -#define CC_DIRS (1<<28) - -#define CC_EXPANDEXPL (1<<30) -#define CC_RESERVED (1<<31) - -/* objects to complete (mask2) */ -#define CC_NOSORT (1<<0) -#define CC_XORCONT (1<<1) -#define CC_CCCONT (1<<2) -#define CC_PATCONT (1<<3) -#define CC_DEFCONT (1<<4) -#define CC_UNIQCON (1<<5) -#define CC_UNIQALL (1<<6) - typedef struct cexpl *Cexpl; typedef struct cmgroup *Cmgroup; typedef struct cmatch *Cmatch; @@ -191,7 +58,6 @@ struct cmgroup { int ecount; /* number of explanation string */ Cexpl *expls; /* explanation strings */ int ccount; /* number of compctls used */ - Compctl *ccs; /* the compctls used */ LinkList lexpls; /* list of explanation string while building */ LinkList lmatches; /* list of matches */ LinkList lfmatches; /* list of matches without fignore */ @@ -272,22 +138,68 @@ struct cmatcher { int ralen; /* length of right anchor */ }; - #define CMF_LINE 1 #define CMF_LEFT 2 #define CMF_RIGHT 4 - struct cpattern { Cpattern next; /* next sub-pattern */ unsigned char tab[256]; /* table of matched characters */ int equiv; /* if this is a {...} class */ }; -/* Flags for makecomplist*(). Things not to do. */ +/* This is a special return value for parse_cmatcher(), * + * signalling an error. */ + +#define pcm_err ((Cmatcher) 1) + +/* Information about what to put on the line as the unambiguous string. + * The code always keeps lists of these structs up to date while + * matches are added (in the aminfo structs below). + * The lists have two levels: in the first one we have one struct per + * word-part, where parts are separated by the anchors of `*' patterns. + * These structs have pointers (in the prefix and suffix fields) to + * lists of cline structs describing the strings before or after the + * the anchor. */ + +typedef struct cline *Cline; +typedef struct clsub Clsub; + +struct cline { + Cline next; + int flags; + char *line; + int llen; + char *word; + int wlen; + char *orig; + int olen; + int slen; + Cline prefix, suffix; + int min, max; +}; -#define CFN_FIRST 1 -#define CFN_DEFAULT 2 +#define CLF_MISS 1 +#define CLF_DIFF 2 +#define CLF_SUF 4 +#define CLF_MID 8 +#define CLF_NEW 16 +#define CLF_LINE 32 +#define CLF_JOIN 64 +#define CLF_MATCHED 128 + +/* Information for ambiguous completions. One for fignore ignored and * + * one for normal completion. */ + +typedef struct aminfo *Aminfo; + +struct aminfo { + Cmatch firstm; /* the first match */ + int exact; /* if there was an exact match */ + Cmatch exactm; /* the exact match (if any) */ + int count; /* number of matches */ + Cline line; /* unambiguous line string */ +}; /* Information about menucompletion stuff. */ @@ -463,3 +375,13 @@ struct chdata { #define CP_KEYPARAMS 27 #define CP_ALLKEYS ((unsigned int) 0x7ffffff) + +/* Types of completion. */ + +#define COMP_COMPLETE 0 +#define COMP_LIST_COMPLETE 1 +#define COMP_SPELL 2 +#define COMP_EXPAND 3 +#define COMP_EXPAND_COMPLETE 4 +#define COMP_LIST_EXPAND 5 +#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND) diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index d09fff259..d00e317c2 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -31,8 +31,24 @@ #include "compctl.pro" #define GLOBAL_PROTOTYPES #include "zle_tricky.pro" +#include "complete.pro" #undef GLOBAL_PROTOTYPES +/* Default completion infos */ + +/**/ +struct compctl cc_compos, cc_default, cc_first, cc_dummy; + +/* Hash table for completion info for commands */ + +/**/ +HashTable compctltab; + +/* List of pattern compctls */ + +/**/ +Patcomp patcomps; + #define COMP_LIST (1<<0) /* -L */ #define COMP_COMMAND (1<<1) /* -C */ #define COMP_DEFAULT (1<<2) /* -D */ @@ -48,105 +64,219 @@ static int cclist; /* Mask for determining what to print */ static unsigned long showmask = 0; -/* This is a special return value for parse_cmatcher(), * - * signalling an error. */ +/**/ +static void +createcompctltable(void) +{ + compctltab = newhashtable(23, "compctltab", NULL); + + compctltab->hash = hasher; + compctltab->emptytable = emptyhashtable; + compctltab->filltable = NULL; + compctltab->cmpnodes = strcmp; + compctltab->addnode = addhashnode; + compctltab->getnode = gethashnode2; + compctltab->getnode2 = gethashnode2; + compctltab->removenode = removehashnode; + compctltab->disablenode = NULL; + compctltab->enablenode = NULL; + compctltab->freenode = freecompctlp; + compctltab->printnode = printcompctlp; + + patcomps = NULL; +} -#define pcm_err ((Cmatcher) 1) +/**/ +static void +freecompctlp(HashNode hn) +{ + Compctlp ccp = (Compctlp) hn; -/* Copy a list of completion matchers. */ + zsfree(ccp->nam); + freecompctl(ccp->cc); + zfree(ccp, sizeof(struct compctlp)); +} -static Cmlist -cpcmlist(Cmlist l) +/**/ +void +freecompctl(Compctl cc) { - Cmlist r = NULL, *p = &r, n; + if (cc == &cc_default || + cc == &cc_first || + cc == &cc_compos || + --cc->refc > 0) + return; - while (l) { - *p = n = (Cmlist) zalloc(sizeof(struct cmlist)); - n->next = NULL; - n->matcher = cpcmatcher(l->matcher); - n->str = ztrdup(l->str); + zsfree(cc->keyvar); + zsfree(cc->glob); + zsfree(cc->str); + zsfree(cc->func); + zsfree(cc->explain); + zsfree(cc->ylist); + zsfree(cc->prefix); + zsfree(cc->suffix); + zsfree(cc->hpat); + zsfree(cc->gname); + zsfree(cc->subcmd); + zsfree(cc->substr); + if (cc->cond) + freecompcond(cc->cond); + if (cc->ext) { + Compctl n, m; - p = &(n->next); - l = l->next; + n = cc->ext; + do { + m = (Compctl) (n->next); + freecompctl(n); + n = m; + } + while (n); } - return r; + if (cc->xor && cc->xor != &cc_default) + freecompctl(cc->xor); + if (cc->matcher) + freecmatcher(cc->matcher); + zsfree(cc->mstr); + zfree(cc, sizeof(struct compctl)); } -/* Copy a completion matcher list. */ - /**/ -static Cmatcher -cpcmatcher(Cmatcher m) +void +freecompcond(void *a) { - Cmatcher r = NULL, *p = &r, n; - - while (m) { - *p = n = (Cmatcher) zalloc(sizeof(struct cmatcher)); - - n->refc = 1; - n->next = NULL; - n->flags = m->flags; - n->line = cpcpattern(m->line); - n->llen = m->llen; - n->word = cpcpattern(m->word); - n->wlen = m->wlen; - n->left = cpcpattern(m->left); - n->lalen = m->lalen; - n->right = cpcpattern(m->right); - n->ralen = m->ralen; - - p = &(n->next); - m = m->next; - } - return r; + Compcond cc = (Compcond) a; + Compcond and, or, c; + int n; + + for (c = cc; c; c = or) { + or = c->or; + for (; c; c = and) { + and = c->and; + if (c->type == CCT_POS || + c->type == CCT_NUMWORDS) { + free(c->u.r.a); + free(c->u.r.b); + } else if (c->type == CCT_CURSUF || + c->type == CCT_CURPRE) { + for (n = 0; n < c->n; n++) + if (c->u.s.s[n]) + zsfree(c->u.s.s[n]); + free(c->u.s.s); + } else if (c->type == CCT_RANGESTR || + c->type == CCT_RANGEPAT) { + for (n = 0; n < c->n; n++) + if (c->u.l.a[n]) + zsfree(c->u.l.a[n]); + free(c->u.l.a); + for (n = 0; n < c->n; n++) + if (c->u.l.b[n]) + zsfree(c->u.l.b[n]); + free(c->u.l.b); + } else { + for (n = 0; n < c->n; n++) + if (c->u.s.s[n]) + zsfree(c->u.s.s[n]); + free(c->u.s.p); + free(c->u.s.s); + } + zfree(c, sizeof(struct compcond)); + } + } } -/* Copy a completion matcher pattern. */ - /**/ -static Cpattern -cpcpattern(Cpattern o) +int +compctlread(char *name, char **args, char *ops, char *reply) { - Cpattern r = NULL, *p = &r, n; + char *buf, *bptr; - while (o) { - *p = n = (Cpattern) zalloc(sizeof(struct cpattern)); + /* only allowed to be called for completion */ + if (!incompctlfunc) { + zwarnnam(name, "option valid only in functions called for completion", + NULL, 0); + return 1; + } - n->next = NULL; - memcpy(n->tab, o->tab, 256); - n->equiv = o->equiv; + if (ops['l']) { + /* -ln gives the index of the word the cursor is currently on, which is + available in cs (but remember that Zsh counts from one, not zero!) */ + if (ops['n']) { + char nbuf[14]; + + if (ops['e'] || ops['E']) + printf("%d\n", cs + 1); + if (!ops['e']) { + sprintf(nbuf, "%d", cs + 1); + setsparam(reply, ztrdup(nbuf)); + } + return 0; + } + /* without -n, the current line is assigned to the given parameter as a + scalar */ + if (ops['e'] || ops['E']) { + zputs((char *) line, stdout); + putchar('\n'); + } + if (!ops['e']) + setsparam(reply, ztrdup((char *) line)); + } else { + int i; - p = &(n->next); - o = o->next; - } - return r; -} + /* -cn gives the current cursor position within the current word, which + is available in clwpos (but remember that Zsh counts from one, not + zero!) */ + if (ops['n']) { + char nbuf[14]; + + if (ops['e'] || ops['E']) + printf("%d\n", clwpos + 1); + if (!ops['e']) { + sprintf(nbuf, "%d", clwpos + 1); + setsparam(reply, ztrdup(nbuf)); + } + return 0; + } + /* without -n, the words of the current line are assigned to the given + parameters separately */ + if (ops['A'] && !ops['e']) { + /* the -A option means that one array is specified, instead of + many parameters */ + char **p, **b = (char **)zcalloc((clwnum + 1) * sizeof(char *)); -/* Set the global match specs. */ + for (i = 0, p = b; i < clwnum; p++, i++) + *p = ztrdup(clwords[i]); -/**/ -static int -set_gmatcher(char *name, char **argv) -{ - Cmlist l = NULL, *q = &l, n; - Cmatcher m; + setaparam(reply, b); + return 0; + } + if (ops['e'] || ops['E']) { + for (i = 0; i < clwnum; i++) { + zputs(clwords[i], stdout); + putchar('\n'); + } - while (*argv) { - if ((m = parse_cmatcher(name, *argv)) == pcm_err) - return 1; - *q = n = (Cmlist) zhalloc(sizeof(struct cmlist)); - n->next = NULL; - n->matcher = m; - n->str = *argv++; + if (ops['e']) + return 0; + } - q = &(n->next); - } - freecmlist(cmatcher); - PERMALLOC { - cmatcher = cpcmlist(l); - } LASTALLOC; + for (i = 0; i < clwnum && *args; reply = *args++, i++) + setsparam(reply, ztrdup(clwords[i])); + + if (i < clwnum) { + int j, len; - return 1; + for (j = i, len = 0; j < clwnum; len += strlen(clwords[j++])); + bptr = buf = zalloc(len + j - i); + while (i < clwnum) { + strucpy(&bptr, clwords[i++]); + *bptr++ = ' '; + } + bptr[-1] = '\0'; + } else + buf = ztrdup(""); + setsparam(reply, buf); + } + return 0; } /* Try to get the global matcher from the given compctl. */ @@ -190,208 +320,6 @@ print_gmatcher(int ac) } } -/* Parse a string for matcher control, containing multiple matchers. */ - -/**/ -static Cmatcher -parse_cmatcher(char *name, char *s) -{ - Cmatcher ret = NULL, r = NULL, n; - Cpattern line, word, left, right; - int fl, ll, wl, lal, ral, err; - - if (!*s) - return NULL; - - while (*s) { - while (*s && inblank(*s)) s++; - - if (!*s) break; - - switch (*s) { - case 'l': fl = CMF_LEFT; break; - case 'r': fl = CMF_RIGHT; break; - case 'm': fl = 0; break; - case 'L': fl = CMF_LEFT | CMF_LINE; break; - case 'R': fl = CMF_RIGHT | CMF_LINE; break; - case 'M': fl = CMF_LINE; break; - default: - zwarnnam(name, "unknown match specification character `%c'", NULL, *s); - return pcm_err; - } - if (s[1] != ':') { - zwarnnam(name, "missing `:'", NULL, 0); - return pcm_err; - } - s += 2; - if (!*s) { - zwarnnam(name, "missing patterns", NULL, 0); - return pcm_err; - } - if (fl & CMF_LEFT) { - left = parse_pattern(name, &s, &lal, '|', &err); - if (err) - return pcm_err; - if (!*s || !*++s) { - zwarnnam(name, "missing line pattern", NULL, 0); - return pcm_err; - } - } else - left = NULL; - - line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='), - &err); - if (err) - return pcm_err; - if ((fl & CMF_RIGHT) && (!*s || !*++s)) { - zwarnnam(name, "missing right anchor", NULL, 0); - } else if (!(fl & CMF_RIGHT)) { - if (!*s) { - zwarnnam(name, "missing word pattern", NULL, 0); - return pcm_err; - } - s++; - } - if (fl & CMF_RIGHT) { - right = parse_pattern(name, &s, &ral, '=', &err); - if (err) - return pcm_err; - if (!*s) { - zwarnnam(name, "missing word pattern", NULL, 0); - return pcm_err; - } - s++; - } else - right = NULL; - - if (*s == '*') { - if (!(fl & (CMF_LEFT | CMF_RIGHT))) { - zwarnnam(name, "need anchor for `*'", NULL, 0); - return pcm_err; - } - word = NULL; - wl = -1; - s++; - } else { - word = parse_pattern(name, &s, &wl, 0, &err); - - if (!word && !line) { - zwarnnam(name, "need non-empty word or line pattern", NULL, 0); - return pcm_err; - } - } - if (err) - return pcm_err; - - n = (Cmatcher) zcalloc(sizeof(*ret)); - n->next = NULL; - n->flags = fl; - n->line = line; - n->llen = ll; - n->word = word; - n->wlen = wl; - n->left = left; - n->lalen = lal; - n->right = right; - n->ralen = ral; - - if (ret) - r->next = n; - else - ret = n; - - r = n; - } - return ret; -} - -/* Parse a pattern for matcher control. */ - -/**/ -static Cpattern -parse_pattern(char *name, char **sp, int *lp, char e, int *err) -{ - Cpattern ret = NULL, r = NULL, n; - unsigned char *s = (unsigned char *) *sp; - int l = 0; - - *err = 0; - - while (*s && (e ? (*s != e) : !inblank(*s))) { - n = (Cpattern) hcalloc(sizeof(*n)); - n->next = NULL; - n->equiv = 0; - - if (*s == '[') { - s = parse_class(n, s + 1, ']'); - if (!*s) { - *err = 1; - zwarnnam(name, "unterminated character class", NULL, 0); - return NULL; - } - } else if (*s == '{') { - n->equiv = 1; - s = parse_class(n, s + 1, '}'); - if (!*s) { - *err = 1; - zwarnnam(name, "unterminated character class", NULL, 0); - return NULL; - } - } else if (*s == '?') { - memset(n->tab, 1, 256); - } else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') { - *err = 1; - zwarnnam(name, "invalid pattern character `%c'", NULL, *s); - return NULL; - } else { - if (*s == '\\' && s[1]) - s++; - - memset(n->tab, 0, 256); - n->tab[*s] = 1; - } - if (ret) - r->next = n; - else - ret = n; - - r = n; - - l++; - s++; - } - *sp = (char *) s; - *lp = l; - return ret; -} - -/* Parse a character class for matcher control. */ - -/**/ -static unsigned char * -parse_class(Cpattern p, unsigned char *s, unsigned char e) -{ - int n = 0, i = 1, j, eq = (e == '}'), k = 1; - - if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; } - - memset(p->tab, n, 256); - - n = !n; - while (*s && (k || *s != e)) { - if (s[1] == '-' && s[2] != e) { - /* a run of characters */ - for (j = (int) *s; j <= (int) s[2]; j++) - p->tab[j] = (eq ? i++ : n); - - s += 3; - } else - p->tab[*s++] = (eq ? i++ : n); - k = 0; - } - return s; -} - /* Parse the basic flags for `compctl' */ /**/ @@ -599,19 +527,6 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl) *argv = "" - 1; } break; - case 'i': - if ((*argv)[1]) { - cct.widget = (*argv) + 1; - *argv = "" - 1; - } else if (!argv[1]) { - zwarnnam(name, "function name expected after -%c", NULL, - **argv); - return 1; - } else { - cct.widget = *++argv; - *argv = "" - 1; - } - break; case 'Y': cct.mask |= CC_EXPANDEXPL; goto expl; @@ -1260,7 +1175,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass) zsfree(cc->glob); zsfree(cc->str); zsfree(cc->func); - zsfree(cc->widget); zsfree(cc->explain); zsfree(cc->ylist); zsfree(cc->prefix); @@ -1282,7 +1196,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass) cc->glob = ztrdup(cct->glob); cc->str = ztrdup(cct->str); cc->func = ztrdup(cct->func); - cc->widget = ztrdup(cct->widget); cc->explain = ztrdup(cct->explain); cc->ylist = ztrdup(cct->ylist); cc->prefix = ztrdup(cct->prefix); @@ -1494,7 +1407,6 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat) printif(cc->gname, 'J'); printif(cc->keyvar, 'k'); printif(cc->func, 'K'); - printif(cc->widget, 'i'); printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X'); printif(cc->ylist, 'y'); printif(cc->prefix, 'P'); @@ -1716,955 +1628,2094 @@ bin_compctl(char *name, char **argv, char *ops, int func) return ret; } -/**/ +/* Flags for makecomplist*(). Things not to do. */ + +#define CFN_FIRST 1 +#define CFN_DEFAULT 2 + static int -bin_compgen(char *name, char **argv, char *ops, int func) +bin_compcall(char *name, char **argv, char *ops, int func) { - Compctl cc; - int ret = 0; - if (incompfunc != 1) { zerrnam(name, "can only be called from completion function", NULL, 0); return 1; } - cc = (Compctl) zcalloc(sizeof(*cc)); - cclist = 0; - showmask = 0; - - if (get_compctl(name, &argv, cc, 1, 0, 1)) - ret = 1; - else if (*argv) { - zerrnam(name, "command names illegal", NULL, 0); - ret = 1; - } else - ret = makecomplistcallptr(cc); - - freecompctl(cc); - return ret; + return makecomplistctl((ops['T'] ? 0 : CFN_FIRST) | + (ops['D'] ? 0 : CFN_DEFAULT)); } -/**/ -static int -bin_compadd(char *name, char **argv, char *ops, int func) -{ - struct cadata dat; - char *p, **sp, *e, *m = NULL; - int dm; - Cmatcher match = NULL; +/* + * Functions to generate matches. + */ - if (incompfunc != 1) { - zerrnam(name, "can only be called from completion function", NULL, 0); - return 1; - } - dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = - dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = - dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL; - dat.match = NULL; - dat.flags = 0; - dat.aflags = CAF_MATCH; +/* A pointer to the compctl we are using. */ - for (; *argv && **argv == '-'; argv++) { - if (!(*argv)[1]) { - argv++; - break; - } - for (p = *argv + 1; *p; p++) { - sp = NULL; - e = NULL; - dm = 0; - switch (*p) { - case 'q': - dat.flags |= CMF_REMOVE; - break; - case 'Q': - dat.aflags |= CAF_QUOTE; - break; - case 'f': - dat.flags |= CMF_FILE; - break; - case 'e': - dat.flags |= CMF_ISPAR; - break; - case 'F': - sp = &(dat.ign); - e = "string expected after -%c"; - break; - case 'n': - dat.flags |= CMF_NOLIST; - break; - case 'U': - dat.aflags &= ~CAF_MATCH; - break; - case 'P': - sp = &(dat.pre); - e = "string expected after -%c"; - break; - case 'S': - sp = &(dat.suf); - e = "string expected after -%c"; - break; - case 'J': - sp = &(dat.group); - e = "group name expected after -%c"; - break; - case 'V': - if (!dat.group) - dat.aflags |= CAF_NOSORT; - sp = &(dat.group); - e = "group name expected after -%c"; - break; - case '1': - if (!(dat.aflags & CAF_UNIQCON)) - dat.aflags |= CAF_UNIQALL; - break; - case '2': - if (!(dat.aflags & CAF_UNIQALL)) - dat.aflags |= CAF_UNIQCON; - break; - case 'y': - sp = &(dat.ylist); - e = "string expected after -%c"; - break; - case 'i': - sp = &(dat.ipre); - e = "string expected after -%c"; - break; - case 'I': - sp = &(dat.isuf); - e = "string expected after -%c"; - break; - case 'p': - sp = &(dat.ppre); - e = "string expected after -%c"; - break; - case 's': - sp = &(dat.psuf); - e = "string expected after -%c"; - break; - case 'W': - sp = &(dat.prpre); - e = "string expected after -%c"; - break; - case 'a': - dat.aflags |= CAF_ALT; - break; - case 'M': - sp = &m; - e = "matching specification expected after -%c"; - dm = 1; - break; - case 'X': - sp = &(dat.exp); - e = "string expected after -%c"; - break; - case 'r': - dat.flags |= CMF_REMOVE; - sp = &(dat.rems); - e = "string expected after -%c"; - break; - case 'R': - dat.flags |= CMF_REMOVE; - sp = &(dat.remf); - e = "function name expected after -%c"; - break; - case 'A': - sp = &(dat.apar); - e = "parameter name expected after -%c"; - break; - case 'O': - sp = &(dat.opar); - e = "parameter name expected after -%c"; - break; - case 'D': - sp = &(dat.dpar); - e = "parameter name expected after -%c"; - break; - case 'd': - sp = &(dat.disp); - e = "parameter name expected after -%c"; - break; - case 'l': - dat.flags |= CMF_DISPLINE; - break; - case '-': - argv++; - goto ca_args; - default: - zerrnam(name, "bad option: -%c", NULL, *p); - return 1; - } - if (sp) { - if (p[1]) { - if (!*sp) - *sp = p + 1; - p = "" - 1; - } else if (argv[1]) { - argv++; - if (!*sp) - *sp = *argv; - p = "" - 1; - } else { - zerrnam(name, e, NULL, *p); - return 1; - } - if (dm && (match = parse_cmatcher(name, m)) == pcm_err) { - match = NULL; - return 1; - } - } - } - } - ca_args: - if (!*argv) - return 1; +static Compctl curcc; - dat.match = match = cpcmatcher(match); - dm = addmatchesptr(&dat, argv); - freecmatcher(match); +/* A list of all compctls we have already used. */ - return dm; -} +static LinkList ccused, lastccused; -#define CVT_RANGENUM 0 -#define CVT_RANGEPAT 1 -#define CVT_PRENUM 2 -#define CVT_PREPAT 3 -#define CVT_SUFNUM 4 -#define CVT_SUFPAT 5 +/* A stack of currently used compctls. */ -/**/ -void -ignore_prefix(int l) -{ - if (l) { - char *tmp, sav; - int pl = strlen(compprefix); +static LinkList ccstack; - if (l > pl) - l = pl; +/* The beginning and end of a word range to be used by -l. */ - sav = compprefix[l]; +static int brange, erange; - compprefix[l] = '\0'; - tmp = tricat(compiprefix, compprefix, ""); - zsfree(compiprefix); - compiprefix = tmp; - compprefix[l] = sav; - tmp = ztrdup(compprefix + l); - zsfree(compprefix); - compprefix = tmp; - } -} +/* This is used to detect when and what to continue. */ -/**/ -void -ignore_suffix(int l) -{ - if (l) { - char *tmp, sav; - int sl = strlen(compsuffix); - - if ((l = sl - l) < 0) - l = 0; - - tmp = tricat(compsuffix + l, compisuffix, ""); - zsfree(compisuffix); - compisuffix = tmp; - sav = compsuffix[l]; - compsuffix[l] = '\0'; - tmp = ztrdup(compsuffix); - compsuffix[l] = sav; - zsfree(compsuffix); - compsuffix = tmp; - } -} +static unsigned long ccont; -/**/ -void -restrict_range(int b, int e) -{ - int wl = arrlen(compwords) - 1; +/* Two patterns used when doing glob-completion. The first one is built * + * from the whole word we are completing and the second one from that * + * part of the word that was identified as a possible filename. */ - if (wl && b >= 0 && e >= 0 && (b > 0 || e < wl)) { - int i; - char **p, **q, **pp; +static Patprog patcomp, filecomp; - if (e > wl) - e = wl; +/* We store the following prefixes/suffixes: * + * lpre/lsuf -- what's on the line * + * rpre/rsuf -- same as lpre/lsuf, but expanded * + * ppre/psuf -- the path prefix/suffix * + * lppre/lpsuf -- the path prefix/suffix, unexpanded * + * fpre/fsuf -- prefix/suffix of the pathname component the cursor is in * + * prpre -- ppre in expanded form usable for opendir * + * qipre, qisuf-- ingnored quoted string * + * * + * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf, * + * fpre, fsuf, lppre, and lpsuf. noreal is non-zero if we have rpre/rsuf. */ - i = e - b + 1; - p = (char **) zcalloc((i + 1) * sizeof(char *)); +static char *lpre, *lsuf; +static char *rpre, *rsuf; +static char *ppre, *psuf, *lppre, *lpsuf, *prpre; +static char *fpre, *fsuf; +static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf; +static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl; +static int noreal; - for (q = p, pp = compwords + b; i; i--, q++, pp++) - *q = ztrdup(*pp); - freearray(compwords); - compwords = p; - compcurrent -= b; - } -} +/* This is either zero or equal to the special character the word we are * + * trying to complete starts with (e.g. Tilde or Equals). */ -static int -do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) -{ - switch (test) { - case CVT_RANGENUM: - { - int l = arrlen(compwords); +static char ic; - if (na < 0) - na += l; - else - na--; - if (nb < 0) - nb += l; - else - nb--; +/* This variable says what we are currently adding to the list of matches. */ - if (compcurrent - 1 < na || compcurrent - 1 > nb) - return 0; - if (mod) - restrict_range(na, nb); - return 1; - } - case CVT_RANGEPAT: - { - char **p; - int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1; - Patprog pp; +static int addwhat; - i = compcurrent - 1; - if (i < 0 || i >= l) - return 0; +/* Convenience macro for calling bslashquote() (formerly quotename()). * + * This uses the instring variable above. */ - singsub(&sa); - pp = patcompile(sa, PAT_STATIC, NULL); +#define quotename(s, e) bslashquote(s, e, instring) - for (i--, p = compwords + i; i >= 0; p--, i--) { - if (pattry(pp, *p)) { - b = i + 1; - t = 1; - break; - } - } - if (t && sb) { - int tt = 0; +/* Hook functions */ - singsub(&sb); - pp = patcompile(sb, PAT_STATIC, NULL); +static int +ccmakehookfn(Hookdef dummy, struct ccmakedat *dat) +{ + makecomplistglobal(dat->str, dat->incmd, dat->lst, 0); - for (i++, p = compwords + i; i < l; p++, i++) { - if (pattry(pp, *p)) { - e = i - 1; - tt = 1; - break; - } - } - if (tt && i < compcurrent) - t = 0; - } - if (e < b) - t = 0; - if (t && mod) - restrict_range(b, e); - return t; - } - case CVT_PRENUM: - case CVT_SUFNUM: - if (!na) - return 1; - if (na > 0 && - strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) { - if (mod) { - if (test == CVT_PRENUM) - ignore_prefix(na); - else - ignore_suffix(na); - return 1; - } - return 0; - } - case CVT_PREPAT: - case CVT_SUFPAT: - { - Patprog pp; + return 0; +} - if (!na) - return 0; +static int +ccbeforehookfn(Hookdef dummy, void *zdup) +{ + ccused = newlinklist(); + ccstack = newlinklist(); - if (!(pp = patcompile(sa, PAT_STATIC, 0))) - return 0; + return 0; +} - if (test == CVT_PREPAT) { - int l, add; - char *p, sav; +static int +ccafterhookfn(Hookdef dummy, void *zdup) +{ + LinkNode n; - if (!(l = strlen(compprefix))) - return 0; - if (na < 0) { - p = compprefix + l; - na = -na; - add = -1; - } else { - p = compprefix + 1; - add = 1; - } - for (; l; l--, p += add) { - sav = *p; - *p = '\0'; - test = pattry(pp, compprefix); - *p = sav; - if (test && !--na) - break; - } - if (!l) - return 0; - if (mod) - ignore_prefix(p - compprefix); - } else { - int l, ol, add; - char *p; + if (zdup) { + if (lastccused) + freelinklist(lastccused, (FreeFunc) freecompctl); - if (!(ol = l = strlen(compsuffix))) - return 0; - if (na < 0) { - p = compsuffix; - na = -na; - add = 1; - } else { - p = compsuffix + l - 1; - add = -1; - } - for (; l; l--, p += add) - if (pattry(pp, p) && !--na) - break; + PERMALLOC { + lastccused = newlinklist(); + for (n = firstnode(ccused); n; incnode(n)) + addlinknode(lastccused, getdata(n)); + } LASTALLOC; + } else + for (n = firstnode(ccused); n; incnode(n)) + if (((Compctl) getdata(n)) != &cc_dummy) + freecompctl((Compctl) getdata(n)); - if (!l) - return 0; - if (mod) - ignore_suffix(ol - (p - compsuffix)); - } - return 1; - } - } return 0; } +/* This adds a match to the list of matches. The string to add is given * + * in s, the type of match is given in the global variable addwhat and * + * the parameter t (if not NULL) is a pointer to a hash node node which * + * may be used to give other information to this function. * + * * + * addwhat contains either one of the special values (negative, see below) * + * or the inclusive OR of some of the CC_* flags used for compctls. */ + /**/ -static int -bin_compset(char *name, char **argv, char *ops, int func) +static void +addmatch(char *s, char *t) { - int test = 0, na = 0, nb = 0; - char *sa = NULL, *sb = NULL; + int isfile = 0, isalt = 0, isexact; + char *ms = NULL, *tt; + HashNode hn; + Param pm; + Cline lc = NULL; + Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst; + + for (bp = brbeg; bp; bp = bp->next) + bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos); + for (bp = brend; bp; bp = bp->next) + bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos); + + /* + * addwhat: -5 is for files, + * -6 is for glob expansions, + * -8 is for executable files (e.g. command paths), + * -9 is for parameters + * -7 is for command names (from cmdnamtab) + * -4 is for a cdable parameter + * -3 is for executable command names. + * -2 is for anything unquoted + * -1 is for other file specifications + * (things with `~' or `=' at the beginning, ...). + */ - if (incompfunc != 1) { - zerrnam(name, "can only be called from completion function", NULL, 0); - return 1; - } - if (argv[0][0] != '-') { - zerrnam(name, "missing option", NULL, 0); - return 1; - } - switch (argv[0][1]) { - case 'n': test = CVT_RANGENUM; break; - case 'N': test = CVT_RANGEPAT; break; - case 'p': test = CVT_PRENUM; break; - case 'P': test = CVT_PREPAT; break; - case 's': test = CVT_SUFNUM; break; - case 'S': test = CVT_SUFPAT; break; - case 'q': return set_comp_sepptr(); - default: - zerrnam(name, "bad option -%c", NULL, argv[0][1]); - return 1; - } - if (argv[0][2]) { - sa = argv[0] + 2; - sb = argv[1]; - na = 2; - } else { - if (!(sa = argv[1])) { - zerrnam(name, "missing string for option -%c", NULL, argv[0][1]); - return 1; + /* Just to make the code cleaner */ + hn = (HashNode) t; + pm = (Param) t; + + if (addwhat == -1 || addwhat == -5 || addwhat == -6 || + addwhat == CC_FILES || addwhat == -7 || addwhat == -8) { + int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0); + + while (bpl && bpl->curpos < ppl) + bpl = bpl->next; + while (bsl && bsl->curpos < psl) + bsl = bsl->next; + + if ((addwhat == CC_FILES || + addwhat == -5) && !*psuf) { + /* If this is a filename, do the fignore check. */ + char **pt = fignore; + int filell, sl = strlen(s); + + for (isalt = 0; !isalt && *pt; pt++) + if ((filell = strlen(*pt)) < sl && + !strcmp(*pt, s + sl - filell)) + isalt = 1; + } + ms = ((addwhat == CC_FILES || addwhat == -6 || + addwhat == -5 || addwhat == -8) ? + comp_match(qfpre, qfsuf, s, filecomp, &lc, (ppre && *ppre ? 1 : 2), + &bpl, ppl ,&bsl, psl, &isexact) : + comp_match(fpre, fsuf, s, filecomp, &lc, 0, + &bpl, ppl, &bsl, psl, &isexact)); + if (!ms) + return; + + if (addwhat == -7 && !findcmd(s, 0)) + return; + isfile = CMF_FILE; + } else if (addwhat == CC_QUOTEFLAG || addwhat == -2 || + (addwhat == -3 && !(hn->flags & DISABLED)) || + (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) && + !pm->level && (tt = pm->gets.cfn(pm)) && *tt == '/') || + (addwhat == -9 && !(hn->flags & PM_UNSET) && !pm->level) || + (addwhat > 0 && + ((!(hn->flags & PM_UNSET) && + (((addwhat & CC_ARRAYS) && (hn->flags & PM_ARRAY)) || + ((addwhat & CC_INTVARS) && (hn->flags & PM_INTEGER)) || + ((addwhat & CC_ENVVARS) && (hn->flags & PM_EXPORTED)) || + ((addwhat & CC_SCALARS) && (hn->flags & PM_SCALAR)) || + ((addwhat & CC_READONLYS) && (hn->flags & PM_READONLY)) || + ((addwhat & CC_SPECIALS) && (hn->flags & PM_SPECIAL)) || + ((addwhat & CC_PARAMS) && !(hn->flags & PM_EXPORTED))) && + !pm->level) || + ((( addwhat & CC_SHFUNCS) || + ( addwhat & CC_BUILTINS) || + ( addwhat & CC_EXTCMDS) || + ( addwhat & CC_RESWDS) || + ((addwhat & CC_ALREG) && !(hn->flags & ALIAS_GLOBAL)) || + ((addwhat & CC_ALGLOB) && (hn->flags & ALIAS_GLOBAL))) && + (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) || + ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) || + ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) { + char *p1, *s1, *p2, *s2; + + if (addwhat == CC_QUOTEFLAG) { + p1 = qrpre; s1 = qrsuf; + p2 = rpre; s2 = rsuf; + } else { + p1 = qlpre; s1 = qlsuf; + p2 = lpre; s2 = lsuf; + } + bpt = bpl; + bst = bsl; + + if (!(ms = comp_match(p1, s1, s, patcomp, &lc, + (addwhat == CC_QUOTEFLAG), + &bpl, strlen(p1), &bsl, strlen(s1), + &isexact))) { + bpl = bpt; + bsl = bst; + if (!(ms = comp_match(p2, s2, s, NULL, &lc, + (addwhat == CC_QUOTEFLAG), + &bpl, strlen(p2), &bsl, strlen(s2), + &isexact))) + return; } - sb = argv[2]; - na = 3; - } - if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb : - (sb && argv[na]))) { - zerrnam(name, "too many arguments", NULL, 0); - return 1; - } - switch (test) { - case CVT_RANGENUM: - na = atoi(sa); - nb = (sb ? atoi(sb) : -1); - break; - case CVT_RANGEPAT: - tokenize(sa); - sa = rembslash(sa); - remnulargs(sa); - if (sb) { - tokenize(sb); - sb = rembslash(sb); - remnulargs(sb); - } - break; - case CVT_PRENUM: - case CVT_SUFNUM: - na = atoi(sa); - break; - case CVT_PREPAT: - case CVT_SUFPAT: - if (sb) { - na = atoi(sa); - sa = sb; - } else - na = -1; - tokenize(sa); - sa = rembslash(sa); - remnulargs(sa); - break; } - return !do_comp_vars(test, na, sa, nb, sb, 1); + if (!ms) + return; + add_match_data(isalt, ms, lc, ipre, ripre, isuf, + (incompfunc ? dupstring(curcc->prefix) : curcc->prefix), + prpre, + (isfile ? lppre : NULL), NULL, + (isfile ? lpsuf : NULL), NULL, + (incompfunc ? dupstring(curcc->suffix) : curcc->suffix), + (mflags | isfile), isexact); } /**/ -static int -bin_compcall(char *name, char **argv, char *ops, int func) +static void +maketildelist(void) { - if (incompfunc != 1) { - zerrnam(name, "can only be called from completion function", NULL, 0); - return 1; - } - return makecomplistctlptr((ops['T'] ? 0 : CFN_FIRST) | - (ops['D'] ? 0 : CFN_DEFAULT)); + /* add all the usernames to the named directory table */ + nameddirtab->filltable(nameddirtab); + + scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0, + addhnmatch, 0); } -/* Definitions for the special parameters. Note that these have to match the - * order of the CP_* bits in comp.h */ +/* This does the check for compctl -x `n' and `N' patterns. */ -#define VAL(X) ((void *) (&(X))) -struct compparam { - char *name; - int type; - void *var, *set, *get; -}; +/**/ +int +getcpat(char *str, int cpatindex, char *cpat, int class) +{ + char *s, *t, *p; + int d = 0; -static struct compparam comprparams[] = { - { "words", PM_ARRAY, VAL(compwords), NULL, NULL }, - { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL, NULL }, - { "PREFIX", PM_SCALAR, VAL(compprefix), NULL, NULL }, - { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL, NULL }, - { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL, NULL }, - { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL, NULL }, - { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL }, - { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL }, - { NULL, 0, NULL, NULL, NULL } -}; + if (!str || !*str) + return -1; -static struct compparam compkparams[] = { - { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) }, - { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL }, - { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL }, - { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL }, - { "context", PM_SCALAR, VAL(compcontext), NULL, NULL }, - { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL }, - { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL }, - { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL, NULL }, - { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL }, - { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL }, - { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) }, - { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL }, - { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL }, - { "exact", PM_SCALAR, VAL(compexact), NULL, NULL }, - { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL }, - { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL }, - { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL }, - { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) }, - { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL, - VAL(get_unambig_curs) }, - { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL }, - { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL }, - { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL }, - { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL }, - { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL }, - { "vared", PM_SCALAR, VAL(compvared), NULL, NULL }, - { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) }, - { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) }, - { NULL, 0, NULL, NULL, NULL } -}; + cpat = rembslash(cpat); -#define COMPSTATENAME "compstate" + if (!cpatindex) + cpatindex++, d = 0; + else if ((d = (cpatindex < 0))) + cpatindex = -cpatindex; -static void -addcompparams(struct compparam *cp, Param *pp) -{ - for (; cp->name; cp++, pp++) { - Param pm = createparam(cp->name, - cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL); - if (!pm) - pm = (Param) paramtab->getnode(paramtab, cp->name); - DPUTS(!pm, "param not set in addcompparams"); - - *pp = pm; - pm->level = locallevel + 1; - if ((pm->u.data = cp->var)) { - switch(PM_TYPE(cp->type)) { - case PM_SCALAR: - pm->sets.cfn = strvarsetfn; - pm->gets.cfn = strvargetfn; - break; - case PM_INTEGER: - pm->sets.ifn = intvarsetfn; - pm->gets.ifn = intvargetfn; - pm->ct = 10; - break; - case PM_ARRAY: - pm->sets.afn = arrvarsetfn; - pm->gets.afn = arrvargetfn; + for (s = d ? str + strlen(str) - 1 : str; + d ? (s >= str) : *s; + d ? s-- : s++) { + for (t = s, p = cpat; *t && *p; p++) { + if (class) { + if (*p == *s && !--cpatindex) + return (int)(s - str + 1); + } else if (*t++ != *p) break; - } - } else { - pm->sets.cfn = (void (*) _((Param, char *))) cp->set; - pm->gets.cfn = (char *(*) _((Param))) cp->get; } - pm->unsetfn = compunsetfn; + if (!class && !*p && !--cpatindex) + return t - str; } + return -1; } -/**/ -void -makecompparams(void) -{ - Param cpm; - HashTable tht; - - addcompparams(comprparams, comprpms); - - if (!(cpm = createparam(COMPSTATENAME, - PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED))) - cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME); - DPUTS(!cpm, "param not set in makecompparams"); - - comprpms[CPN_COMPSTATE] = cpm; - tht = paramtab; - cpm->level = locallevel; - cpm->gets.hfn = get_compstate; - cpm->sets.hfn = set_compstate; - cpm->unsetfn = compunsetfn; - cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME); - addcompparams(compkparams, compkpms); - paramtab = tht; -} - -/**/ -static HashTable -get_compstate(Param pm) -{ - return pm->u.hash; -} +/* Dump a hash table (without sorting). For each element the addmatch * + * function is called and at the beginning the addwhat variable is set. * + * This could be done using scanhashtable(), but this is easy and much * + * more efficient. */ /**/ static void -set_compstate(Param pm, HashTable ht) +dumphashtable(HashTable ht, int what) { - struct compparam *cp; - Param *pp; HashNode hn; int i; - struct value v; - char *str; + + addwhat = what; for (i = 0; i < ht->hsize; i++) for (hn = ht->nodes[i]; hn; hn = hn->next) - for (cp = compkparams, - pp = compkpms; cp->name; cp++, pp++) - if (!strcmp(hn->nam, cp->name)) { - v.isarr = v.inv = v.a = 0; - v.b = -1; - v.arr = NULL; - v.pm = (Param) hn; - if (cp->type == PM_INTEGER) - *((zlong *) cp->var) = getintvalue(&v); - else if ((str = getstrvalue(&v))) { - zsfree(*((char **) cp->var)); - *((char **) cp->var) = ztrdup(str); - } - (*pp)->flags &= ~PM_UNSET; - - break; - } - deleteparamtable(ht); + addmatch(hn->nam, (char *) hn); } -/**/ -static zlong -get_nmatches(Param pm) -{ - return num_matchesptr(1); -} +/* ScanFunc used by maketildelist() et al. */ /**/ -static zlong -get_anmatches(Param pm) +static void +addhnmatch(HashNode hn, int flags) { - return num_matchesptr(0); + addmatch(hn->nam, NULL); } +/* Perform expansion on the given string and return the result. * + * During this errors are not reported. */ + /**/ -static zlong -get_listlines(Param pm) +static char * +getreal(char *str) { - return list_linesptr(); + LinkList l = newlinklist(); + int ne = noerrs; + + noerrs = 1; + addlinknode(l, dupstring(str)); + prefork(l, 0); + noerrs = ne; + if (!errflag && nonempty(l) && + ((char *) peekfirst(l)) && ((char *) peekfirst(l))[0]) + return dupstring(peekfirst(l)); + errflag = 0; + + return dupstring(str); } +/* This reads a directory and adds the files to the list of * + * matches. The parameters say which files should be added. */ + /**/ static void -set_complist(Param pm, char *v) +gen_matches_files(int dirs, int execs, int all) { - comp_listptr(v); + DIR *d; + struct stat buf; + char *n, p[PATH_MAX], *q = NULL, *e; + LinkList l = NULL; + int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat; + + opts[NULLGLOB] = 1; + + if (*psuf) { + /* If there is a path suffix, check if it doesn't have a `*' or * + * `)' at the end (this is used to determine if we should use * + * globbing). */ + q = psuf + strlen(psuf) - 1; + ns = !(*q == Star || *q == Outpar); + l = newlinklist(); + /* And generate only directory names. */ + dirs = 1; + all = execs = 0; + } + /* Open directory. */ + if ((d = opendir((prpre && *prpre) ? prpre : "."))) { + /* If we search only special files, prepare a path buffer for stat. */ + if (!all && prpre) { + strcpy(p, prpre); + q = p + strlen(prpre); + } + /* Fine, now read the directory. */ + while ((n = zreaddir(d, 1)) && !errflag) { + /* Ignore files beginning with `.' unless the thing we found on * + * the command line also starts with a dot or GLOBDOTS is set. */ + if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) { + addwhat = execs ? -8 : -5; + if (filecomp) + /* If we have a pattern for the filename check, use it. */ + test = pattry(filecomp, n); + else { + /* Otherwise use the prefix and suffix strings directly. */ + e = n + strlen(n) - fsl; + if ((test = !strncmp(n, fpre, fpl))) + test = !strcmp(e, fsuf); + if (!test && mstack) { + test = 1; + addwhat = CC_FILES; + } + } + /* Filename didn't match? */ + if (!test) + continue; + if (!all) { + /* We still have to check the file type, so prepare * + * the path buffer by appending the filename. */ + strcpy(q, n); + /* And do the stat. */ + if (stat(p, &buf) < 0) + continue; + } + if (all || + (dirs && S_ISDIR(buf.st_mode)) || + (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) { + /* If we want all files or the file has the right type... */ + if (*psuf) { + /* We have to test for a path suffix. */ + int o = strlen(p), tt; + + /* Append it to the path buffer. */ + strcpy(p + o, psuf); + + /* Do we have to use globbing? */ + if (ispattern || + (ns && comppatmatch && *comppatmatch)) { + /* Yes, so append a `*' if needed. */ + if (ns && comppatmatch && *comppatmatch == '*') { + int tl = strlen(p); + + p[tl] = Star; + p[tl + 1] = '\0'; + } + /* Do the globbing... */ + remnulargs(p); + addlinknode(l, p); + globlist(l); + /* And see if that produced a filename. */ + tt = nonempty(l); + while (ugetnode(l)); + } else + /* Otherwise just check, if we have access * + * to the file. */ + tt = !access(p, F_OK); + + p[o] = '\0'; + if (tt) + /* Ok, we can add the filename to the * + * list of matches. */ + addmatch(dupstring(n), NULL); + } else + /* We want all files, so just add the name * + * to the matches. */ + addmatch(dupstring(n), NULL); + } + } + } + closedir(d); + } + opts[NULLGLOB] = ng; + addwhat = aw; } -/**/ -static char * -get_complist(Param pm) -{ - return complist; -} +/* This returns the node with the given data. */ +/* ...should probably be moved to linklist.c. */ -/**/ -static char * -get_unambig(Param pm) +static LinkNode +findnode(LinkList list, void *dat) { - return unambig_dataptr(NULL); + LinkNode tmp = list->first; + + while (tmp && tmp->dat != dat) tmp = tmp->next; + + return tmp; } -/**/ -static zlong -get_unambig_curs(Param pm) -{ - int c; +/* A simple counter to avoid endless recursion between old and new style * + * completion. */ - unambig_dataptr(&c); +static int cdepth = 0; - return c; -} +#define MAX_CDEPTH 16 /**/ -static void -compunsetfn(Param pm, int exp) +static int +makecomplistctl(int flags) { - if (exp) { - if (PM_TYPE(pm->flags) == PM_SCALAR) { - zsfree(*((char **) pm->u.data)); - *((char **) pm->u.data) = ztrdup(""); - } else if (PM_TYPE(pm->flags) == PM_ARRAY) { - freearray(*((char ***) pm->u.data)); - *((char ***) pm->u.data) = zcalloc(sizeof(char *)); - } - pm->flags |= PM_UNSET; - } + int ret; + + if (cdepth == MAX_CDEPTH) + return 0; + + cdepth++; + SWITCHHEAPS(compheap) { + HEAPALLOC { + int ooffs = offs, lip, lp; + char *str = comp_str(&lip, &lp, 0), *t; + char *os = cmdstr, **ow = clwords, **p, **q, qc; + int on = clwnum, op = clwpos, ois = instring, oib = inbackt; + char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq; + + if (compquote && (qc = *compquote)) { + if (qc == '`') { + instring = 0; + inbackt = 0; + autoq = '\0'; + } else { + instring = (qc == '\'' ? 1 : 2); + inbackt = 0; + autoq = qc; + } + } else { + instring = inbackt = 0; + autoq = '\0'; + } + qipre = ztrdup(compqiprefix ? compqiprefix : ""); + qisuf = ztrdup(compqisuffix ? compqisuffix : ""); + isuf = dupstring(compisuffix); + ctokenize(isuf); + remnulargs(isuf); + clwnum = arrlen(compwords); + clwpos = compcurrent - 1; + cmdstr = ztrdup(compwords[0]); + clwords = (char **) zalloc((clwnum + 1) * sizeof(char *)); + for (p = compwords, q = clwords; *p; p++, q++) { + t = dupstring(*p); + tokenize(t); + remnulargs(t); + *q = ztrdup(t); + } + *q = NULL; + offs = lip + lp; + incompfunc = 2; + ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags); + incompfunc = 1; + isuf = oisuf; + zsfree(qipre); + zsfree(qisuf); + qipre = oqp; + qisuf = oqs; + instring = ois; + inbackt = oib; + autoq = oaq; + offs = ooffs; + zsfree(cmdstr); + freearray(clwords); + cmdstr = os; + clwords = ow; + clwnum = on; + clwpos = op; + } LASTALLOC; + } SWITCHBACKHEAPS; + cdepth--; + + return ret; } +/* This function gets the compctls for the given command line and * + * adds all completions for them. */ + /**/ -void -comp_setunset(int rset, int runset, int kset, int kunset) +static int +makecomplistglobal(char *os, int incmd, int lst, int flags) { - Param *p; + Compctl cc = NULL; + char *s; - if (comprpms && (rset >= 0 || runset >= 0)) { - for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) { - if (rset & 1) - (*p)->flags &= ~PM_UNSET; - if (runset & 1) - (*p)->flags |= PM_UNSET; + ccont = CC_CCCONT; + cc_dummy.suffix = NULL; + + if (linwhat == IN_ENV) { + /* Default completion for parameter values. */ + if (!(flags & CFN_DEFAULT)) { + cc = &cc_default; + keypm = NULL; + } + } else if (linwhat == IN_MATH) { + if (!(flags & CFN_DEFAULT)) { + if (insubscr >= 2) { + /* Inside subscript of assoc array, complete keys. */ + cc_dummy.mask = 0; + cc_dummy.suffix = (insubscr == 2 ? "]" : ""); + } else { + /* Other math environment, complete paramete names. */ + keypm = NULL; + cc_dummy.mask = CC_PARAMS; + } + cc = &cc_dummy; + cc_dummy.refc = 10000; + } + } else if (linwhat == IN_COND) { + /* We try to be clever here: in conditions we complete option * + * names after a `-o', file names after `-nt', `-ot', and `-ef' * + * and file names and parameter names elsewhere. */ + if (!(flags & CFN_DEFAULT)) { + s = clwpos ? clwords[clwpos - 1] : ""; + cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS : + ((*s == '-' && s[1] && !s[2]) || + !strcmp("-nt", s) || + !strcmp("-ot", s) || + !strcmp("-ef", s)) ? CC_FILES : + (CC_FILES | CC_PARAMS); + cc = &cc_dummy; + cc_dummy.refc = 10000; + keypm = NULL; + } + } else if (linredir) { + if (!(flags & CFN_DEFAULT)) { + /* In redirections use default completion. */ + cc = &cc_default; + keypm = NULL; } + } else { + /* Otherwise get the matches for the command. */ + keypm = NULL; + return makecomplistcmd(os, incmd, flags); } - if (comprpms && (kset >= 0 || kunset >= 0)) { - for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) { - if (kset & 1) - (*p)->flags &= ~PM_UNSET; - if (kunset & 1) - (*p)->flags |= PM_UNSET; + if (cc) { + /* First, use the -T compctl. */ + if (!(flags & CFN_FIRST)) { + makecomplistcc(&cc_first, os, incmd); + + if (!(ccont & CC_CCCONT)) + return 0; } + makecomplistcc(cc, os, incmd); + return 1; } + return 0; } +/* This produces the matches for a command. */ + /**/ static int -comp_wrapper(List list, FuncWrap w, char *name) +makecomplistcmd(char *os, int incmd, int flags) { - if (incompfunc != 1) - return 1; - else { - char *orest, *opre, *osuf, *oipre, *oisuf, **owords; - char *oqipre, *oqisuf, *oq, *oqi; - zlong ocur; - unsigned int runset = 0, kunset = 0, m, sm; - Param *pp; - - m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | - CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX; - for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) { - if ((m & 1) && ((*pp)->flags & PM_UNSET)) - runset |= sm; - } - if (compkpms[CPN_RESTORE]->flags & PM_UNSET) - kunset = CP_RESTORE; - orest = comprestore; - comprestore = ztrdup("auto"); - ocur = compcurrent; - opre = dupstring(compprefix); - osuf = dupstring(compsuffix); - oipre = dupstring(compiprefix); - oisuf = dupstring(compisuffix); - oqipre = dupstring(compqiprefix); - oqisuf = dupstring(compqisuffix); - oq = dupstring(compquote); - oqi = dupstring(compquoting); - - HEAPALLOC { - owords = arrdup(compwords); - } LASTALLOC; + Compctl cc; + Compctlp ccp; + char *s; + int ret = 0; - runshfunc(list, w, name); - - if (comprestore && !strcmp(comprestore, "auto")) { - compcurrent = ocur; - zsfree(compprefix); - compprefix = ztrdup(opre); - zsfree(compsuffix); - compsuffix = ztrdup(osuf); - zsfree(compiprefix); - compiprefix = ztrdup(oipre); - zsfree(compisuffix); - compisuffix = ztrdup(oisuf); - zsfree(compqiprefix); - compqiprefix = ztrdup(oqipre); - zsfree(compqisuffix); - compqisuffix = ztrdup(oqisuf); - zsfree(compquote); - compquote = ztrdup(oq); - zsfree(compquoting); - compquoting = ztrdup(oqi); - freearray(compwords); - PERMALLOC { - compwords = arrdup(owords); - } LASTALLOC; - comp_setunset(CP_COMPSTATE | - (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX | - CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX | - CP_QIPREFIX | CP_QISUFFIX)), - (runset & CP_ALLREALS), - (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS)); - } else - comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE), - (kunset & CP_RESTORE)); - zsfree(comprestore); - comprestore = orest; + /* First, use the -T compctl. */ + if (!(flags & CFN_FIRST)) { + makecomplistcc(&cc_first, os, incmd); - return 0; + if (!(ccont & CC_CCCONT)) + return 0; + } + /* Then search the pattern compctls, with the command name and the * + * full pathname of the command. */ + if (cmdstr) { + ret |= makecomplistpc(os, incmd); + if (!(ccont & CC_CCCONT)) + return ret; } + /* If the command string starts with `=', try the path name of the * + * command. */ + if (cmdstr && cmdstr[0] == Equals) { + char *c = findcmd(cmdstr + 1, 1); + + if (c) { + zsfree(cmdstr); + cmdstr = ztrdup(c); + } + } + + /* Find the compctl for this command, trying the full name and then * + * the trailing pathname component. If that doesn't yield anything, * + * use default completion. */ + if (incmd) + cc = &cc_compos; + else if (!(cmdstr && + (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) && + (cc = ccp->cc)) || + ((s = dupstring(cmdstr)) && remlpaths(&s) && + (ccp = (Compctlp) compctltab->getnode(compctltab, s)) && + (cc = ccp->cc))))) { + if (flags & CFN_DEFAULT) + return ret; + cc = &cc_default; + } else + ret|= 1; + makecomplistcc(cc, os, incmd); + return ret; } +/* This add the matches for the pattern compctls. */ + /**/ static int -comp_check(void) +makecomplistpc(char *os, int incmd) { - if (incompfunc != 1) { - zerr("condition can only be used in completion function", NULL, 0); - return 0; + Patcomp pc; + Patprog pat; + char *s = findcmd(cmdstr, 1); + int ret = 0; + + for (pc = patcomps; pc; pc = pc->next) { + if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) && + (pattry(pat, cmdstr) || + (s && pattry(pat, s)))) { + makecomplistcc(pc->cc, os, incmd); + ret |= 2; + if (!(ccont & CC_CCCONT)) + return ret; + } } - return 1; + return ret; } +/* This produces the matches for one compctl. */ + /**/ -static int -cond_psfix(char **a, int id) +static void +makecomplistcc(Compctl cc, char *s, int incmd) { - if (comp_check()) { - if (a[1]) - return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1), - 0, NULL, 0); - else - return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0); - } - return 0; + cc->refc++; + addlinknode(ccused, cc); + + ccont = 0; + + makecomplistor(cc, s, incmd, 0, 0); } +/* This adds the completions for one run of [x]or'ed completions. */ + /**/ -static int -cond_range(char **a, int id) +static void +makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub) { - return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0, - (id ? cond_str(a, 1, 1) : NULL), 0); + int mn, ct, um = usemenu; + + /* Loop over xors. */ + do { + mn = mnum; + + /* Loop over ors. */ + do { + /* Reset the range information if we are not in a sub-list. */ + if (!sub) { + brange = 0; + erange = clwnum - 1; + } + usemenu = 0; + makecomplistlist(cc, s, incmd, compadd); + um |= usemenu; + + ct = cc->mask2 & CC_XORCONT; + + cc = cc->xor; + } while (cc && ct); + + /* Stop if we got some matches. */ + if (mn != mnum) + break; + if (cc) { + ccont &= ~(CC_DEFCONT | CC_PATCONT); + if (!sub) + ccont &= ~CC_CCCONT; + } + } while (cc); + + usemenu = um; } +/* This dispatches for simple and extended completion. */ + /**/ static void -cmsetfn(Param pm, char **v) +makecomplistlist(Compctl cc, char *s, int incmd, int compadd) { - set_gmatcher(pm->nam, v); + int oloffs = offs, owe = we, owb = wb, ocs = cs; + + if (cc->ext) + /* Handle extended completion. */ + makecomplistext(cc, s, incmd); + else + /* Only normal flags. */ + makecomplistflags(cc, s, incmd, compadd); + + /* Reset some information variables for the next try. */ + errflag = 0; + offs = oloffs; + wb = owb; + we = owe; + cs = ocs; } +/* This add matches for extended completion patterns */ + /**/ -static char ** -cmgetfn(Param pm) +static void +makecomplistext(Compctl occ, char *os, int incmd) { - int num; - Cmlist p; - char **ret, **q; - - for (num = 0, p = cmatcher; p; p = p->next, num++); - - ret = (char **) zhalloc((num + 1) * sizeof(char *)); + Compctl compc; + Compcond or, cc; + Patprog pprog; + int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins; + char *sc = NULL, *s, *ss; + + ins = (instring ? instring : (inbackt ? 3 : 0)); + + /* This loops over the patterns separated by `-'s. */ + for (compc = occ->ext; compc; compc = compc->next) { + compadd = t = brange = 0; + erange = clwnum - 1; + /* This loops over OR'ed patterns. */ + for (cc = compc->cond; cc && !t; cc = or) { + or = cc->or; + /* This loops over AND'ed patterns. */ + for (t = 1; cc && t; cc = cc->and) { + /* And this loops over [...] pairs. */ + for (t = i = 0; i < cc->n && !t; i++) { + s = NULL; + brange = 0; + erange = clwnum - 1; + switch (cc->type) { + case CCT_QUOTE: + t = ((cc->u.s.s[i][0] == 's' && ins == 1) || + (cc->u.s.s[i][0] == 'd' && ins == 2) || + (cc->u.s.s[i][0] == 'b' && ins == 3)); + break; + case CCT_POS: + tt = clwpos; + goto cct_num; + case CCT_NUMWORDS: + tt = clwnum; + cct_num: + if ((a = cc->u.r.a[i]) < 0) + a += clwnum; + if ((b = cc->u.r.b[i]) < 0) + b += clwnum; + if (cc->type == CCT_POS) + brange = a, erange = b; + t = (tt >= a && tt <= b); + break; + case CCT_CURSUF: + case CCT_CURPRE: + s = ztrdup(clwpos < clwnum ? os : ""); + untokenize(s); + if (isset(COMPLETEINWORD)) s[offs] = '\0'; + sc = rembslash(cc->u.s.s[i]); + a = strlen(sc); + if (!strncmp(s, sc, a)) { + compadd = (cc->type == CCT_CURSUF ? a : 0); + t = 1; + } + break; + case CCT_CURSUB: + case CCT_CURSUBC: + if (clwpos < 0 || clwpos >= clwnum) + t = 0; + else { + s = ztrdup(os); + untokenize(s); + if (isset(COMPLETEINWORD)) s[offs] = '\0'; + a = getcpat(s, + cc->u.s.p[i], + cc->u.s.s[i], + cc->type == CCT_CURSUBC); + if (a != -1) + compadd = a, t = 1; + } + break; + + case CCT_CURPAT: + case CCT_CURSTR: + tt = clwpos; + goto cct_str; + case CCT_WORDPAT: + case CCT_WORDSTR: + tt = 0; + cct_str: + if ((a = tt + cc->u.s.p[i]) < 0) + a += clwnum; + s = ztrdup((a < 0 || a >= clwnum) ? "" : + clwords[a]); + untokenize(s); + + if (cc->type == CCT_CURPAT || + cc->type == CCT_WORDPAT) { + tokenize(ss = dupstring(cc->u.s.s[i])); + t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) && + pattry(pprog, s)); + } else + t = (!strcmp(s, rembslash(cc->u.s.s[i]))); + break; + case CCT_RANGESTR: + case CCT_RANGEPAT: + if (cc->type == CCT_RANGEPAT) + tokenize(sc = dupstring(cc->u.l.a[i])); + for (j = clwpos - 1; j > 0; j--) { + untokenize(s = ztrdup(clwords[j])); + if (cc->type == CCT_RANGESTR) + sc = rembslash(cc->u.l.a[i]); + if (cc->type == CCT_RANGESTR ? + !strncmp(s, sc, strlen(sc)) : + ((pprog = patcompile(sc, PAT_STATIC, 0)) && + pattry(pprog, s))) { + zsfree(s); + brange = j + 1; + t = 1; + break; + } + zsfree(s); + } + if (t && cc->u.l.b[i]) { + if (cc->type == CCT_RANGEPAT) + tokenize(sc = dupstring(cc->u.l.b[i])); + for (j++; j < clwnum; j++) { + untokenize(s = ztrdup(clwords[j])); + if (cc->type == CCT_RANGESTR) + sc = rembslash(cc->u.l.b[i]); + if (cc->type == CCT_RANGESTR ? + !strncmp(s, sc, strlen(sc)) : + ((pprog = patcompile(sc, PAT_STATIC, 0)) && + pattry(pprog, s))) { + zsfree(s); + erange = j - 1; + t = clwpos <= erange; + break; + } + zsfree(s); + } + } + s = NULL; + } + zsfree(s); + } + } + } + if (t) { + /* The patterns matched, use the flags. */ + m = 1; + ccont &= ~(CC_PATCONT | CC_DEFCONT); + makecomplistor(compc, os, incmd, compadd, 1); + if (!d && (ccont & CC_DEFCONT)) { + d = 1; + compadd = 0; + brange = 0; + erange = clwnum - 1; + makecomplistflags(occ, os, incmd, 0); + } + if (!(ccont & CC_PATCONT)) + break; + } + } + /* If no pattern matched, use the standard flags. */ + if (!m) { + compadd = 0; + brange = 0; + erange = clwnum - 1; + makecomplistflags(occ, os, incmd, 0); + } +} - for (q = ret, p = cmatcher; p; p = p->next, q++) - *q = dupstring(p->str); - *q = NULL; +/**/ +static int +sep_comp_string(char *ss, char *s, int noffs) +{ + LinkList foo = newlinklist(); + LinkNode n; + int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs; + int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll; + int ois = instring, oib = inbackt; + char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs; + + swb = swe = soffs = 0; + ns = NULL; + + /* Put the string in the lexer buffer and call the lexer to * + * get the words we have to expand. */ + zleparse = 1; + addedx = 1; + noerrs = 1; + lexsave(); + tmp = (char *) zhalloc(tl = sl + 3 + strlen(s)); + strcpy(tmp, ss); + tmp[sl] = ' '; + memcpy(tmp + sl + 1, s, noffs); + tmp[(scs = cs = sl + 1 + noffs)] = 'x'; + strcpy(tmp + sl + 2 + noffs, s + noffs); + inpush(dupstrspace(tmp), 0, NULL); + line = (unsigned char *) tmp; + ll = tl - 1; + strinbeg(0); + noaliases = 1; + do { + ctxtlex(); + if (tok == LEXERR) { + int j; + + if (!tokstr) + break; + for (j = 0, p = tokstr; *p; p++) + if (*p == Snull || *p == Dnull) + j++; + if (j & 1) { + tok = STRING; + if (p > tokstr && p[-1] == ' ') + p[-1] = '\0'; + } + } + if (tok == ENDINPUT || tok == LEXERR) + break; + if (tokstr && *tokstr) + addlinknode(foo, (p = ztrdup(tokstr))); + else + p = NULL; + if (!got && !zleparse) { + DPUTS(!p, "no current word in substr"); + got = 1; + cur = i; + swb = wb - 1; + swe = we - 1; + soffs = cs - swb; + chuck(p + soffs); + ns = dupstring(p); + } + i++; + } while (tok != ENDINPUT && tok != LEXERR); + noaliases = 0; + strinend(); + inpop(); + errflag = zleparse = 0; + noerrs = ne; + lexrestore(); + wb = owb; + we = owe; + cs = ocs; + line = (unsigned char *) ol; + ll = oll; + if (cur < 0 || i < 1) + return 1; + owb = offs; + offs = soffs; + if ((p = check_param(ns, 0, 1))) { + for (p = ns; *p; p++) + if (*p == Dnull) + *p = '"'; + else if (*p == Snull) + *p = '\''; + } + offs = owb; + if (*ns == Snull || *ns == Dnull) { + instring = (*ns == Snull ? 1 : 2); + inbackt = 0; + swb++; + if (ns[strlen(ns) - 1] == *ns && ns[1]) + swe--; + autoq = (*ns == Snull ? '\'' : '"'); + } else { + instring = 0; + autoq = '\0'; + } + for (p = ns, i = swb; *p; p++, i++) { + if (INULL(*p)) { + if (i < scs) + soffs--; + if (p[1] || *p != Bnull) { + if (*p == Bnull) { + if (scs == i + 1) + scs++, soffs++; + } else { + if (scs > i--) + scs--; + } + } else { + if (scs == swe) + scs--; + } + chuck(p--); + } + } + sav = s[(i = swb - sl - 1)]; + s[i] = '\0'; + qp = tricat(qipre, s, ""); + s[i] = sav; + if (swe < swb) + swe = swb; + swe -= sl + 1; + sl = strlen(s); + if (swe > sl) + swe = sl, ns[swe - swb + 1] = '\0'; + qs = tricat(s + swe, qisuf, ""); + sl = strlen(ns); + if (soffs > sl) + soffs = sl; + + { + char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf; + int olws = clwsize, olwn = clwnum, olwp = clwpos; + int obr = brange, oer = erange, oof = offs; + unsigned long occ = ccont; + + clwsize = clwnum = countlinknodes(foo); + clwords = (char **) zalloc((clwnum + 1) * sizeof(char *)); + for (n = firstnode(foo), i = 0; n; incnode(n), i++) { + p = clwords[i] = (char *) getdata(n); + untokenize(p); + } + clwords[i] = NULL; + clwpos = cur; + cmdstr = ztrdup(clwords[0]); + brange = 0; + erange = clwnum - 1; + qipre = qp; + qisuf = qs; + offs = soffs; + ccont = CC_CCCONT; + makecomplistcmd(ns, !clwpos, CFN_FIRST); + ccont = occ; + offs = oof; + zsfree(cmdstr); + cmdstr = os; + freearray(clwords); + clwords = ow; + clwsize = olws; + clwnum = olwn; + clwpos = olwp; + brange = obr; + erange = oer; + zsfree(qipre); + qipre = oqp; + zsfree(qisuf); + qisuf = oqs; + } + autoq = oaq; + instring = ois; + inbackt = oib; - return ret; + return 0; } +/* This adds the completions for the flags in the given compctl. */ + /**/ static void -cmunsetfn(Param pm, int exp) +makecomplistflags(Compctl cc, char *s, int incmd, int compadd) { - char *dummy[1]; + int t, sf1, sf2, ooffs, um = usemenu, delit, oaw, gflags; + int mn = mnum, ohp = haspattern; + char *p, *sd = NULL, *tt, *s1, *s2, *os = dupstring(s); + struct cmlist ms; + + ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT)); + + if (incompfunc != 1 && findnode(ccstack, cc)) + return; + + MUSTUSEHEAP("complistflags"); - dummy[0] = NULL; - set_gmatcher(pm->nam, dummy); + addlinknode(ccstack, cc); + + if (incompfunc != 1 && allccs) { + if (findnode(allccs, cc)) { + uremnode(ccstack, firstnode(ccstack)); + return; + } + addlinknode(allccs, cc); + } + /* Go to the end of the word if complete_in_word is not set. */ + if (unset(COMPLETEINWORD) && cs != we) + cs = we, offs = strlen(s); + + s = dupstring(s); + delit = ispattern = 0; + usemenu = um; + patcomp = filecomp = NULL; + rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf = + fpre = fsuf = ipre = ripre = prpre = + qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL; + + curcc = cc; + + mflags = 0; + gflags = (((cc->mask2 & CC_NOSORT ) ? CGF_NOSORT : 0) | + ((cc->mask2 & CC_UNIQALL) ? CGF_UNIQALL : 0) | + ((cc->mask2 & CC_UNIQCON) ? CGF_UNIQCON : 0)); + if (cc->gname) { + endcmgroup(NULL); + begcmgroup(cc->gname, gflags); + } + if (cc->ylist) { + endcmgroup(NULL); + begcmgroup(NULL, gflags); + } + if (cc->mask & CC_REMOVE) + mflags |= CMF_REMOVE; + if (cc->explain) { + curexpl = (Cexpl) zhalloc(sizeof(struct cexpl)); + curexpl->count = curexpl->fcount = 0; + } else + curexpl = NULL; + /* compadd is the number of characters we have to ignore at the * + * beginning of the word. */ + if (compadd) { + ipre = dupstring(s); + ipre[compadd] = '\0'; + untokenize(ipre); + wb += compadd; + s += compadd; + if ((offs -= compadd) < 0) { + /* It's bigger than our word prefix, so we can't help here... */ + uremnode(ccstack, firstnode(ccstack)); + return; + } + } else + ipre = NULL; + + if (cc->matcher) { + ms.next = mstack; + ms.matcher = cc->matcher; + mstack = &ms; + + if (!mnum) + add_bmatchers(cc->matcher); + + addlinknode(matchers, cc->matcher); + cc->matcher->refc++; + } + if (mnum && (mstack || bmatchers)) + update_bmatchers(); + + /* Insert the prefix (compctl -P), if any. */ + if (cc->prefix) { + int pl = 0; + + if (*s) { + char *dp = rembslash(cc->prefix); + /* First find out how much of the prefix is already on the line. */ + sd = dupstring(s); + untokenize(sd); + pl = pfxlen(dp, sd); + s += pl; + sd += pl; + offs -= pl; + } + } + /* Does this compctl have a suffix (compctl -S)? */ + if (cc->suffix) { + char *sdup = rembslash(cc->suffix); + int sl = strlen(sdup), suffixll; + + /* Ignore trailing spaces. */ + for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--); + p[1] = '\0'; + + if (!sd) { + sd = dupstring(s); + untokenize(sd); + } + /* If the suffix is already there, ignore it (and don't add * + * it again). */ + if (*sd && (suffixll = strlen(sd)) >= sl && + offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl)) + s[suffixll - sl] = '\0'; + } + /* Do we have one of the special characters `~' and `=' at the beginning? */ + if (incompfunc || ((ic = *s) != Tilde && ic != Equals)) + ic = 0; + + /* Check if we have to complete a parameter name... */ + if (!incompfunc && (p = check_param(s, 1, 0))) { + s = p; + /* And now make sure that we complete parameter names. */ + cc = &cc_dummy; + cc_dummy.refc = 10000; + cc_dummy.mask = CC_PARAMS | CC_ENVVARS; + } + ooffs = offs; + /* If we have to ignore the word, do that. */ + if (cc->mask & CC_DELETE) { + delit = 1; + *s = '\0'; + offs = 0; + if (isset(AUTOMENU)) + usemenu = 1; + } + + /* Compute line prefix/suffix. */ + lpl = offs; + lpre = zhalloc(lpl + 1); + memcpy(lpre, s, lpl); + lpre[lpl] = '\0'; + qlpre = quotename(lpre, NULL); + lsuf = dupstring(s + offs); + lsl = strlen(lsuf); + qlsuf = quotename(lsuf, NULL); + + /* First check for ~.../... */ + if (ic == Tilde) { + for (p = lpre + lpl; p > lpre; p--) + if (*p == '/') + break; + + if (*p == '/') + ic = 0; + } + /* Compute real prefix/suffix. */ + + noreal = !delit; + for (p = lpre; *p && *p != String && *p != Tick; p++); + tt = ic && !ispar ? lpre + 1 : lpre; + rpre = (*p || *lpre == Tilde || *lpre == Equals) ? + (noreal = 0, getreal(tt)) : + dupstring(tt); + qrpre = quotename(rpre, NULL); + + for (p = lsuf; *p && *p != String && *p != Tick; p++); + rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf); + qrsuf = quotename(rsuf, NULL); + + /* Check if word is a pattern. */ + + for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1; + p >= rpre && (ispattern != 3 || !sf1); + p--) + if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde))) + ispattern |= sf1 ? 1 : 2; + else if (*p == '/') { + sf1++; + if (!s1) + s1 = p; + } + rsl = strlen(rsuf); + for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++) + if (itok(*p)) + t |= sf2 ? 4 : 2; + else if (*p == '/') { + sf2++; + if (!s2) + s2 = p; + } + ispattern = ispattern | t; + + /* But if we were asked not to do glob completion, we never treat the * + * thing as a pattern. */ + if (!comppatmatch || !*comppatmatch) + ispattern = 0; + + if (ispattern) { + /* The word should be treated as a pattern, so compute the matcher. */ + p = (char *) zhalloc(rpl + rsl + 2); + strcpy(p, rpre); + if (rpl && p[rpl - 1] != Star && + (!comppatmatch || *comppatmatch == '*')) { + p[rpl] = Star; + strcpy(p + rpl + 1, rsuf); + } else + strcpy(p + rpl, rsuf); + patcomp = patcompile(p, 0, NULL); + haspattern = 1; + } + if (!patcomp) { + untokenize(rpre); + untokenize(rsuf); + + rpl = strlen(rpre); + rsl = strlen(rsuf); + } + untokenize(lpre); + untokenize(lsuf); + + if (!(cc->mask & CC_DELETE)) + hasmatched = 1; + + /* Handle completion of files specially (of course). */ + + if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) { + /* s1 and s2 point to the last/first slash in the prefix/suffix. */ + if (!s1) + s1 = rpre; + if (!s2) + s2 = rsuf + rsl; + + /* Compute the path prefix/suffix. */ + if (*s1 != '/') + ppre = ""; + else + ppre = dupstrpfx(rpre, s1 - rpre + 1); + psuf = dupstring(s2); + + if (cs != wb) { + char save = line[cs]; + + line[cs] = 0; + lppre = dupstring((char *) line + wb + + (qipre && *qipre ? + (strlen(qipre) - + (*qipre == '\'' || *qipre == '\"')) : 0)); + line[cs] = save; + if (brbeg) { + Brinfo bp; + + for (bp = brbeg; bp; bp = bp->next) + strcpy(lppre + bp->qpos, + lppre + bp->qpos + strlen(bp->str)); + } + if ((p = strrchr(lppre, '/'))) { + p[1] = '\0'; + lppl = strlen(lppre); + } else if (!sf1) { + lppre = NULL; + lppl = 0; + } else { + lppre = ppre; + lppl = strlen(lppre); + } + } else { + lppre = NULL; + lppl = 0; + } + if (cs != we) { + int end = we; + char save = line[end]; + + if (qisuf && *qisuf) { + int ql = strlen(qisuf); + + end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"'); + } + line[end] = 0; + lpsuf = dupstring((char *) (line + cs)); + line[end] = save; + if (brend) { + Brinfo bp; + char *p; + int bl; + + for (bp = brend; bp; bp = bp->next) { + p = lpsuf + (we - cs) - bp->qpos - (bl = strlen(bp->str)); + strcpy(p, p + bl); + } + } + if (!(lpsuf = strchr(lpsuf, '/')) && sf2) + lpsuf = psuf; + lpsl = (lpsuf ? strlen(lpsuf) : 0); + } else { + lpsuf = NULL; + lpsl = 0; + } + + /* And get the file prefix. */ + fpre = dupstring(((s1 == s || s1 == rpre || ic) && + (*s != '/' || cs == wb)) ? s1 : s1 + 1); + qfpre = quotename(fpre, NULL); + /* And the suffix. */ + fsuf = dupstrpfx(rsuf, s2 - rsuf); + qfsuf = quotename(fsuf, NULL); + + if (comppatmatch && *comppatmatch && (ispattern & 2)) { + int t2; + + /* We have to use globbing, so compute the pattern from * + * the file prefix and suffix with a `*' between them. */ + p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2); + strcpy(p, fpre); + if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star && + (!comppatmatch || *comppatmatch == '*')) + p[t2++] = Star; + strcpy(p + t2, fsuf); + filecomp = patcompile(p, 0, NULL); + } + if (!filecomp) { + untokenize(fpre); + untokenize(fsuf); + + fpl = strlen(fpre); + fsl = strlen(fsuf); + } + addwhat = -1; + + /* Completion after `~', maketildelist adds the usernames * + * and named directories. */ + if (ic == Tilde) { + char *oi = ipre; + + ipre = (ipre ? dyncat("~", ipre) : "~"); + maketildelist(); + ipre = oi; + } else if (ic == Equals) { + /* Completion after `=', get the command names from * + * the cmdnamtab and aliases from aliastab. */ + char *oi = ipre; + + ipre = (ipre ? dyncat("=", ipre) : "="); + if (isset(HASHLISTALL)) + cmdnamtab->filltable(cmdnamtab); + dumphashtable(cmdnamtab, -7); + dumphashtable(aliastab, -2); + ipre = oi; + } else { + /* Normal file completion... */ + if (ispattern & 1) { + /* But with pattern matching. */ + LinkList l = newlinklist(); + LinkNode n; + int ng = opts[NULLGLOB]; + + opts[NULLGLOB] = 1; + + addwhat = 0; + p = (char *) zhalloc(lpl + lsl + 3); + strcpy(p, lpre); + if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*') + strcat(p, "*"); + strcat(p, lsuf); + if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')') + strcat(p, "*"); + + /* Do the globbing. */ + tokenize(p); + remnulargs(p); + addlinknode(l, p); + globlist(l); + + if (nonempty(l)) { + /* And add the resulting words. */ + mflags |= CMF_FILE; + for (n = firstnode(l); n; incnode(n)) + addmatch(getdata(n), NULL); + mflags &= !CMF_FILE; + } + opts[NULLGLOB] = ng; + } else { + char **dirs = 0, *ta[2]; + + /* No pattern matching. */ + addwhat = CC_FILES; + + if (cc->withd) { + char **pp, **npp, *tp; + int tl = strlen(ppre) + 2, pl; + + if ((pp = get_user_var(cc->withd))) { + dirs = npp = + (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1)); + while (*pp) { + pl = strlen(*pp); + tp = (char *) zhalloc(strlen(*pp) + tl); + strcpy(tp, *pp); + tp[pl] = '/'; + strcpy(tp + pl + 1, ppre); + *npp++ = tp; + pp++; + } + *npp = '\0'; + } + } + if (!dirs) { + dirs = ta; + if (cc->withd) { + char *tp; + int pl = strlen(cc->withd); + + ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2); + strcpy(tp, cc->withd); + tp[pl] = '/'; + strcpy(tp + pl + 1, ppre); + } else + ta[0] = ppre; + ta[1] = NULL; + } + while (*dirs) { + prpre = *dirs; + + if (sf2) + /* We are in the path, so add only directories. */ + gen_matches_files(1, 0, 0); + else { + if (cc->mask & CC_FILES) + /* Add all files. */ + gen_matches_files(0, 0, 1); + else if (cc->mask & CC_COMMPATH) { + /* Completion of command paths. */ + if (sf1 || cc->withd) + /* There is a path prefix, so add * + * directories and executables. */ + gen_matches_files(1, 1, 0); + else { + /* No path prefix, so add the things * + * reachable via the PATH variable. */ + char **pc = path, *pp = prpre; + + for (; *pc; pc++) + if (!**pc || (pc[0][0] == '.' && !pc[0][1])) + break; + if (*pc) { + prpre = "./"; + gen_matches_files(1, 1, 0); + prpre = pp; + } + } + } else if (cc->mask & CC_DIRS) + gen_matches_files(1, 0, 0); + /* The compctl has a glob pattern (compctl -g). */ + if (cc->glob) { + int ns, pl = strlen(prpre), o, paalloc; + char *g = dupstring(cc->glob), *pa; + char *p2, *p3; + int ne = noerrs, md = opts[MARKDIRS]; + + /* These are used in the globbing code to make * + * things a bit faster. */ + if (ispattern || mstack) + glob_pre = glob_suf = NULL; + else { + glob_pre = fpre; + glob_suf = fsuf; + } + noerrs = 1; + addwhat = -6; + o = strlen(prpre); + pa = (char *)zalloc(paalloc = o + PATH_MAX); + strcpy(pa, prpre); + opts[MARKDIRS] = 0; + + /* The compctl -g string may contain more than * + * one pattern, so we need a loop. */ + while (*g) { + LinkList l = newlinklist(); + int ng; + + /* Find the blank terminating the pattern. */ + while (*g && inblank(*g)) + g++; + /* Oops, we already reached the end of the + string. */ + if (!*g) + break; + for (p = g + 1; *p && !inblank(*p); p++) + if (*p == '\\' && p[1]) + p++; + /* Get the pattern string. */ + tokenize(g = dupstrpfx(g, p - g)); + if (*g == '=') + *g = Equals; + if (*g == '~') + *g = Tilde; + remnulargs(g); + if ((*g == Equals || *g == Tilde) && !cc->withd) { + /* The pattern has a `~' or `=' at the * + * beginning, so we expand this and use * + * the result. */ + filesub(&g, 0); + addlinknode(l, dupstring(g)); + } else if (*g == '/' && !cc->withd) + /* The pattern is a full path (starting * + * with '/'), so add it unchanged. */ + addlinknode(l, dupstring(g)); + else { + /* It's a simple pattern, so append it to * + * the path we have on the command line. */ + int minlen = o + strlen(g); + if (minlen >= paalloc) + pa = (char *) + zrealloc(pa, paalloc = minlen+1); + strcpy(pa + o, g); + addlinknode(l, dupstring(pa)); + } + /* Do the globbing. */ + ng = opts[NULLGLOB]; + opts[NULLGLOB] = 1; + globlist(l); + opts[NULLGLOB] = ng; + /* Get the results. */ + if (nonempty(l) && peekfirst(l)) { + for (p2 = (char *)peekfirst(l); *p2; p2++) + if (itok(*p2)) + break; + if (!*p2) { + if ((*g == Equals || *g == Tilde || + *g == '/') || cc->withd) { + /* IF the pattern started with `~', * + * `=', or `/', add the result only, * + * if it really matches what we have * + * on the line. * + * Do this if an initial directory * + * was specified, too. */ + while ((p2 = (char *)ugetnode(l))) + if (strpfx(prpre, p2)) + addmatch(p2 + pl, NULL); + } else { + /* Otherwise ignore the path we * + * prepended to the pattern. */ + while ((p2 = p3 = + (char *)ugetnode(l))) { + for (ns = sf1; *p3 && ns; p3++) + if (*p3 == '/') + ns--; + + addmatch(p3, NULL); + } + } + } + } + pa[o] = '\0'; + g = p; + } + glob_pre = glob_suf = NULL; + noerrs = ne; + opts[MARKDIRS] = md; + + zfree(pa, paalloc); + } + } + dirs++; + } + prpre = NULL; + } + } + lppre = lpsuf = NULL; + lppl = lpsl = 0; + } + if (ic) { + /* Now change the `~' and `=' tokens to the real characters so * + * that things starting with these characters will be added. */ + rpre = dyncat((ic == Tilde) ? "~" : "=", rpre); + rpl++; + qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre); + } + if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) { + /* If we have to complete commands, add alias names, * + * shell functions and builtins too. */ + dumphashtable(aliastab, -3); + dumphashtable(reswdtab, -3); + dumphashtable(shfunctab, -3); + dumphashtable(builtintab, -3); + if (isset(HASHLISTALL)) + cmdnamtab->filltable(cmdnamtab); + dumphashtable(cmdnamtab, -3); + /* And parameter names if autocd and cdablevars are set. */ + if (isset(AUTOCD) && isset(CDABLEVARS)) + dumphashtable(paramtab, -4); + } + oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG; + + if (cc->mask & CC_NAMED) + /* Add named directories. */ + dumphashtable(nameddirtab, addwhat); + if (cc->mask & CC_OPTIONS) + /* Add option names. */ + dumphashtable(optiontab, addwhat); + if (cc->mask & CC_VARS) { + /* And parameter names. */ + dumphashtable(paramtab, -9); + addwhat = oaw; + } + if (cc->mask & CC_BINDINGS) { + /* And zle function names... */ + dumphashtable(thingytab, CC_BINDINGS); + addwhat = oaw; + } + if (cc->keyvar) { + /* This adds things given to the compctl -k flag * + * (from a parameter or a list of words). */ + char **usr = get_user_var(cc->keyvar); + + if (usr) + while (*usr) + addmatch(*usr++, NULL); + } + if (cc->mask & CC_USERS) { + /* Add user names. */ + maketildelist(); + addwhat = oaw; + } + if (cc->func) { + /* This handles the compctl -K flag. */ + List list; + char **r; + int lv = lastval; + + /* Get the function. */ + if ((list = getshfunc(cc->func)) != &dummy_list) { + /* We have it, so build a argument list. */ + LinkList args = newlinklist(); + int osc = sfcontext; + + addlinknode(args, cc->func); + + if (delit) { + p = dupstrpfx(os, ooffs); + untokenize(p); + addlinknode(args, p); + p = dupstring(os + ooffs); + untokenize(p); + addlinknode(args, p); + } else { + addlinknode(args, lpre); + addlinknode(args, lsuf); + } + + /* This flag allows us to use read -l and -c. */ + if (incompfunc != 1) + incompctlfunc = 1; + sfcontext = SFC_COMPLETE; + /* Call the function. */ + doshfunc(cc->func, list, args, 0, 1); + sfcontext = osc; + incompctlfunc = 0; + /* And get the result from the reply parameter. */ + if ((r = get_user_var("reply"))) + while (*r) + addmatch(*r++, NULL); + } + lastval = lv; + } + if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) { + /* Get job names. */ + int i; + char *j; + + for (i = 0; i < MAXJOB; i++) + if ((jobtab[i].stat & STAT_INUSE) && + jobtab[i].procs && jobtab[i].procs->text) { + int stopped = jobtab[i].stat & STAT_STOPPED; + + j = dupstring(jobtab[i].procs->text); + if ((cc->mask & CC_JOBS) || + (stopped && (cc->mask & CC_STOPPED)) || + (!stopped && (cc->mask & CC_RUNNING))) + addmatch(j, NULL); + } + } + if (cc->str) { + /* Get the stuff from a compctl -s. */ + LinkList foo = newlinklist(); + LinkNode n; + int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb; + char *tmpbuf; + + opts[NULLGLOB] = 1; + + /* Put the string in the lexer buffer and call the lexer to * + * get the words we have to expand. */ + zleparse = 1; + lexsave(); + tmpbuf = (char *)zhalloc(strlen(cc->str) + 5); + sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */ + inpush(tmpbuf, 0, NULL); + strinbeg(0); + noaliases = 1; + do { + ctxtlex(); + if (tok == ENDINPUT || tok == LEXERR) + break; + if (!first && tokstr && *tokstr) + addlinknode(foo, ztrdup(tokstr)); + first = 0; + } while (tok != ENDINPUT && tok != LEXERR); + noaliases = 0; + strinend(); + inpop(); + errflag = zleparse = 0; + lexrestore(); + /* Fine, now do full expansion. */ + prefork(foo, 0); + if (!errflag) { + globlist(foo); + if (!errflag) + /* And add the resulting words as matches. */ + for (n = firstnode(foo); n; incnode(n)) + addmatch((char *)n->dat, NULL); + } + opts[NULLGLOB] = ng; + we = oowe; + wb = oowb; + } + if (cc->hpat) { + /* We have a pattern to take things from the history. */ + Patprog pprogc = NULL; + char *e, *h, hpatsav; + int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum; + Histent he = quietgethistent(i, GETHIST_UPWARD); + + /* Parse the pattern, if it isn't the null string. */ + if (*(cc->hpat)) { + char *thpat = dupstring(cc->hpat); + + tokenize(thpat); + pprogc = patcompile(thpat, 0, NULL); + } + /* n holds the number of history line we have to search. */ + if (!n) + n = -1; + + /* Now search the history. */ + while (n-- && he) { + int iwords; + for (iwords = he->nwords - 1; iwords >= 0; iwords--) { + h = he->text + he->words[iwords*2]; + e = he->text + he->words[iwords*2+1]; + hpatsav = *e; + *e = '\0'; + /* We now have a word from the history, ignore it * + * if it begins with a quote or `$'. */ + if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' && + (!pprogc || pattry(pprogc, h))) + /* Otherwise add it if it was matched. */ + addmatch(dupstring(h), NULL); + if (hpatsav) + *e = hpatsav; + } + he = up_histent(he); + } + } + if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS | + CC_READONLYS | CC_SPECIALS | CC_PARAMS))) + /* Add various flavours of parameters. */ + dumphashtable(paramtab, t); + if ((t = cc->mask & CC_SHFUNCS)) + /* Add shell functions. */ + dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); + if ((t = cc->mask & CC_BUILTINS)) + /* Add builtins. */ + dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); + if ((t = cc->mask & CC_EXTCMDS)) { + /* Add external commands */ + if (isset(HASHLISTALL)) + cmdnamtab->filltable(cmdnamtab); + dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); + } + if ((t = cc->mask & CC_RESWDS)) + /* Add reserved words */ + dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); + if ((t = cc->mask & (CC_ALREG | CC_ALGLOB))) + /* Add the two types of aliases. */ + dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); + if (keypm && cc == &cc_dummy) { + /* Add the keys of the parameter in keypm. */ + scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0); + keypm = NULL; + cc_dummy.suffix = NULL; + } + if (!errflag && cc->ylist) { + /* generate the user-defined display list: if anything fails, * + * we silently allow the normal completion list to be used. */ + char **yaptr = NULL, *uv = NULL; + List list; + + if (cc->ylist[0] == '$' || cc->ylist[0] == '(') { + /* from variable */ + uv = cc->ylist + (cc->ylist[0] == '$'); + } else if ((list = getshfunc(cc->ylist)) != &dummy_list) { + /* from function: pass completions as arg list */ + LinkList args = newlinklist(); + LinkNode ln; + Cmatch m; + int osc = sfcontext; + + addlinknode(args, cc->ylist); + for (ln = firstnode(matches); ln; ln = nextnode(ln)) { + m = (Cmatch) getdata(ln); + if (m->ppre) { + char *p = (char *) zhalloc(strlen(m->ppre) + strlen(m->str) + + strlen(m->psuf) + 1); + + sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf); + addlinknode(args, dupstring(p)); + } else + addlinknode(args, dupstring(m->str)); + } + + /* No harm in allowing read -l and -c here, too */ + if (incompfunc != 1) + incompctlfunc = 1; + sfcontext = SFC_COMPLETE; + doshfunc(cc->ylist, list, args, 0, 1); + sfcontext = osc; + incompctlfunc = 0; + uv = "reply"; + } + if (uv) + yaptr = get_user_var(uv); + if ((tt = cc->explain)) { + tt = dupstring(tt); + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { + singsub(&tt); + untokenize(tt); + } + curexpl->str = tt; + if (cc->gname) { + endcmgroup(yaptr); + begcmgroup(cc->gname, gflags); + addexpl(); + } else { + addexpl(); + endcmgroup(yaptr); + begcmgroup("default", 0); + } + } + } else if ((tt = cc->explain)) { + tt = dupstring(tt); + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { + singsub(&tt); + untokenize(tt); + } + curexpl->str = tt; + addexpl(); + } + if (cc->subcmd) { + /* Handle -l sub-completion. */ + char **ow = clwords, *os = cmdstr, *ops = NULL; + int oldn = clwnum, oldp = clwpos, br; + unsigned long occ = ccont; + + ccont = CC_CCCONT; + + /* So we restrict the words-array. */ + if (brange >= clwnum) + brange = clwnum - 1; + if (brange < 1) + brange = 1; + if (erange >= clwnum) + erange = clwnum - 1; + if (erange < 1) + erange = 1; + clwnum = erange - brange + 1; + clwpos = clwpos - brange; + br = brange; + + if (cc->subcmd[0]) { + /* And probably put the command name given to the flag * + * in the array. */ + clwpos++; + clwnum++; + incmd = 0; + ops = clwords[br - 1]; + clwords[br - 1] = ztrdup(cc->subcmd); + cmdstr = ztrdup(cc->subcmd); + clwords += br - 1; + } else { + cmdstr = ztrdup(clwords[br]); + incmd = !clwpos; + clwords += br; + } + /* Produce the matches. */ + makecomplistcmd(s, incmd, CFN_FIRST); + + /* And restore the things we changed. */ + clwords = ow; + zsfree(cmdstr); + cmdstr = os; + clwnum = oldn; + clwpos = oldp; + if (ops) { + zsfree(clwords[br - 1]); + clwords[br - 1] = ops; + } + ccont = occ; + } + if (cc->substr) + sep_comp_string(cc->substr, s, offs); + uremnode(ccstack, firstnode(ccstack)); + if (cc->matcher) + mstack = mstack->next; + + if (mn == mnum) + haspattern = ohp; } + static struct builtin bintab[] = { BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL), - BUILTIN("compgen", 0, bin_compgen, 1, -1, 0, NULL, NULL), - BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL), - BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL), BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL), }; -static struct conddef cotab[] = { - CONDDEF("prefix", 0, cond_psfix, 1, 2, CVT_PREPAT), - CONDDEF("suffix", 0, cond_psfix, 1, 2, CVT_SUFPAT), - CONDDEF("between", 0, cond_range, 2, 2, 1), - CONDDEF("after", 0, cond_range, 1, 1, 0), -}; - -static struct funcwrap wrapper[] = { - WRAPDEF(comp_wrapper), -}; - -static struct paramdef patab[] = { - PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn) -}; - /**/ int setup_compctl(Module m) { - compctltab->printnode = printcompctlp; - makecompparamsptr = makecompparams; - comp_setunsetptr = comp_setunset; + compctlreadptr = compctlread; + createcompctltable(); + cc_compos.mask = CC_COMMPATH; + cc_compos.mask2 = 0; + cc_default.refc = 10000; + cc_default.mask = CC_FILES; + cc_default.mask2 = 0; + cc_first.refc = 10000; + cc_first.mask = 0; + cc_first.mask2 = CC_CCCONT; + + lastccused = NULL; + return 0; } @@ -2672,12 +3723,10 @@ setup_compctl(Module m) int boot_compctl(Module m) { - if(!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) | - addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) | - addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) | - !addwrapper(m, wrapper))) - return 1; - return 0; + addhookfunc("compctl_make", (Hookfn) ccmakehookfn); + addhookfunc("compctl_before", (Hookfn) ccbeforehookfn); + addhookfunc("compctl_after", (Hookfn) ccafterhookfn); + return (addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) != 1); } #ifdef MODULE @@ -2686,10 +3735,10 @@ boot_compctl(Module m) int cleanup_compctl(Module m) { + deletehookfunc("compctl_make", (Hookfn) ccmakehookfn); + deletehookfunc("compctl_before", (Hookfn) ccbeforehookfn); + deletehookfunc("compctl_after", (Hookfn) ccafterhookfn); deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); - deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)); - deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)); - deletewrapper(m, wrapper); return 0; } @@ -2697,9 +3746,12 @@ cleanup_compctl(Module m) int finish_compctl(Module m) { - compctltab->printnode = NULL; - makecompparamsptr = NULL; - comp_setunsetptr = NULL; + deletehashtable(compctltab); + + if (lastccused) + freelinklist(lastccused, (FreeFunc) freecompctl); + + compctlreadptr = fallback_compctlread; return 0; } diff --git a/Src/Zle/compctl.h b/Src/Zle/compctl.h new file mode 100644 index 000000000..9a8ba5692 --- /dev/null +++ b/Src/Zle/compctl.h @@ -0,0 +1,160 @@ +/* + * comp.h - header file for completion + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#undef compctlread + +typedef struct compctlp *Compctlp; +typedef struct compctl *Compctl; +typedef struct compcond *Compcond; +typedef struct patcomp *Patcomp; + +/* node for compctl hash table (compctltab) */ + +struct compctlp { + HashNode next; /* next in hash chain */ + char *nam; /* command name */ + int flags; /* CURRENTLY UNUSED */ + Compctl cc; /* pointer to the compctl desc. */ +}; + +/* for the list of pattern compctls */ + +struct patcomp { + Patcomp next; + char *pat; + Compctl cc; +}; + +/* compctl -x condition */ + +struct compcond { + Compcond and, or; /* the next or'ed/and'ed conditions */ + int type; /* the type (CCT_*) */ + int n; /* the array length */ + union { /* these structs hold the data used to */ + struct { /* test this condition */ + int *a, *b; /* CCT_POS, CCT_NUMWORDS */ + } + r; + struct { /* CCT_CURSTR, CCT_CURPAT,... */ + int *p; + char **s; + } + s; + struct { /* CCT_RANGESTR,... */ + char **a, **b; + } + l; + } + u; +}; + +#define CCT_UNUSED 0 +#define CCT_POS 1 +#define CCT_CURSTR 2 +#define CCT_CURPAT 3 +#define CCT_WORDSTR 4 +#define CCT_WORDPAT 5 +#define CCT_CURSUF 6 +#define CCT_CURPRE 7 +#define CCT_CURSUB 8 +#define CCT_CURSUBC 9 +#define CCT_NUMWORDS 10 +#define CCT_RANGESTR 11 +#define CCT_RANGEPAT 12 +#define CCT_QUOTE 13 + +/* Contains the real description for compctls */ + +struct compctl { + int refc; /* reference count */ + Compctl next; /* next compctl for -x */ + unsigned long mask, mask2; /* masks of things to complete (CC_*) */ + char *keyvar; /* for -k (variable) */ + char *glob; /* for -g (globbing) */ + char *str; /* for -s (expansion) */ + char *func; /* for -K (function) */ + char *explain; /* for -X (explanation) */ + char *ylist; /* for -y (user-defined desc. for listing) */ + char *prefix, *suffix; /* for -P and -S (prefix, suffix) */ + char *subcmd; /* for -l (command name to use) */ + char *substr; /* for -1 (command name to use) */ + char *withd; /* for -w (with directory */ + char *hpat; /* for -H (history pattern) */ + int hnum; /* for -H (number of events to search) */ + char *gname; /* for -J and -V (group name) */ + Compctl ext; /* for -x (first of the compctls after -x) */ + Compcond cond; /* for -x (condition for this compctl) */ + Compctl xor; /* for + (next of the xor'ed compctls) */ + Cmatcher matcher; /* matcher control (-M) */ + char *mstr; /* matcher string */ +}; + +/* objects to complete (mask) */ +#define CC_FILES (1<<0) +#define CC_COMMPATH (1<<1) +#define CC_REMOVE (1<<2) +#define CC_OPTIONS (1<<3) +#define CC_VARS (1<<4) +#define CC_BINDINGS (1<<5) +#define CC_ARRAYS (1<<6) +#define CC_INTVARS (1<<7) +#define CC_SHFUNCS (1<<8) +#define CC_PARAMS (1<<9) +#define CC_ENVVARS (1<<10) +#define CC_JOBS (1<<11) +#define CC_RUNNING (1<<12) +#define CC_STOPPED (1<<13) +#define CC_BUILTINS (1<<14) +#define CC_ALREG (1<<15) +#define CC_ALGLOB (1<<16) +#define CC_USERS (1<<17) +#define CC_DISCMDS (1<<18) +#define CC_EXCMDS (1<<19) +#define CC_SCALARS (1<<20) +#define CC_READONLYS (1<<21) +#define CC_SPECIALS (1<<22) +#define CC_DELETE (1<<23) +#define CC_NAMED (1<<24) +#define CC_QUOTEFLAG (1<<25) +#define CC_EXTCMDS (1<<26) +#define CC_RESWDS (1<<27) +#define CC_DIRS (1<<28) + +#define CC_EXPANDEXPL (1<<30) +#define CC_RESERVED (1<<31) + +/* objects to complete (mask2) */ +#define CC_NOSORT (1<<0) +#define CC_XORCONT (1<<1) +#define CC_CCCONT (1<<2) +#define CC_PATCONT (1<<3) +#define CC_DEFCONT (1<<4) +#define CC_UNIQCON (1<<5) +#define CC_UNIQALL (1<<6) diff --git a/Src/Zle/compctl.mdd b/Src/Zle/compctl.mdd index c52285c72..1bebb9338 100644 --- a/Src/Zle/compctl.mdd +++ b/Src/Zle/compctl.mdd @@ -1,9 +1,6 @@ -moddeps="comp1" +moddeps="complete" -autobins="compctl compgen compadd compset" - -autoprefixconds="prefix suffix between after" - -autoparams="compmatchers" +autobins="compctl" +headers="compctl.h" objects="compctl.o" diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c new file mode 100644 index 000000000..283b8de62 --- /dev/null +++ b/Src/Zle/complete.c @@ -0,0 +1,1332 @@ +/* + * complete.c - the complete module + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1999 Sven Wischnowsky + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Sven Wischnowsky or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Sven Wischnowsky and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Sven Wischnowsky and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "complete.mdh" +#include "complete.pro" +#define GLOBAL_PROTOTYPES +#include "zle_tricky.pro" +#undef GLOBAL_PROTOTYPES + +/**/ +void +freecmlist(Cmlist l) +{ + Cmlist n; + + while (l) { + n = l->next; + freecmatcher(l->matcher); + zsfree(l->str); + + zfree(l, sizeof(struct cmlist)); + + l = n; + } +} + +/**/ +void +freecmatcher(Cmatcher m) +{ + Cmatcher n; + + if (!m || --(m->refc)) + return; + + while (m) { + n = m->next; + freecpattern(m->line); + freecpattern(m->word); + freecpattern(m->left); + freecpattern(m->right); + + zfree(m, sizeof(struct cmatcher)); + + m = n; + } +} + +/**/ +void +freecpattern(Cpattern p) +{ + Cpattern n; + + while (p) { + n = p->next; + zfree(p, sizeof(struct cpattern)); + + p = n; + } +} + +/* Copy a list of completion matchers. */ + +static Cmlist +cpcmlist(Cmlist l) +{ + Cmlist r = NULL, *p = &r, n; + + while (l) { + *p = n = (Cmlist) zalloc(sizeof(struct cmlist)); + n->next = NULL; + n->matcher = cpcmatcher(l->matcher); + n->str = ztrdup(l->str); + + p = &(n->next); + l = l->next; + } + return r; +} + +/* Copy a completion matcher list. */ + +/**/ +Cmatcher +cpcmatcher(Cmatcher m) +{ + Cmatcher r = NULL, *p = &r, n; + + while (m) { + *p = n = (Cmatcher) zalloc(sizeof(struct cmatcher)); + + n->refc = 1; + n->next = NULL; + n->flags = m->flags; + n->line = cpcpattern(m->line); + n->llen = m->llen; + n->word = cpcpattern(m->word); + n->wlen = m->wlen; + n->left = cpcpattern(m->left); + n->lalen = m->lalen; + n->right = cpcpattern(m->right); + n->ralen = m->ralen; + + p = &(n->next); + m = m->next; + } + return r; +} + +/* Copy a completion matcher pattern. */ + +/**/ +static Cpattern +cpcpattern(Cpattern o) +{ + Cpattern r = NULL, *p = &r, n; + + while (o) { + *p = n = (Cpattern) zalloc(sizeof(struct cpattern)); + + n->next = NULL; + memcpy(n->tab, o->tab, 256); + n->equiv = o->equiv; + + p = &(n->next); + o = o->next; + } + return r; +} + +/* Set the global match specs. */ + +/**/ +int +set_gmatcher(char *name, char **argv) +{ + Cmlist l = NULL, *q = &l, n; + Cmatcher m; + + while (*argv) { + if ((m = parse_cmatcher(name, *argv)) == pcm_err) + return 1; + *q = n = (Cmlist) zhalloc(sizeof(struct cmlist)); + n->next = NULL; + n->matcher = m; + n->str = *argv++; + + q = &(n->next); + } + freecmlist(cmatcher); + PERMALLOC { + cmatcher = cpcmlist(l); + } LASTALLOC; + + return 1; +} + +/* Parse a string for matcher control, containing multiple matchers. */ + +/**/ +Cmatcher +parse_cmatcher(char *name, char *s) +{ + Cmatcher ret = NULL, r = NULL, n; + Cpattern line, word, left, right; + int fl, ll, wl, lal, ral, err; + + if (!*s) + return NULL; + + while (*s) { + while (*s && inblank(*s)) s++; + + if (!*s) break; + + switch (*s) { + case 'l': fl = CMF_LEFT; break; + case 'r': fl = CMF_RIGHT; break; + case 'm': fl = 0; break; + case 'L': fl = CMF_LEFT | CMF_LINE; break; + case 'R': fl = CMF_RIGHT | CMF_LINE; break; + case 'M': fl = CMF_LINE; break; + default: + zwarnnam(name, "unknown match specification character `%c'", NULL, *s); + return pcm_err; + } + if (s[1] != ':') { + zwarnnam(name, "missing `:'", NULL, 0); + return pcm_err; + } + s += 2; + if (!*s) { + zwarnnam(name, "missing patterns", NULL, 0); + return pcm_err; + } + if (fl & CMF_LEFT) { + left = parse_pattern(name, &s, &lal, '|', &err); + if (err) + return pcm_err; + if (!*s || !*++s) { + zwarnnam(name, "missing line pattern", NULL, 0); + return pcm_err; + } + } else + left = NULL; + + line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='), + &err); + if (err) + return pcm_err; + if ((fl & CMF_RIGHT) && (!*s || !*++s)) { + zwarnnam(name, "missing right anchor", NULL, 0); + } else if (!(fl & CMF_RIGHT)) { + if (!*s) { + zwarnnam(name, "missing word pattern", NULL, 0); + return pcm_err; + } + s++; + } + if (fl & CMF_RIGHT) { + right = parse_pattern(name, &s, &ral, '=', &err); + if (err) + return pcm_err; + if (!*s) { + zwarnnam(name, "missing word pattern", NULL, 0); + return pcm_err; + } + s++; + } else + right = NULL; + + if (*s == '*') { + if (!(fl & (CMF_LEFT | CMF_RIGHT))) { + zwarnnam(name, "need anchor for `*'", NULL, 0); + return pcm_err; + } + word = NULL; + wl = -1; + s++; + } else { + word = parse_pattern(name, &s, &wl, 0, &err); + + if (!word && !line) { + zwarnnam(name, "need non-empty word or line pattern", NULL, 0); + return pcm_err; + } + } + if (err) + return pcm_err; + + n = (Cmatcher) zcalloc(sizeof(*ret)); + n->next = NULL; + n->flags = fl; + n->line = line; + n->llen = ll; + n->word = word; + n->wlen = wl; + n->left = left; + n->lalen = lal; + n->right = right; + n->ralen = ral; + + if (ret) + r->next = n; + else + ret = n; + + r = n; + } + return ret; +} + +/* Parse a pattern for matcher control. */ + +/**/ +static Cpattern +parse_pattern(char *name, char **sp, int *lp, char e, int *err) +{ + Cpattern ret = NULL, r = NULL, n; + unsigned char *s = (unsigned char *) *sp; + int l = 0; + + *err = 0; + + while (*s && (e ? (*s != e) : !inblank(*s))) { + n = (Cpattern) hcalloc(sizeof(*n)); + n->next = NULL; + n->equiv = 0; + + if (*s == '[') { + s = parse_class(n, s + 1, ']'); + if (!*s) { + *err = 1; + zwarnnam(name, "unterminated character class", NULL, 0); + return NULL; + } + } else if (*s == '{') { + n->equiv = 1; + s = parse_class(n, s + 1, '}'); + if (!*s) { + *err = 1; + zwarnnam(name, "unterminated character class", NULL, 0); + return NULL; + } + } else if (*s == '?') { + memset(n->tab, 1, 256); + } else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') { + *err = 1; + zwarnnam(name, "invalid pattern character `%c'", NULL, *s); + return NULL; + } else { + if (*s == '\\' && s[1]) + s++; + + memset(n->tab, 0, 256); + n->tab[*s] = 1; + } + if (ret) + r->next = n; + else + ret = n; + + r = n; + + l++; + s++; + } + *sp = (char *) s; + *lp = l; + return ret; +} + +/* Parse a character class for matcher control. */ + +/**/ +static unsigned char * +parse_class(Cpattern p, unsigned char *s, unsigned char e) +{ + int n = 0, i = 1, j, eq = (e == '}'), k = 1; + + if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; } + + memset(p->tab, n, 256); + + n = !n; + while (*s && (k || *s != e)) { + if (s[1] == '-' && s[2] != e) { + /* a run of characters */ + for (j = (int) *s; j <= (int) s[2]; j++) + p->tab[j] = (eq ? i++ : n); + + s += 3; + } else + p->tab[*s++] = (eq ? i++ : n); + k = 0; + } + return s; +} + +/**/ +static int +bin_compadd(char *name, char **argv, char *ops, int func) +{ + struct cadata dat; + char *p, **sp, *e, *m = NULL; + int dm; + Cmatcher match = NULL; + + if (incompfunc != 1) { + zerrnam(name, "can only be called from completion function", NULL, 0); + return 1; + } + dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = + dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = + dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL; + dat.match = NULL; + dat.flags = 0; + dat.aflags = CAF_MATCH; + + for (; *argv && **argv == '-'; argv++) { + if (!(*argv)[1]) { + argv++; + break; + } + for (p = *argv + 1; *p; p++) { + sp = NULL; + e = NULL; + dm = 0; + switch (*p) { + case 'q': + dat.flags |= CMF_REMOVE; + break; + case 'Q': + dat.aflags |= CAF_QUOTE; + break; + case 'f': + dat.flags |= CMF_FILE; + break; + case 'e': + dat.flags |= CMF_ISPAR; + break; + case 'F': + sp = &(dat.ign); + e = "string expected after -%c"; + break; + case 'n': + dat.flags |= CMF_NOLIST; + break; + case 'U': + dat.aflags &= ~CAF_MATCH; + break; + case 'P': + sp = &(dat.pre); + e = "string expected after -%c"; + break; + case 'S': + sp = &(dat.suf); + e = "string expected after -%c"; + break; + case 'J': + sp = &(dat.group); + e = "group name expected after -%c"; + break; + case 'V': + if (!dat.group) + dat.aflags |= CAF_NOSORT; + sp = &(dat.group); + e = "group name expected after -%c"; + break; + case '1': + if (!(dat.aflags & CAF_UNIQCON)) + dat.aflags |= CAF_UNIQALL; + break; + case '2': + if (!(dat.aflags & CAF_UNIQALL)) + dat.aflags |= CAF_UNIQCON; + break; + case 'y': + sp = &(dat.ylist); + e = "string expected after -%c"; + break; + case 'i': + sp = &(dat.ipre); + e = "string expected after -%c"; + break; + case 'I': + sp = &(dat.isuf); + e = "string expected after -%c"; + break; + case 'p': + sp = &(dat.ppre); + e = "string expected after -%c"; + break; + case 's': + sp = &(dat.psuf); + e = "string expected after -%c"; + break; + case 'W': + sp = &(dat.prpre); + e = "string expected after -%c"; + break; + case 'a': + dat.aflags |= CAF_ALT; + break; + case 'M': + sp = &m; + e = "matching specification expected after -%c"; + dm = 1; + break; + case 'X': + sp = &(dat.exp); + e = "string expected after -%c"; + break; + case 'r': + dat.flags |= CMF_REMOVE; + sp = &(dat.rems); + e = "string expected after -%c"; + break; + case 'R': + dat.flags |= CMF_REMOVE; + sp = &(dat.remf); + e = "function name expected after -%c"; + break; + case 'A': + sp = &(dat.apar); + e = "parameter name expected after -%c"; + break; + case 'O': + sp = &(dat.opar); + e = "parameter name expected after -%c"; + break; + case 'D': + sp = &(dat.dpar); + e = "parameter name expected after -%c"; + break; + case 'd': + sp = &(dat.disp); + e = "parameter name expected after -%c"; + break; + case 'l': + dat.flags |= CMF_DISPLINE; + break; + case '-': + argv++; + goto ca_args; + default: + zerrnam(name, "bad option: -%c", NULL, *p); + return 1; + } + if (sp) { + if (p[1]) { + if (!*sp) + *sp = p + 1; + p = "" - 1; + } else if (argv[1]) { + argv++; + if (!*sp) + *sp = *argv; + p = "" - 1; + } else { + zerrnam(name, e, NULL, *p); + return 1; + } + if (dm && (match = parse_cmatcher(name, m)) == pcm_err) { + match = NULL; + return 1; + } + } + } + } + ca_args: + if (!*argv) + return 1; + + dat.match = match = cpcmatcher(match); + dm = addmatches(&dat, argv); + freecmatcher(match); + + return dm; +} + +#define CVT_RANGENUM 0 +#define CVT_RANGEPAT 1 +#define CVT_PRENUM 2 +#define CVT_PREPAT 3 +#define CVT_SUFNUM 4 +#define CVT_SUFPAT 5 + +/**/ +void +ignore_prefix(int l) +{ + if (l) { + char *tmp, sav; + int pl = strlen(compprefix); + + if (l > pl) + l = pl; + + sav = compprefix[l]; + + compprefix[l] = '\0'; + tmp = tricat(compiprefix, compprefix, ""); + zsfree(compiprefix); + compiprefix = tmp; + compprefix[l] = sav; + tmp = ztrdup(compprefix + l); + zsfree(compprefix); + compprefix = tmp; + } +} + +/**/ +void +ignore_suffix(int l) +{ + if (l) { + char *tmp, sav; + int sl = strlen(compsuffix); + + if ((l = sl - l) < 0) + l = 0; + + tmp = tricat(compsuffix + l, compisuffix, ""); + zsfree(compisuffix); + compisuffix = tmp; + sav = compsuffix[l]; + compsuffix[l] = '\0'; + tmp = ztrdup(compsuffix); + compsuffix[l] = sav; + zsfree(compsuffix); + compsuffix = tmp; + } +} + +/**/ +void +restrict_range(int b, int e) +{ + int wl = arrlen(compwords) - 1; + + if (wl && b >= 0 && e >= 0 && (b > 0 || e < wl)) { + int i; + char **p, **q, **pp; + + if (e > wl) + e = wl; + + i = e - b + 1; + p = (char **) zcalloc((i + 1) * sizeof(char *)); + + for (q = p, pp = compwords + b; i; i--, q++, pp++) + *q = ztrdup(*pp); + freearray(compwords); + compwords = p; + compcurrent -= b; + } +} + +static int +do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) +{ + switch (test) { + case CVT_RANGENUM: + { + int l = arrlen(compwords); + + if (na < 0) + na += l; + else + na--; + if (nb < 0) + nb += l; + else + nb--; + + if (compcurrent - 1 < na || compcurrent - 1 > nb) + return 0; + if (mod) + restrict_range(na, nb); + return 1; + } + case CVT_RANGEPAT: + { + char **p; + int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1; + Patprog pp; + + i = compcurrent - 1; + if (i < 0 || i >= l) + return 0; + + singsub(&sa); + pp = patcompile(sa, PAT_STATIC, NULL); + + for (i--, p = compwords + i; i >= 0; p--, i--) { + if (pattry(pp, *p)) { + b = i + 1; + t = 1; + break; + } + } + if (t && sb) { + int tt = 0; + + singsub(&sb); + pp = patcompile(sb, PAT_STATIC, NULL); + + for (i++, p = compwords + i; i < l; p++, i++) { + if (pattry(pp, *p)) { + e = i - 1; + tt = 1; + break; + } + } + if (tt && i < compcurrent) + t = 0; + } + if (e < b) + t = 0; + if (t && mod) + restrict_range(b, e); + return t; + } + case CVT_PRENUM: + case CVT_SUFNUM: + if (!na) + return 1; + if (na > 0 && + strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) { + if (mod) { + if (test == CVT_PRENUM) + ignore_prefix(na); + else + ignore_suffix(na); + return 1; + } + return 0; + } + case CVT_PREPAT: + case CVT_SUFPAT: + { + Patprog pp; + + if (!na) + return 0; + + if (!(pp = patcompile(sa, PAT_STATIC, 0))) + return 0; + + if (test == CVT_PREPAT) { + int l, add; + char *p, sav; + + if (!(l = strlen(compprefix))) + return 0; + if (na < 0) { + p = compprefix + l; + na = -na; + add = -1; + } else { + p = compprefix + 1; + add = 1; + } + for (; l; l--, p += add) { + sav = *p; + *p = '\0'; + test = pattry(pp, compprefix); + *p = sav; + if (test && !--na) + break; + } + if (!l) + return 0; + if (mod) + ignore_prefix(p - compprefix); + } else { + int l, ol, add; + char *p; + + if (!(ol = l = strlen(compsuffix))) + return 0; + if (na < 0) { + p = compsuffix; + na = -na; + add = 1; + } else { + p = compsuffix + l - 1; + add = -1; + } + for (; l; l--, p += add) + if (pattry(pp, p) && !--na) + break; + + if (!l) + return 0; + if (mod) + ignore_suffix(ol - (p - compsuffix)); + } + return 1; + } + } + return 0; +} + +/**/ +static int +bin_compset(char *name, char **argv, char *ops, int func) +{ + int test = 0, na = 0, nb = 0; + char *sa = NULL, *sb = NULL; + + if (incompfunc != 1) { + zerrnam(name, "can only be called from completion function", NULL, 0); + return 1; + } + if (argv[0][0] != '-') { + zerrnam(name, "missing option", NULL, 0); + return 1; + } + switch (argv[0][1]) { + case 'n': test = CVT_RANGENUM; break; + case 'N': test = CVT_RANGEPAT; break; + case 'p': test = CVT_PRENUM; break; + case 'P': test = CVT_PREPAT; break; + case 's': test = CVT_SUFNUM; break; + case 'S': test = CVT_SUFPAT; break; + case 'q': return set_comp_sep(); + default: + zerrnam(name, "bad option -%c", NULL, argv[0][1]); + return 1; + } + if (argv[0][2]) { + sa = argv[0] + 2; + sb = argv[1]; + na = 2; + } else { + if (!(sa = argv[1])) { + zerrnam(name, "missing string for option -%c", NULL, argv[0][1]); + return 1; + } + sb = argv[2]; + na = 3; + } + if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb : + (sb && argv[na]))) { + zerrnam(name, "too many arguments", NULL, 0); + return 1; + } + switch (test) { + case CVT_RANGENUM: + na = atoi(sa); + nb = (sb ? atoi(sb) : -1); + break; + case CVT_RANGEPAT: + tokenize(sa); + sa = rembslash(sa); + remnulargs(sa); + if (sb) { + tokenize(sb); + sb = rembslash(sb); + remnulargs(sb); + } + break; + case CVT_PRENUM: + case CVT_SUFNUM: + na = atoi(sa); + break; + case CVT_PREPAT: + case CVT_SUFPAT: + if (sb) { + na = atoi(sa); + sa = sb; + } else + na = -1; + tokenize(sa); + sa = rembslash(sa); + remnulargs(sa); + break; + } + return !do_comp_vars(test, na, sa, nb, sb, 1); +} + +/* Definitions for the special parameters. Note that these have to match the + * order of the CP_* bits in comp.h */ + +#define VAL(X) ((void *) (&(X))) +struct compparam { + char *name; + int type; + void *var, *set, *get; +}; + +static struct compparam comprparams[] = { + { "words", PM_ARRAY, VAL(compwords), NULL, NULL }, + { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL, NULL }, + { "PREFIX", PM_SCALAR, VAL(compprefix), NULL, NULL }, + { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL, NULL }, + { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL, NULL }, + { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL, NULL }, + { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL }, + { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL }, + { NULL, 0, NULL, NULL, NULL } +}; + +static struct compparam compkparams[] = { + { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) }, + { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL }, + { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL }, + { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL }, + { "context", PM_SCALAR, VAL(compcontext), NULL, NULL }, + { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL }, + { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL }, + { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL, NULL }, + { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL }, + { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL }, + { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) }, + { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL }, + { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL }, + { "exact", PM_SCALAR, VAL(compexact), NULL, NULL }, + { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL }, + { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL }, + { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL }, + { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) }, + { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL, + VAL(get_unambig_curs) }, + { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL }, + { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL }, + { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL }, + { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL }, + { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL }, + { "vared", PM_SCALAR, VAL(compvared), NULL, NULL }, + { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) }, + { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) }, + { NULL, 0, NULL, NULL, NULL } +}; + +#define COMPSTATENAME "compstate" + +static void +addcompparams(struct compparam *cp, Param *pp) +{ + for (; cp->name; cp++, pp++) { + Param pm = createparam(cp->name, + cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL); + if (!pm) + pm = (Param) paramtab->getnode(paramtab, cp->name); + DPUTS(!pm, "param not set in addcompparams"); + + *pp = pm; + pm->level = locallevel + 1; + if ((pm->u.data = cp->var)) { + switch(PM_TYPE(cp->type)) { + case PM_SCALAR: + pm->sets.cfn = strvarsetfn; + pm->gets.cfn = strvargetfn; + break; + case PM_INTEGER: + pm->sets.ifn = intvarsetfn; + pm->gets.ifn = intvargetfn; + pm->ct = 10; + break; + case PM_ARRAY: + pm->sets.afn = arrvarsetfn; + pm->gets.afn = arrvargetfn; + break; + } + } else { + pm->sets.cfn = (void (*) _((Param, char *))) cp->set; + pm->gets.cfn = (char *(*) _((Param))) cp->get; + } + pm->unsetfn = compunsetfn; + } +} + +/**/ +void +makecompparams(void) +{ + Param cpm; + HashTable tht; + + addcompparams(comprparams, comprpms); + + if (!(cpm = createparam(COMPSTATENAME, + PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED))) + cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME); + DPUTS(!cpm, "param not set in makecompparams"); + + comprpms[CPN_COMPSTATE] = cpm; + tht = paramtab; + cpm->level = locallevel; + cpm->gets.hfn = get_compstate; + cpm->sets.hfn = set_compstate; + cpm->unsetfn = compunsetfn; + cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME); + addcompparams(compkparams, compkpms); + paramtab = tht; +} + +/**/ +static HashTable +get_compstate(Param pm) +{ + return pm->u.hash; +} + +/**/ +static void +set_compstate(Param pm, HashTable ht) +{ + struct compparam *cp; + Param *pp; + HashNode hn; + int i; + struct value v; + char *str; + + for (i = 0; i < ht->hsize; i++) + for (hn = ht->nodes[i]; hn; hn = hn->next) + for (cp = compkparams, + pp = compkpms; cp->name; cp++, pp++) + if (!strcmp(hn->nam, cp->name)) { + v.isarr = v.inv = v.a = 0; + v.b = -1; + v.arr = NULL; + v.pm = (Param) hn; + if (cp->type == PM_INTEGER) + *((zlong *) cp->var) = getintvalue(&v); + else if ((str = getstrvalue(&v))) { + zsfree(*((char **) cp->var)); + *((char **) cp->var) = ztrdup(str); + } + (*pp)->flags &= ~PM_UNSET; + + break; + } + deleteparamtable(ht); +} + +/**/ +static zlong +get_nmatches(Param pm) +{ + return num_matches(1); +} + +/**/ +static zlong +get_anmatches(Param pm) +{ + return num_matches(0); +} + +/**/ +static zlong +get_listlines(Param pm) +{ + return list_lines(); +} + +/**/ +static void +set_complist(Param pm, char *v) +{ + comp_list(v); +} + +/**/ +static char * +get_complist(Param pm) +{ + return complist; +} + +/**/ +static char * +get_unambig(Param pm) +{ + return unambig_data(NULL); +} + +/**/ +static zlong +get_unambig_curs(Param pm) +{ + int c; + + unambig_data(&c); + + return c; +} + +/**/ +static void +compunsetfn(Param pm, int exp) +{ + if (exp) { + if (PM_TYPE(pm->flags) == PM_SCALAR) { + zsfree(*((char **) pm->u.data)); + *((char **) pm->u.data) = ztrdup(""); + } else if (PM_TYPE(pm->flags) == PM_ARRAY) { + freearray(*((char ***) pm->u.data)); + *((char ***) pm->u.data) = zcalloc(sizeof(char *)); + } + pm->flags |= PM_UNSET; + } +} + +/**/ +void +comp_setunset(int rset, int runset, int kset, int kunset) +{ + Param *p; + + if (comprpms && (rset >= 0 || runset >= 0)) { + for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) { + if (rset & 1) + (*p)->flags &= ~PM_UNSET; + if (runset & 1) + (*p)->flags |= PM_UNSET; + } + } + if (comprpms && (kset >= 0 || kunset >= 0)) { + for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) { + if (kset & 1) + (*p)->flags &= ~PM_UNSET; + if (kunset & 1) + (*p)->flags |= PM_UNSET; + } + } +} + +/**/ +static int +comp_wrapper(List list, FuncWrap w, char *name) +{ + if (incompfunc != 1) + return 1; + else { + char *orest, *opre, *osuf, *oipre, *oisuf, **owords; + char *oqipre, *oqisuf, *oq, *oqi; + zlong ocur; + unsigned int runset = 0, kunset = 0, m, sm; + Param *pp; + + m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | + CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX; + for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) { + if ((m & 1) && ((*pp)->flags & PM_UNSET)) + runset |= sm; + } + if (compkpms[CPN_RESTORE]->flags & PM_UNSET) + kunset = CP_RESTORE; + orest = comprestore; + comprestore = ztrdup("auto"); + ocur = compcurrent; + opre = dupstring(compprefix); + osuf = dupstring(compsuffix); + oipre = dupstring(compiprefix); + oisuf = dupstring(compisuffix); + oqipre = dupstring(compqiprefix); + oqisuf = dupstring(compqisuffix); + oq = dupstring(compquote); + oqi = dupstring(compquoting); + + HEAPALLOC { + owords = arrdup(compwords); + } LASTALLOC; + + runshfunc(list, w, name); + + if (comprestore && !strcmp(comprestore, "auto")) { + compcurrent = ocur; + zsfree(compprefix); + compprefix = ztrdup(opre); + zsfree(compsuffix); + compsuffix = ztrdup(osuf); + zsfree(compiprefix); + compiprefix = ztrdup(oipre); + zsfree(compisuffix); + compisuffix = ztrdup(oisuf); + zsfree(compqiprefix); + compqiprefix = ztrdup(oqipre); + zsfree(compqisuffix); + compqisuffix = ztrdup(oqisuf); + zsfree(compquote); + compquote = ztrdup(oq); + zsfree(compquoting); + compquoting = ztrdup(oqi); + freearray(compwords); + PERMALLOC { + compwords = arrdup(owords); + } LASTALLOC; + comp_setunset(CP_COMPSTATE | + (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX | + CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX | + CP_QIPREFIX | CP_QISUFFIX)), + (runset & CP_ALLREALS), + (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS)); + } else + comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE), + (kunset & CP_RESTORE)); + zsfree(comprestore); + comprestore = orest; + + return 0; + } +} + +/**/ +static int +comp_check(void) +{ + if (incompfunc != 1) { + zerr("condition can only be used in completion function", NULL, 0); + return 0; + } + return 1; +} + +/**/ +static int +cond_psfix(char **a, int id) +{ + if (comp_check()) { + if (a[1]) + return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1), + 0, NULL, 0); + else + return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0); + } + return 0; +} + +/**/ +static int +cond_range(char **a, int id) +{ + return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0, + (id ? cond_str(a, 1, 1) : NULL), 0); +} + +/**/ +static void +cmsetfn(Param pm, char **v) +{ + set_gmatcher(pm->nam, v); +} + +/**/ +static char ** +cmgetfn(Param pm) +{ + int num; + Cmlist p; + char **ret, **q; + + for (num = 0, p = cmatcher; p; p = p->next, num++); + + ret = (char **) zhalloc((num + 1) * sizeof(char *)); + + for (q = ret, p = cmatcher; p; p = p->next, q++) + *q = dupstring(p->str); + *q = NULL; + + return ret; +} + +/**/ +static void +cmunsetfn(Param pm, int exp) +{ + char *dummy[1]; + + dummy[0] = NULL; + set_gmatcher(pm->nam, dummy); +} + +static struct builtin bintab[] = { + BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL), + BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL), +}; + +static struct conddef cotab[] = { + CONDDEF("prefix", 0, cond_psfix, 1, 2, CVT_PREPAT), + CONDDEF("suffix", 0, cond_psfix, 1, 2, CVT_SUFPAT), + CONDDEF("between", 0, cond_range, 2, 2, 1), + CONDDEF("after", 0, cond_range, 1, 1, 0), +}; + +static struct funcwrap wrapper[] = { + WRAPDEF(comp_wrapper), +}; + +static struct paramdef patab[] = { + PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn) +}; + +/**/ +int +setup_complete(Module m) +{ + makecompparamsptr = makecompparams; + comp_setunsetptr = comp_setunset; + + return 0; +} + +/**/ +int +boot_complete(Module m) +{ + if (!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) | + addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) | + addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) | + !addwrapper(m, wrapper))) + return 1; + return 0; +} + +#ifdef MODULE + +/**/ +int +cleanup_complete(Module m) +{ + deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)); + deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)); + deletewrapper(m, wrapper); + return 0; +} + +/**/ +int +finish_complete(Module m) +{ + makecompparamsptr = NULL; + comp_setunsetptr = NULL; + + return 0; +} + +#endif diff --git a/Src/Zle/complete.mdd b/Src/Zle/complete.mdd new file mode 100644 index 000000000..628058e2a --- /dev/null +++ b/Src/Zle/complete.mdd @@ -0,0 +1,11 @@ +hasexport=1 + +moddeps="zle" + +autobins="compgen compadd compset" + +autoprefixconds="prefix suffix between after" + +autoparams="compmatchers" + +objects="complete.o" diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 2ffde6b48..dc1037122 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -415,7 +415,7 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, } putc(file_type(buf->st_mode), shout); len++; - } + } if ((len = width - len - 2) > 0) { if (m->gnum != mselect) { zcoff(); diff --git a/Src/Zle/complist.mdd b/Src/Zle/complist.mdd index 8ea60b0a8..16cd5f1af 100644 --- a/Src/Zle/complist.mdd +++ b/Src/Zle/complist.mdd @@ -1,3 +1,3 @@ -moddeps="comp1 zle" +moddeps="complete" objects="complist.o" diff --git a/Src/Zle/computil.mdd b/Src/Zle/computil.mdd index e4193610c..56f633414 100644 --- a/Src/Zle/computil.mdd +++ b/Src/Zle/computil.mdd @@ -1,4 +1,4 @@ -moddeps="compctl zle" +moddeps="complete" objects="computil.o" diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index ff515e7c4..470aa890f 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -146,6 +146,17 @@ typedef struct cutbuffer *Cutbuffer; /* Convenience macros for the hooks */ -#define LISTMATCHESHOOK (zlehooks + 0) -#define INSERTMATCHHOOK (zlehooks + 1) -#define MENUSTARTHOOK (zlehooks + 2) +#define LISTMATCHESHOOK (zlehooks + 0) +#define INSERTMATCHHOOK (zlehooks + 1) +#define MENUSTARTHOOK (zlehooks + 2) +#define COMPCTLMAKEHOOK (zlehooks + 3) +#define COMPCTLBEFOREHOOK (zlehooks + 4) +#define COMPCTLAFTERHOOK (zlehooks + 5) + +/* compctl hook data structs */ + +struct ccmakedat { + char *str; + int incmd; + int lst; +}; diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd index fec2e213b..95702e948 100644 --- a/Src/Zle/zle.mdd +++ b/Src/Zle/zle.mdd @@ -1,5 +1,75 @@ hasexport=1 +autobins="bindkey vared zle" + +objects="zle_bindings.o zle_hist.o zle_keymap.o zle_main.o \ +zle_misc.o zle_move.o zle_params.o zle_refresh.o \ +zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o" + +headers="zle.h zle_things.h comp.h" + +:<<\Make +zle_things.h: thingies.list zle_things.sed + ( \ + echo '/** zle_things.h **/'; \ + echo '/** indices of and pointers to known thingies **/'; \ + echo; \ + echo 'enum {'; \ + sed -n -f $(sdir)/zle_things.sed < thingies.list; \ + echo ' ZLE_BUILTIN_THINGY_COUNT'; \ + echo '};'; \ + ) > $@ + +zle_widget.h: widgets.list zle_widget.sed + ( \ + echo '/** zle_widget.h **/'; \ + echo '/** indices of and pointers to internal widgets **/'; \ + echo; \ + echo 'enum {'; \ + sed -n -f $(sdir)/zle_widget.sed < widgets.list; \ + echo ' ZLE_BUILTIN_WIDGET_COUNT'; \ + echo '};'; \ + ) > $@ + +thingies.list: iwidgets.list + ( \ + echo '/** thingies.list **/'; \ + echo '/** thingy structures for the known thingies **/'; \ + echo; \ + echo '/* format: T("name", TH_FLAGS, w_widget, t_nextthingy) */'; \ + echo; \ + sed -e 's/#.*//; /^$$/d; s/" *,.*/"/' \ + -e 's/^"/T("/; s/$$/, 0,/; h' \ + -e 's/-//g; s/^.*"\(.*\)".*/w_\1, t_D\1)/' \ + -e 'H; g; s/\n/ /' \ + < $(sdir)/iwidgets.list; \ + sed -e 's/#.*//; /^$$/d; s/" *,.*/"/' \ + -e 's/^"/T("./; s/$$/, TH_IMMORTAL,/; h' \ + -e 's/-//g; s/^.*"\.\(.*\)".*/w_\1, t_\1)/' \ + -e 'H; g; s/\n/ /' \ + < $(sdir)/iwidgets.list; \ + ) > $@ + +widgets.list: iwidgets.list + ( \ + echo '/** widgets.list **/'; \ + echo '/** widget structures for the internal widgets **/'; \ + echo; \ + echo '/* format: W(ZLE_FLAGS, t_firstname, functionname) */'; \ + echo; \ + sed -e 's/#.*//; /^$$/d; s/-//g' \ + -e 's/^"\(.*\)" *, *\([^ ]*\) *, *\(.*\)/W(\3, t_\1, \2)/' \ + < $(sdir)/iwidgets.list; \ + ) > $@ + +zle_bindings.o zle_bindings..o: zle_widget.h widgets.list thingies.list + +clean-here: clean.zle +clean.zle: + rm -f zle_things.h zle_widget.h widgets.list thingies.list +Make +hasexport=1 + moddeps="comp1" autobins="bindkey vared zle" diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 28bc96b64..37c32d581 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -104,7 +104,8 @@ char *curkeymapname; /* the hash table of keymap names */ -static HashTable keymapnamtab; +/**/ +HashTable keymapnamtab; /* key sequence reading data */ diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index d7ffbe259..56ecd02ba 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -30,6 +30,70 @@ #include "zle.mdh" #include "zle_main.pro" +/* Defined by the complete module, called in zle_tricky.c. */ + +/**/ +void (*makecompparamsptr) _((void)); + +/**/ +void (*comp_setunsetptr) _((int, int, int, int)); + +/* != 0 if in a shell function called from completion, such that read -[cl] * + * will work (i.e., the line is metafied, and the above word arrays are OK). */ + +/**/ +int incompctlfunc; + +/* != 0 if we are in a new style completion function */ + +/**/ +int incompfunc; + +/* Global matcher. */ + +/**/ +Cmlist cmatcher; + +/* global variables for shell parameters in new style completion */ + +/**/ +zlong compcurrent, + compmatcher, + compmatchertot, + complistmax, + complistlines; + +/**/ +char **compwords, + *compprefix, + *compsuffix, + *compiprefix, + *compisuffix, + *compqiprefix, + *compqisuffix, + *compmatcherstr, + *compcontext, + *compparameter, + *compredirect, + *compquote, + *compquoting, + *comprestore, + *complist, + *compforcelist, + *compinsert, + *compexact, + *compexactstr, + *comppatmatch, + *comppatinsert, + *complastprompt, + *comptoend, + *compoldlist, + *compoldins, + *compvared; + +/**/ +Param *comprpms, *compkpms; + /* != 0 if we're done editing */ /**/ @@ -961,6 +1025,9 @@ struct hookdef zlehooks[] = { HOOKDEF("list_matches", ilistmatches, 0), HOOKDEF("insert_match", NULL, HOOKF_ALL), HOOKDEF("menu_start", NULL, HOOKF_ALL), + HOOKDEF("compctl_make", NULL, 0), + HOOKDEF("compctl_before", NULL, 0), + HOOKDEF("compctl_after", NULL, 0), }; /**/ @@ -974,17 +1041,6 @@ setup_zle(Module m) spaceinlineptr = spaceinline; zlereadptr = zleread; - addmatchesptr = addmatches; - comp_strptr = comp_str; - getcpatptr = getcpat; - makecomplistcallptr = makecomplistcall; - makecomplistctlptr = makecomplistctl; - num_matchesptr = num_matches; - list_linesptr = list_lines; - comp_listptr = comp_list; - unambig_dataptr = unambig_data; - set_comp_sepptr = set_comp_sep; - getkeyptr = getkey; /* initialise the thingies */ @@ -1001,6 +1057,23 @@ setup_zle(Module m) varedarg = NULL; + incompfunc = incompctlfunc = 0; + + comprpms = compkpms = NULL; + compwords = NULL; + compprefix = compsuffix = compiprefix = compisuffix = + compqiprefix = compqisuffix = compmatcherstr = + compcontext = compparameter = compredirect = compquote = + compquoting = comprestore = complist = compinsert = + compexact = compexactstr = comppatmatch = comppatinsert = + compforcelist = complastprompt = comptoend = + compoldlist = compoldins = compvared = NULL; + + clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *)); + + makecompparamsptr = NULL; + comp_setunsetptr = NULL; + return 0; } @@ -1057,19 +1130,37 @@ finish_zle(Module m) spaceinlineptr = noop_function_int; zlereadptr = fallback_zleread; - addmatchesptr = NULL; - comp_strptr = NULL; - getcpatptr = NULL; - makecomplistcallptr = NULL; - makecomplistctlptr = NULL; - num_matchesptr = NULL; - list_linesptr = NULL; - comp_listptr = NULL; - unambig_dataptr = NULL; - set_comp_sepptr = NULL; - getkeyptr = NULL; + freearray(compwords); + zsfree(compprefix); + zsfree(compsuffix); + zsfree(compiprefix); + zsfree(compisuffix); + zsfree(compqiprefix); + zsfree(compqisuffix); + zsfree(compmatcherstr); + zsfree(compcontext); + zsfree(compparameter); + zsfree(compredirect); + zsfree(compquote); + zsfree(compquoting); + zsfree(comprestore); + zsfree(complist); + zsfree(compforcelist); + zsfree(compinsert); + zsfree(compexact); + zsfree(compexactstr); + zsfree(comppatmatch); + zsfree(comppatinsert); + zsfree(complastprompt); + zsfree(comptoend); + zsfree(compoldlist); + zsfree(compoldins); + zsfree(compvared); + + zfree(clwords, clwsize * sizeof(char *)); + return 0; } diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 4f1bb03f2..72a0f120d 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -551,13 +551,13 @@ bin_zle_complete(char *name, char **args, char *ops, char func) Widget w, cw; #ifdef DYNAMIC - if (!require_module(name, "compctl", 0, 0)) { - zerrnam(name, "can't load compctl module", NULL, 0); + if (!require_module(name, "complete", 0, 0)) { + zerrnam(name, "can't load complete module", NULL, 0); return 1; } #else - if (!makecompparamsptr) { - zerrnam(name, "compctl module not available", NULL, 0); + if (!module_linked("complete")) { + zerrnam(name, "complete module not available", NULL, 0); return 1; } #endif diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 5c21f946b..5803ac392 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -42,24 +42,6 @@ * file only are not translated: they remain indexes into the metafied * * line. */ -#ifdef HAVE_NIS_PLUS -# include -#else -# ifdef HAVE_NIS -# include -# include -# include -# include - -/* This is used when getting usernames from the NIS. */ -typedef struct { - int len; - char *s; -} -dopestring; -# endif -#endif - #define inststr(X) inststrlen((X),1,-1) @@ -68,14 +50,31 @@ dopestring; static char *origline; static int origcs, origll; +/* Words on the command line, for use in completion */ + +/**/ +int clwsize, clwnum, clwpos; +/**/ +char **clwords; + /* wb and we hold the beginning/end position of the word we are completing. */ -static int wb, we; +/**/ +int wb, we; /* offs is the cursor position within the tokenized * * current word after removing nulargs. */ -static int offs; +/**/ +int offs; + +/* We store the following prefixes/suffixes: * + * ipre,ripre -- the ignored prefix (quoted and unquoted) * + * isuf -- the ignored suffix * + * autoq -- quotes to automatically insert */ + +/**/ +char *ipre, *ripre, *isuf, *qipre, *qisuf, autoq; /* the last completion widget called */ @@ -86,7 +85,8 @@ static Widget lastcompwidget; * usemenu is set to 2 if we have to start automenu and 3 if we have to * * insert a match as if for menucompletion but without really stating it. */ -static int usemenu, useglob, useexact, useline, uselist; +/**/ +int usemenu, useglob, useexact, useline, uselist; /* Non-zero if we should keep an old list. */ @@ -131,7 +131,8 @@ static int hasunqu, useqbr, brpcs, brscs; /* The list of matches. fmatches contains the matches we first ignore * * because of fignore. */ -static LinkList matches, fmatches; +/**/ +LinkList matches, fmatches; /* This holds the list of matches-groups. lastmatches holds the last list of * permanently allocated matches, pmatches is the same for the list @@ -178,45 +179,13 @@ struct cldata listdat; /* This flag is non-zero if we are completing a pattern (with globcomplete) */ -static int ispattern, haspattern; +/**/ +int ispattern, haspattern; /* Non-zero if at least one match was added without -U. */ -static int hasmatched; - -/* Two patterns used when doing glob-completion. The first one is built * - * from the whole word we are completing and the second one from that * - * part of the word that was identified as a possible filename. */ - -static Patprog patcomp, filecomp; - -/* We store the following prefixes/suffixes: * - * lpre/lsuf -- what's on the line * - * rpre/rsuf -- same as lpre/lsuf, but expanded * - * * - * ... and if we are completing files, too: * - * ppre/psuf -- the path prefix/suffix * - * lppre/lpsuf -- the path prefix/suffix, unexpanded * - * fpre/fsuf -- prefix/suffix of the pathname component the cursor is in * - * prpre -- ppre in expanded form usable for opendir * - * ipre,ripre -- the ignored prefix (quoted and unquoted) * - * isuf -- the ignored suffix * - * qipre, qisuf-- ingnored quoted string * - * autoq -- quotes to automatically insert * - * * - * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf, * - * fpre, fsuf, lppre, and lpsuf. noreal is non-zero if we have rpre/rsuf. */ - -static char *lpre, *lsuf; -static char *rpre, *rsuf; -static char *ppre, *psuf, *lppre, *lpsuf, *prpre; -static char *fpre, *fsuf; -static char *ipre, *ripre; -static char *isuf; -static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf; -static char *qipre, *qisuf, autoq; -static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl; -static int noreal; +/**/ +int hasmatched; /* A parameter expansion prefix (like ${). */ @@ -226,15 +195,6 @@ static char *parpre; static int parflags; -/* This is either zero or equal to the special character the word we are * - * trying to complete starts with (e.g. Tilde or Equals). */ - -static char ic; - -/* This variable says what we are currently adding to the list of matches. */ - -static int addwhat; - /* This holds the word we are completing in quoted from. */ static char *qword; @@ -245,7 +205,8 @@ static Cmgroup mgroup; /* Match counters: all matches, normal matches (not alternate set). */ -static int mnum; +/**/ +int mnum; /* The match counter when unambig_data() was called. */ @@ -253,7 +214,8 @@ static int unambig_mnum; /* Match flags for all matches in this group. */ -static int mflags; +/**/ +int mflags; /* Length of longest/shortest match. */ @@ -263,87 +225,30 @@ static int maxmlen, minmlen; * a pointer to the current cexpl structure. */ static LinkList expls; -static Cexpl expl; - -/* A pointer to the compctl we are using. */ -static Compctl curcc; - -/* A list of all compctls we have already used. */ - -static LinkList ccused; - -/* A list of all compctls used so far. */ - -static LinkList allccs; - -/* A stack of currently used compctls. */ - -static LinkList ccstack; +/**/ +Cexpl curexpl; /* A stack of completion matchers to be used. */ -static Cmlist mstack; +/**/ +Cmlist mstack; /* The completion matchers used when building new stuff for the line. */ -static Cmlist bmatchers; +/**/ +Cmlist bmatchers; /* A list with references to all matchers we used. */ -static LinkList matchers; - -/* Information about what to put on the line as the unambiguous string. - * The code always keeps lists of these structs up to date while - * matches are added (in the aminfo structs below). - * The lists have two levels: in the first one we have one struct per - * word-part, where parts are separated by the anchors of `*' patterns. - * These structs have pointers (in the prefix and suffix fields) to - * lists of cline structs describing the strings before or after the - * the anchor. */ - -typedef struct cline *Cline; -typedef struct clsub Clsub; - -struct cline { - Cline next; - int flags; - char *line; - int llen; - char *word; - int wlen; - char *orig; - int olen; - int slen; - Cline prefix, suffix; - int min, max; -}; - -#define CLF_MISS 1 -#define CLF_DIFF 2 -#define CLF_SUF 4 -#define CLF_MID 8 -#define CLF_NEW 16 -#define CLF_LINE 32 -#define CLF_JOIN 64 -#define CLF_MATCHED 128 +/**/ +LinkList matchers; /* A heap of free Cline structures. */ static Cline freecl; -/* Information for ambiguous completions. One for fignore ignored and * - * one for normal completion. */ - -typedef struct aminfo *Aminfo; - -struct aminfo { - Cmatch firstm; /* the first match */ - int exact; /* if there was an exact match */ - Cmatch exactm; /* the exact match (if any) */ - int count; /* number of matches */ - Cline line; /* unambiguous line string */ -}; +/* Ambiguous information. */ static Aminfo ainfo, fainfo; @@ -354,10 +259,19 @@ static char *compfunc = NULL; /* The memory heap to use for new style completion generation. */ -static Heap compheap; +/**/ +Heap compheap; /* Find out if we have to insert a tab (instead of trying to complete). */ +/* A list of some data. + * + * Well, actually, it's the list of all compctls used so far, but since + * conceptually we don't know anything about compctls here... */ + +/**/ +LinkList allccs; + /**/ static int usetab(void) @@ -370,14 +284,6 @@ usetab(void) return 1; } -enum { COMP_COMPLETE, - COMP_LIST_COMPLETE, - COMP_SPELL, - COMP_EXPAND, - COMP_EXPAND_COMPLETE, - COMP_LIST_EXPAND }; -#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND) - /* Non-zero if the last completion done was ambiguous (used to find * * out if AUTOMENU should start). More precisely, it's nonzero after * * successfully doing any completion, unless the completion was * @@ -648,7 +554,8 @@ acceptandmenucomplete(char **args) /* These are flags saying if we are completing in the command * * position, in a redirection, or in a parameter expansion. */ -static int lincmd, linredir, ispar, parq, eparq, linwhat, linarr; +/**/ +int lincmd, linredir, ispar, parq, eparq, linwhat, linarr; /* The string for the redirection operator. */ @@ -657,7 +564,8 @@ static char *rdstr; /* This holds the name of the current command (used to find the right * * compctl). */ -static char *cmdstr; +/**/ +char *cmdstr; /* This hold the name of the variable we are working on. */ @@ -665,11 +573,13 @@ static char *varname; /* != 0 if we are in a subscript */ -static int insubscr; +/**/ +int insubscr; /* Parameter pointer for completing keys of an assoc array. */ -static Param keypm; +/**/ +Param keypm; /* 1 if we are completing in a quoted string (or inside `...`) */ @@ -681,6 +591,25 @@ int instring, inbackt; #define quotename(s, e) bslashquote(s, e, instring) +/* Copy the given string and remove backslashes from the copy and return it. */ + +/**/ +char * +rembslash(char *s) +{ + char *t = s = dupstring(s); + + while (*s) + if (*s == '\\') { + chuck(s); + if (*s) + s++; + } else + s++; + + return t; +} + /* Check if the given string is the name of a parameter and if this * * parameter is one worth expanding. */ @@ -761,7 +690,8 @@ cmphaswilds(char *str) /* Check if we have to complete a parameter name. */ -static char * +/**/ +char * check_param(char *s, int set, int test) { char *p; @@ -1245,7 +1175,7 @@ addx(char **ptmp) /* Like dupstring, but add an extra space at the end of the string. */ /**/ -static char * +char * dupstrspace(const char *str) { int len = strlen((char *)str); @@ -1338,7 +1268,7 @@ dupbrinfo(Brinfo p, Brinfo *last) static char * get_comp_string(void) { - int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct; + int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct, varq = 0; char *s = NULL, *linptr, *tmp, *p, *tt = NULL; freebrinfo(brbeg); @@ -1417,9 +1347,15 @@ get_comp_string(void) for (j = 0, p = tokstr; *p; p++) if (*p == Snull || *p == Dnull) j++; - if (j & 1) - tok = STRING; - } + if (j & 1) { + if (lincmd && strchr(tokstr, '=')) { + varq = 1; + tok = ENVSTRING; + } else + tok = STRING; + } + } else if (tok == ENVSTRING) + varq = 0; if (tok == ENVARRAY) { linarr = 1; zsfree(varname); @@ -1556,6 +1492,10 @@ get_comp_string(void) } else if (t0 == ENVSTRING) { char sav; /* The cursor was inside a parameter assignment. */ + + if (varq) + tt = clwords[clwpos]; + for (s = tt; iident(*s); s++); sav = *s; *s = '\0'; @@ -2964,7 +2904,8 @@ match_parts(char *l, char *w, int n, int part) * The return value is the string to use as a completion or NULL if the prefix * and the suffix don't match the word w. */ -static char * +/**/ +char * comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact) { @@ -3911,7 +3852,8 @@ join_clines(Cline o, Cline n) /* This adds all the data we have for a match. */ -static Cmatch +/**/ +Cmatch add_match_data(int alt, char *str, Cline line, char *ipre, char *ripre, char *isuf, char *pre, char *prpre, @@ -4181,11 +4123,11 @@ add_match_data(int alt, char *str, Cline line, newmatches = 1; /* One more match for this explanation. */ - if (expl) { + if (curexpl) { if (alt) - expl->fcount++; + curexpl->fcount++; else - expl->count++; + curexpl->count++; } if (!ai->firstm) ai->firstm = cm; @@ -4315,11 +4257,11 @@ addmatches(Cadata dat, char **argv) dparl = newlinklist(); } if (dat->exp) { - expl = (Cexpl) zhalloc(sizeof(struct cexpl)); - expl->count = expl->fcount = 0; - expl->str = dupstring(dat->exp); + curexpl = (Cexpl) zhalloc(sizeof(struct cexpl)); + curexpl->count = curexpl->fcount = 0; + curexpl->str = dupstring(dat->exp); } else - expl = NULL; + curexpl = NULL; /* Store the matcher in our stack of matchers. */ if (dat->match) { @@ -4613,431 +4555,6 @@ addmatches(Cadata dat, char **argv) return (mnum == nm); } -/* This adds a match to the list of matches. The string to add is given * - * in s, the type of match is given in the global variable addwhat and * - * the parameter t (if not NULL) is a pointer to a hash node node which * - * may be used to give other information to this function. * - * * - * addwhat contains either one of the special values (negative, see below) * - * or the inclusive OR of some of the CC_* flags used for compctls. */ - -/**/ -static void -addmatch(char *s, char *t) -{ - int isfile = 0, isalt = 0, isexact; - char *ms = NULL, *tt; - HashNode hn; - Param pm; - Cline lc = NULL; - Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst; - - for (bp = brbeg; bp; bp = bp->next) - bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos); - for (bp = brend; bp; bp = bp->next) - bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos); - - /* - * addwhat: -5 is for files, - * -6 is for glob expansions, - * -8 is for executable files (e.g. command paths), - * -9 is for parameters - * -7 is for command names (from cmdnamtab) - * -4 is for a cdable parameter - * -3 is for executable command names. - * -2 is for anything unquoted - * -1 is for other file specifications - * (things with `~' or `=' at the beginning, ...). - */ - - /* Just to make the code cleaner */ - hn = (HashNode) t; - pm = (Param) t; - - if (addwhat == -1 || addwhat == -5 || addwhat == -6 || - addwhat == CC_FILES || addwhat == -7 || addwhat == -8) { - int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0); - - while (bpl && bpl->curpos < ppl) - bpl = bpl->next; - while (bsl && bsl->curpos < psl) - bsl = bsl->next; - - if ((addwhat == CC_FILES || - addwhat == -5) && !*psuf) { - /* If this is a filename, do the fignore check. */ - char **pt = fignore; - int filell, sl = strlen(s); - - for (isalt = 0; !isalt && *pt; pt++) - if ((filell = strlen(*pt)) < sl && - !strcmp(*pt, s + sl - filell)) - isalt = 1; - } - ms = ((addwhat == CC_FILES || addwhat == -6 || - addwhat == -5 || addwhat == -8) ? - comp_match(qfpre, qfsuf, s, filecomp, &lc, (ppre && *ppre ? 1 : 2), - &bpl, ppl ,&bsl, psl, &isexact) : - comp_match(fpre, fsuf, s, filecomp, &lc, 0, - &bpl, ppl, &bsl, psl, &isexact)); - if (!ms) - return; - - if (addwhat == -7 && !findcmd(s, 0)) - return; - isfile = CMF_FILE; - } else if (addwhat == CC_QUOTEFLAG || addwhat == -2 || - (addwhat == -3 && !(hn->flags & DISABLED)) || - (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) && - !pm->level && (tt = pm->gets.cfn(pm)) && *tt == '/') || - (addwhat == -9 && !(hn->flags & PM_UNSET) && !pm->level) || - (addwhat > 0 && - ((!(hn->flags & PM_UNSET) && - (((addwhat & CC_ARRAYS) && (hn->flags & PM_ARRAY)) || - ((addwhat & CC_INTVARS) && (hn->flags & PM_INTEGER)) || - ((addwhat & CC_ENVVARS) && (hn->flags & PM_EXPORTED)) || - ((addwhat & CC_SCALARS) && (hn->flags & PM_SCALAR)) || - ((addwhat & CC_READONLYS) && (hn->flags & PM_READONLY)) || - ((addwhat & CC_SPECIALS) && (hn->flags & PM_SPECIAL)) || - ((addwhat & CC_PARAMS) && !(hn->flags & PM_EXPORTED))) && - !pm->level) || - ((( addwhat & CC_SHFUNCS) || - ( addwhat & CC_BUILTINS) || - ( addwhat & CC_EXTCMDS) || - ( addwhat & CC_RESWDS) || - ((addwhat & CC_ALREG) && !(hn->flags & ALIAS_GLOBAL)) || - ((addwhat & CC_ALGLOB) && (hn->flags & ALIAS_GLOBAL))) && - (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) || - ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) || - ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) { - char *p1, *s1, *p2, *s2; - - if (addwhat == CC_QUOTEFLAG) { - p1 = qrpre; s1 = qrsuf; - p2 = rpre; s2 = rsuf; - } else { - p1 = qlpre; s1 = qlsuf; - p2 = lpre; s2 = lsuf; - } - bpt = bpl; - bst = bsl; - - if (!(ms = comp_match(p1, s1, s, patcomp, &lc, - (addwhat == CC_QUOTEFLAG), - &bpl, strlen(p1), &bsl, strlen(s1), - &isexact))) { - bpl = bpt; - bsl = bst; - if (!(ms = comp_match(p2, s2, s, NULL, &lc, - (addwhat == CC_QUOTEFLAG), - &bpl, strlen(p2), &bsl, strlen(s2), - &isexact))) - return; - } - } - if (!ms) - return; - add_match_data(isalt, ms, lc, ipre, ripre, isuf, - (incompfunc ? dupstring(curcc->prefix) : curcc->prefix), - prpre, - (isfile ? lppre : NULL), NULL, - (isfile ? lpsuf : NULL), NULL, - (incompfunc ? dupstring(curcc->suffix) : curcc->suffix), - (mflags | isfile), isexact); -} - -#ifdef HAVE_NIS_PLUS -static int -match_username(nis_name table, nis_object *object, void *userdata) -{ - if (errflag) - return 1; - else { - static char buf[40]; - register entry_col *ec = - object->zo_data.objdata_u.en_data.en_cols.en_cols_val; - register int l = minimum(ec->ec_value.ec_value_len, 39); - - memcpy(buf, ec->ec_value.ec_value_val, l); - buf[l] = '\0'; - - addmatch(dupstring(buf), NULL); - } - return 0; -} -#else -# ifdef HAVE_NIS -static int -match_username(int status, char *key, int keylen, char *val, int vallen, dopestring *data) -{ - if (errflag || status != YP_TRUE) - return 1; - - if (vallen > keylen && val[keylen] == ':') { - val[keylen] = '\0'; - addmatch(dupstring(val), NULL); - } - return 0; -} -# endif /* HAVE_NIS */ -#endif /* HAVE_NIS_PLUS */ - -/**/ -static void -maketildelist(void) -{ -#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS) - FILE *pwf; - char buf[BUFSIZ], *p; - int skipping; - -# ifndef HAVE_NIS_PLUS - char domain[YPMAXDOMAIN]; - struct ypall_callback cb; - dopestring data; - - data.s = fpre; - data.len = fpl; - /* Get potential matches from NIS and cull those without local accounts */ - if (getdomainname(domain, YPMAXDOMAIN) == 0) { - cb.foreach = (int (*)()) match_username; - cb.data = (char *)&data; - yp_all(domain, PASSWD_MAP, &cb); -/* for (n = firstnode(matches); n; incnode(n)) - if (getpwnam(getdata(n)) == NULL) - uremnode(matches, n);*/ - } -# else /* HAVE_NIS_PLUS */ - /* Maybe we should turn this string into a #define'd constant...? */ - - nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH, - match_username, 0); -# endif - /* Don't forget the non-NIS matches from the flat passwd file */ - if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) { - skipping = 0; - while (fgets(buf, BUFSIZ, pwf) != NULL) { - if (strchr(buf, '\n') != NULL) { - if (!skipping) { - if ((p = strchr(buf, ':')) != NULL) { - *p = '\0'; - addmatch(dupstring(buf), NULL); - } - } else - skipping = 0; - } else - skipping = 1; - } - fclose(pwf); - } -#else /* no NIS or NIS_PLUS */ - /* add all the usernames to the named directory table */ - nameddirtab->filltable(nameddirtab); -#endif - - scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0, - addhnmatch, 0); -} - -/* This does the check for compctl -x `n' and `N' patterns. */ - -/**/ -int -getcpat(char *str, int cpatindex, char *cpat, int class) -{ - char *s, *t, *p; - int d = 0; - - if (!str || !*str) - return -1; - - cpat = rembslash(cpat); - - if (!cpatindex) - cpatindex++, d = 0; - else if ((d = (cpatindex < 0))) - cpatindex = -cpatindex; - - for (s = d ? str + strlen(str) - 1 : str; - d ? (s >= str) : *s; - d ? s-- : s++) { - for (t = s, p = cpat; *t && *p; p++) { - if (class) { - if (*p == *s && !--cpatindex) - return (int)(s - str + 1); - } else if (*t++ != *p) - break; - } - if (!class && !*p && !--cpatindex) - return t - str; - } - return -1; -} - -/* Dump a hash table (without sorting). For each element the addmatch * - * function is called and at the beginning the addwhat variable is set. * - * This could be done using scanhashtable(), but this is easy and much * - * more efficient. */ - -/**/ -static void -dumphashtable(HashTable ht, int what) -{ - HashNode hn; - int i; - - addwhat = what; - - for (i = 0; i < ht->hsize; i++) - for (hn = ht->nodes[i]; hn; hn = hn->next) - addmatch(hn->nam, (char *) hn); -} - -/* ScanFunc used by maketildelist() et al. */ - -/**/ -static void -addhnmatch(HashNode hn, int flags) -{ - addmatch(hn->nam, NULL); -} - -/* Perform expansion on the given string and return the result. * - * During this errors are not reported. */ - -/**/ -static char * -getreal(char *str) -{ - LinkList l = newlinklist(); - int ne = noerrs; - - noerrs = 1; - addlinknode(l, dupstring(str)); - prefork(l, 0); - noerrs = ne; - if (!errflag && nonempty(l) && - ((char *) peekfirst(l)) && ((char *) peekfirst(l))[0]) - return dupstring(peekfirst(l)); - errflag = 0; - - return dupstring(str); -} - -/* This reads a directory and adds the files to the list of * - * matches. The parameters say which files should be added. */ - -/**/ -static void -gen_matches_files(int dirs, int execs, int all) -{ - DIR *d; - struct stat buf; - char *n, p[PATH_MAX], *q = NULL, *e; - LinkList l = NULL; - int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat; - - opts[NULLGLOB] = 1; - - if (*psuf) { - /* If there is a path suffix, check if it doesn't have a `*' or * - * `)' at the end (this is used to determine if we should use * - * globbing). */ - q = psuf + strlen(psuf) - 1; - ns = !(*q == Star || *q == Outpar); - l = newlinklist(); - /* And generate only directory names. */ - dirs = 1; - all = execs = 0; - } - /* Open directory. */ - if ((d = opendir((prpre && *prpre) ? prpre : "."))) { - /* If we search only special files, prepare a path buffer for stat. */ - if (!all && prpre) { - strcpy(p, prpre); - q = p + strlen(prpre); - } - /* Fine, now read the directory. */ - while ((n = zreaddir(d, 1)) && !errflag) { - /* Ignore files beginning with `.' unless the thing we found on * - * the command line also starts with a dot or GLOBDOTS is set. */ - if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) { - addwhat = execs ? -8 : -5; - if (filecomp) - /* If we have a pattern for the filename check, use it. */ - test = pattry(filecomp, n); - else { - /* Otherwise use the prefix and suffix strings directly. */ - e = n + strlen(n) - fsl; - if ((test = !strncmp(n, fpre, fpl))) - test = !strcmp(e, fsuf); - if (!test && mstack) { - test = 1; - addwhat = CC_FILES; - } - } - /* Filename didn't match? */ - if (!test) - continue; - if (!all) { - /* We still have to check the file type, so prepare * - * the path buffer by appending the filename. */ - strcpy(q, n); - /* And do the stat. */ - if (stat(p, &buf) < 0) - continue; - } - if (all || - (dirs && S_ISDIR(buf.st_mode)) || - (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) { - /* If we want all files or the file has the right type... */ - if (*psuf) { - /* We have to test for a path suffix. */ - int o = strlen(p), tt; - - /* Append it to the path buffer. */ - strcpy(p + o, psuf); - - /* Do we have to use globbing? */ - if (ispattern || - (ns && comppatmatch && *comppatmatch)) { - /* Yes, so append a `*' if needed. */ - if (ns && comppatmatch && *comppatmatch == '*') { - int tl = strlen(p); - - p[tl] = Star; - p[tl + 1] = '\0'; - } - /* Do the globbing... */ - remnulargs(p); - addlinknode(l, p); - globlist(l); - /* And see if that produced a filename. */ - tt = nonempty(l); - while (ugetnode(l)); - } else - /* Otherwise just check, if we have access * - * to the file. */ - tt = !access(p, F_OK); - - p[o] = '\0'; - if (tt) - /* Ok, we can add the filename to the * - * list of matches. */ - addmatch(dupstring(n), NULL); - } else - /* We want all files, so just add the name * - * to the matches. */ - addmatch(dupstring(n), NULL); - } - } - } - closedir(d); - } - opts[NULLGLOB] = ng; - addwhat = aw; -} - /**/ static int docompletion(char *s, int lst, int incmd) @@ -5180,44 +4697,6 @@ docompletion(char *s, int lst, int incmd) useline != 2 && (!oldlist || !listshown)) { onlyexpl = 1; showinglist = -2; -#if 0 - Cmgroup g = amatches; - Cexpl *e; - int up = 0, tr = 1, nn = 0; - - if (!nmatches) - ret = 1; - - while (g) { - if ((e = g->expls)) - while (*e) { - if ((*e)->count) { - if (tr) { - trashzle(); - tr = 0; - } - if (nn) { - up++; - putc('\n', shout); - } - up += printfmt((*e)->str, (*e)->count, 1, 1); - nn = 1; - } - e++; - } - g = g->next; - } - if (!tr) { - clearflag = (isset(USEZLE) && !termflags && - complastprompt && *complastprompt); - - if (clearflag && up + nlnct < lines) - tcmultout(TCUP, TCMULTUP, up + nlnct); - else - putc('\n', shout); - fflush(shout); - } -#endif } compend: for (n = firstnode(matchers); n; incnode(n)) @@ -5530,14 +5009,6 @@ callcompfunc(char *s, char *fn) lastval = lv; } -/* The beginning and end of a word range to be used by -l. */ - -static int brange, erange; - -/* This is used to detect when and what to continue. */ - -static unsigned long ccont; - /* Create the completion list. This is called whenever some bit of * * completion code needs the list. * * Along with the list is maintained the prefixes/suffixes etc. When * @@ -5620,26 +5091,24 @@ makecomplist(char *s, int incmd, int lst) begcmgroup("default", 0); menucmp = menuacc = newmatches = onlyexpl = 0; - ccused = newlinklist(); - ccstack = newlinklist(); + runhookdef(COMPCTLBEFOREHOOK, NULL); s = dupstring(os); if (compfunc) callcompfunc(s, compfunc); - else - makecomplistglobal(s, incmd, lst, 0); + else { + struct ccmakedat dat; + dat.str = s; + dat.incmd = incmd; + dat.lst = lst; + runhookdef(COMPCTLMAKEHOOK, (void *) &dat); + } endcmgroup(NULL); - if (amatches && !oldlist) - amatches->ccs = (Compctl *) makearray(ccused, 0, 0, - &(amatches->ccount), NULL, NULL); - else { - LinkNode n; + runhookdef(COMPCTLAFTERHOOK, + (void *) ((amatches && !oldlist) ? 1L : 0L)); - for (n = firstnode(ccused); n; incnode(n)) - freecompctl((Compctl) getdata(n)); - } if (oldlist) { nmatches = onm; validlist = 1; @@ -5754,25 +5223,17 @@ set_comp_sep(void) { int lip, lp; char *s = comp_str(&lip, &lp, 0); - - if (compisuffix) - s = dyncat(s, compisuffix); - untokenize(s); - - return sep_comp_string("", s, lip + lp, 0); -} - -/**/ -static int -sep_comp_string(char *ss, char *s, int noffs, int rec) -{ LinkList foo = newlinklist(); LinkNode n; int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs; - int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll; - int ois = instring, oib = inbackt; + int tl, got = 0, i = 0, cur = -1, oll = ll, sl; + int ois = instring, oib = inbackt, noffs = lip + lp; char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs; + if (compisuffix) + s = dyncat(s, compisuffix); + untokenize(s); + swb = swe = soffs = 0; ns = NULL; @@ -5782,14 +5243,12 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) addedx = 1; noerrs = 1; lexsave(); - tmp = (char *) zhalloc(tl = sl + 3 + strlen(s)); - strcpy(tmp, ss); - tmp[sl] = ' '; - memcpy(tmp + sl + 1, s, noffs); - tmp[(scs = cs = sl + 1 + noffs)] = 'x'; - strcpy(tmp + sl + 2 + noffs, s + noffs); - if (incompfunc) - tmp = rembslash(tmp); + tmp = (char *) zhalloc(tl = 3 + strlen(s)); + tmp[0] = ' '; + memcpy(tmp + 1, s, noffs); + tmp[(scs = cs = 1 + noffs)] = 'x'; + strcpy(tmp + 2 + noffs, s + noffs); + tmp = rembslash(tmp); inpush(dupstrspace(tmp), 0, NULL); line = (unsigned char *) tmp; ll = tl - 1; @@ -5882,59 +5341,22 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) chuck(p--); } } - sav = s[(i = swb - sl - 1)]; + sav = s[(i = swb - 1)]; s[i] = '\0'; - qp = tricat(qipre, (incompfunc ? rembslash(s) : s), ""); + qp = tricat(qipre, rembslash(s), ""); s[i] = sav; if (swe < swb) swe = swb; - swe -= sl + 1; + swe--; sl = strlen(s); if (swe > sl) swe = sl, ns[swe - swb + 1] = '\0'; - qs = tricat((incompfunc ? rembslash(s + swe) : s + swe), qisuf, ""); + qs = tricat(rembslash(s + swe), qisuf, ""); sl = strlen(ns); if (soffs > sl) soffs = sl; - if (rec) { - char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf; - int olws = clwsize, olwn = clwnum, olwp = clwpos; - int obr = brange, oer = erange, oof = offs; - unsigned long occ = ccont; - - clwsize = clwnum = countlinknodes(foo); - clwords = (char **) zalloc((clwnum + 1) * sizeof(char *)); - for (n = firstnode(foo), i = 0; n; incnode(n), i++) { - p = clwords[i] = (char *) getdata(n); - untokenize(p); - } - clwords[i] = NULL; - clwpos = cur; - cmdstr = ztrdup(clwords[0]); - brange = 0; - erange = clwnum - 1; - qipre = qp; - qisuf = qs; - offs = soffs; - ccont = CC_CCCONT; - makecomplistcmd(ns, !clwpos, CFN_FIRST); - ccont = occ; - offs = oof; - zsfree(cmdstr); - cmdstr = os; - freearray(clwords); - clwords = ow; - clwsize = olws; - clwnum = olwn; - clwpos = olwp; - brange = obr; - erange = oer; - zsfree(qipre); - qipre = oqp; - zsfree(qisuf); - qisuf = oqs; - } else { + { int set = CP_QUOTE | CP_QUOTING, unset = 0; zsfree(compquote); @@ -6005,1497 +5427,11 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) return 0; } -/**/ -int -makecomplistcall(Compctl cc) -{ - int nm = mnum; - - SWITCHHEAPS(compheap) { - HEAPALLOC { - int ooffs = offs, lip, lp, ois = instring, oib = inbackt; - char *str = comp_str(&lip, &lp, 0), qc; - char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq; - - if (compquote && (qc = *compquote)) { - if (qc == '`') { - instring = 0; - inbackt = 0; - autoq = '\0'; - } else { - instring = (qc == '\'' ? 1 : 2); - inbackt = 0; - autoq = qc; - } - } else { - instring = inbackt = 0; - autoq = '\0'; - } - isuf = dupstring(compisuffix); - ctokenize(isuf); - remnulargs(isuf); - qipre = ztrdup(compqiprefix ? compqiprefix : ""); - qisuf = ztrdup(compqisuffix ? compqisuffix : ""); - offs = lip + lp; - cc->refc++; - ccont = 0; - if (!cc->ylist && !cc->gname) { - endcmgroup(NULL); - begcmgroup("default", 0); - } - makecomplistor(cc, str, lincmd, lip, 0); - offs = ooffs; - isuf = oisuf; - zsfree(qipre); - zsfree(qisuf); - qipre = oqp; - qisuf = oqs; - instring = ois; - inbackt = oib; - autoq = oaq; - } LASTALLOC; - } SWITCHBACKHEAPS; - - return (mnum == nm); -} - -/* A simple counter to avoid endless recursion between old and new style * - * completion. */ - -static int cdepth = 0; - -#define MAX_CDEPTH 16 +/* Invalidate the completion list. */ /**/ -int -makecomplistctl(int flags) -{ - int ret; - - if (cdepth == MAX_CDEPTH) - return 0; - - cdepth++; - SWITCHHEAPS(compheap) { - HEAPALLOC { - int ooffs = offs, lip, lp; - char *str = comp_str(&lip, &lp, 0), *t; - char *os = cmdstr, **ow = clwords, **p, **q, qc; - int on = clwnum, op = clwpos, ois = instring, oib = inbackt; - char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq; - - if (compquote && (qc = *compquote)) { - if (qc == '`') { - instring = 0; - inbackt = 0; - autoq = '\0'; - } else { - instring = (qc == '\'' ? 1 : 2); - inbackt = 0; - autoq = qc; - } - } else { - instring = inbackt = 0; - autoq = '\0'; - } - qipre = ztrdup(compqiprefix ? compqiprefix : ""); - qisuf = ztrdup(compqisuffix ? compqisuffix : ""); - isuf = dupstring(compisuffix); - ctokenize(isuf); - remnulargs(isuf); - clwnum = arrlen(compwords); - clwpos = compcurrent - 1; - cmdstr = ztrdup(compwords[0]); - clwords = (char **) zalloc((clwnum + 1) * sizeof(char *)); - for (p = compwords, q = clwords; *p; p++, q++) { - t = dupstring(*p); - tokenize(t); - remnulargs(t); - *q = ztrdup(t); - } - *q = NULL; - offs = lip + lp; - incompfunc = 2; - ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags); - incompfunc = 1; - isuf = oisuf; - zsfree(qipre); - zsfree(qisuf); - qipre = oqp; - qisuf = oqs; - instring = ois; - inbackt = oib; - autoq = oaq; - offs = ooffs; - zsfree(cmdstr); - freearray(clwords); - cmdstr = os; - clwords = ow; - clwnum = on; - clwpos = op; - } LASTALLOC; - } SWITCHBACKHEAPS; - cdepth--; - - return ret; -} - -/* This function gets the compctls for the given command line and * - * adds all completions for them. */ - -/**/ -static int -makecomplistglobal(char *os, int incmd, int lst, int flags) -{ - Compctl cc = NULL; - char *s; - - ccont = CC_CCCONT; - cc_dummy.suffix = NULL; - - if (linwhat == IN_ENV) { - /* Default completion for parameter values. */ - if (!(flags & CFN_DEFAULT)) { - cc = &cc_default; - keypm = NULL; - } - } else if (linwhat == IN_MATH) { - if (!(flags & CFN_DEFAULT)) { - if (insubscr >= 2) { - /* Inside subscript of assoc array, complete keys. */ - cc_dummy.mask = 0; - cc_dummy.suffix = (insubscr == 2 ? "]" : ""); - } else { - /* Other math environment, complete paramete names. */ - keypm = NULL; - cc_dummy.mask = CC_PARAMS; - } - cc = &cc_dummy; - cc_dummy.refc = 10000; - } - } else if (linwhat == IN_COND) { - /* We try to be clever here: in conditions we complete option * - * names after a `-o', file names after `-nt', `-ot', and `-ef' * - * and file names and parameter names elsewhere. */ - if (!(flags & CFN_DEFAULT)) { - s = clwpos ? clwords[clwpos - 1] : ""; - cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS : - ((*s == '-' && s[1] && !s[2]) || - !strcmp("-nt", s) || - !strcmp("-ot", s) || - !strcmp("-ef", s)) ? CC_FILES : - (CC_FILES | CC_PARAMS); - cc = &cc_dummy; - cc_dummy.refc = 10000; - keypm = NULL; - } - } else if (linredir) { - if (!(flags & CFN_DEFAULT)) { - /* In redirections use default completion. */ - cc = &cc_default; - keypm = NULL; - } - } else { - /* Otherwise get the matches for the command. */ - keypm = NULL; - return makecomplistcmd(os, incmd, flags); - } - if (cc) { - /* First, use the -T compctl. */ - if (!(flags & CFN_FIRST)) { - makecomplistcc(&cc_first, os, incmd); - - if (!(ccont & CC_CCCONT)) - return 0; - } - makecomplistcc(cc, os, incmd); - return 1; - } - return 0; -} - -/* This produces the matches for a command. */ - -/**/ -static int -makecomplistcmd(char *os, int incmd, int flags) -{ - Compctl cc; - Compctlp ccp; - char *s; - int ret = 0; - - /* First, use the -T compctl. */ - if (!(flags & CFN_FIRST)) { - makecomplistcc(&cc_first, os, incmd); - - if (!(ccont & CC_CCCONT)) - return 0; - } - /* Then search the pattern compctls, with the command name and the * - * full pathname of the command. */ - if (cmdstr) { - ret |= makecomplistpc(os, incmd); - if (!(ccont & CC_CCCONT)) - return ret; - } - /* If the command string starts with `=', try the path name of the * - * command. */ - if (cmdstr && cmdstr[0] == Equals) { - char *c = findcmd(cmdstr + 1, 1); - - if (c) { - zsfree(cmdstr); - cmdstr = ztrdup(c); - } - } - - /* Find the compctl for this command, trying the full name and then * - * the trailing pathname component. If that doesn't yield anything, * - * use default completion. */ - if (incmd) - cc = &cc_compos; - else if (!(cmdstr && - (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) && - (cc = ccp->cc)) || - ((s = dupstring(cmdstr)) && remlpaths(&s) && - (ccp = (Compctlp) compctltab->getnode(compctltab, s)) && - (cc = ccp->cc))))) { - if (flags & CFN_DEFAULT) - return ret; - cc = &cc_default; - } else - ret|= 1; - makecomplistcc(cc, os, incmd); - return ret; -} - -/* This add the matches for the pattern compctls. */ - -/**/ -static int -makecomplistpc(char *os, int incmd) -{ - Patcomp pc; - Patprog pat; - char *s = findcmd(cmdstr, 1); - int ret = 0; - - for (pc = patcomps; pc; pc = pc->next) { - if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) && - (pattry(pat, cmdstr) || - (s && pattry(pat, s)))) { - makecomplistcc(pc->cc, os, incmd); - ret |= 2; - if (!(ccont & CC_CCCONT)) - return ret; - } - } - return ret; -} - -/* This produces the matches for one compctl. */ - -/**/ -static void -makecomplistcc(Compctl cc, char *s, int incmd) -{ - cc->refc++; - addlinknode(ccused, cc); - - ccont = 0; - - makecomplistor(cc, s, incmd, 0, 0); -} - -/* This adds the completions for one run of [x]or'ed completions. */ - -/**/ -static void -makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub) -{ - int mn, ct, um = usemenu; - - /* Loop over xors. */ - do { - mn = mnum; - - /* Loop over ors. */ - do { - /* Reset the range information if we are not in a sub-list. */ - if (!sub) { - brange = 0; - erange = clwnum - 1; - } - usemenu = 0; - makecomplistlist(cc, s, incmd, compadd); - um |= usemenu; - - ct = cc->mask2 & CC_XORCONT; - - cc = cc->xor; - } while (cc && ct); - - /* Stop if we got some matches. */ - if (mn != mnum) - break; - if (cc) { - ccont &= ~(CC_DEFCONT | CC_PATCONT); - if (!sub) - ccont &= ~CC_CCCONT; - } - } while (cc); - - usemenu = um; -} - -/* This dispatches for simple and extended completion. */ - -/**/ -static void -makecomplistlist(Compctl cc, char *s, int incmd, int compadd) -{ - int oloffs = offs, owe = we, owb = wb, ocs = cs; - - if (cc->ext) - /* Handle extended completion. */ - makecomplistext(cc, s, incmd); - else - /* Only normal flags. */ - makecomplistflags(cc, s, incmd, compadd); - - /* Reset some information variables for the next try. */ - errflag = 0; - offs = oloffs; - wb = owb; - we = owe; - cs = ocs; -} - -/* This add matches for extended completion patterns */ - -/**/ -static void -makecomplistext(Compctl occ, char *os, int incmd) -{ - Compctl compc; - Compcond or, cc; - Patprog pprog; - int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins; - char *sc = NULL, *s, *ss; - - ins = (instring ? instring : (inbackt ? 3 : 0)); - - /* This loops over the patterns separated by `-'s. */ - for (compc = occ->ext; compc; compc = compc->next) { - compadd = t = brange = 0; - erange = clwnum - 1; - /* This loops over OR'ed patterns. */ - for (cc = compc->cond; cc && !t; cc = or) { - or = cc->or; - /* This loops over AND'ed patterns. */ - for (t = 1; cc && t; cc = cc->and) { - /* And this loops over [...] pairs. */ - for (t = i = 0; i < cc->n && !t; i++) { - s = NULL; - brange = 0; - erange = clwnum - 1; - switch (cc->type) { - case CCT_QUOTE: - t = ((cc->u.s.s[i][0] == 's' && ins == 1) || - (cc->u.s.s[i][0] == 'd' && ins == 2) || - (cc->u.s.s[i][0] == 'b' && ins == 3)); - break; - case CCT_POS: - tt = clwpos; - goto cct_num; - case CCT_NUMWORDS: - tt = clwnum; - cct_num: - if ((a = cc->u.r.a[i]) < 0) - a += clwnum; - if ((b = cc->u.r.b[i]) < 0) - b += clwnum; - if (cc->type == CCT_POS) - brange = a, erange = b; - t = (tt >= a && tt <= b); - break; - case CCT_CURSUF: - case CCT_CURPRE: - s = ztrdup(clwpos < clwnum ? os : ""); - untokenize(s); - if (isset(COMPLETEINWORD)) s[offs] = '\0'; - sc = rembslash(cc->u.s.s[i]); - a = strlen(sc); - if (!strncmp(s, sc, a)) { - compadd = (cc->type == CCT_CURSUF ? a : 0); - t = 1; - } - break; - case CCT_CURSUB: - case CCT_CURSUBC: - if (clwpos < 0 || clwpos >= clwnum) - t = 0; - else { - s = ztrdup(os); - untokenize(s); - if (isset(COMPLETEINWORD)) s[offs] = '\0'; - a = getcpat(s, - cc->u.s.p[i], - cc->u.s.s[i], - cc->type == CCT_CURSUBC); - if (a != -1) - compadd = a, t = 1; - } - break; - - case CCT_CURPAT: - case CCT_CURSTR: - tt = clwpos; - goto cct_str; - case CCT_WORDPAT: - case CCT_WORDSTR: - tt = 0; - cct_str: - if ((a = tt + cc->u.s.p[i]) < 0) - a += clwnum; - s = ztrdup((a < 0 || a >= clwnum) ? "" : - clwords[a]); - untokenize(s); - - if (cc->type == CCT_CURPAT || - cc->type == CCT_WORDPAT) { - tokenize(ss = dupstring(cc->u.s.s[i])); - t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) && - pattry(pprog, s)); - } else - t = (!strcmp(s, rembslash(cc->u.s.s[i]))); - break; - case CCT_RANGESTR: - case CCT_RANGEPAT: - if (cc->type == CCT_RANGEPAT) - tokenize(sc = dupstring(cc->u.l.a[i])); - for (j = clwpos - 1; j > 0; j--) { - untokenize(s = ztrdup(clwords[j])); - if (cc->type == CCT_RANGESTR) - sc = rembslash(cc->u.l.a[i]); - if (cc->type == CCT_RANGESTR ? - !strncmp(s, sc, strlen(sc)) : - ((pprog = patcompile(sc, PAT_STATIC, 0)) && - pattry(pprog, s))) { - zsfree(s); - brange = j + 1; - t = 1; - break; - } - zsfree(s); - } - if (t && cc->u.l.b[i]) { - if (cc->type == CCT_RANGEPAT) - tokenize(sc = dupstring(cc->u.l.b[i])); - for (j++; j < clwnum; j++) { - untokenize(s = ztrdup(clwords[j])); - if (cc->type == CCT_RANGESTR) - sc = rembslash(cc->u.l.b[i]); - if (cc->type == CCT_RANGESTR ? - !strncmp(s, sc, strlen(sc)) : - ((pprog = patcompile(sc, PAT_STATIC, 0)) && - pattry(pprog, s))) { - zsfree(s); - erange = j - 1; - t = clwpos <= erange; - break; - } - zsfree(s); - } - } - s = NULL; - } - zsfree(s); - } - } - } - if (t) { - /* The patterns matched, use the flags. */ - m = 1; - ccont &= ~(CC_PATCONT | CC_DEFCONT); - makecomplistor(compc, os, incmd, compadd, 1); - if (!d && (ccont & CC_DEFCONT)) { - d = 1; - compadd = 0; - brange = 0; - erange = clwnum - 1; - makecomplistflags(occ, os, incmd, 0); - } - if (!(ccont & CC_PATCONT)) - break; - } - } - /* If no pattern matched, use the standard flags. */ - if (!m) { - compadd = 0; - brange = 0; - erange = clwnum - 1; - makecomplistflags(occ, os, incmd, 0); - } -} - -/* This returns the node with the given data. */ -/* ...should probably be moved to linklist.c. */ - -static LinkNode -findnode(LinkList list, void *dat) -{ - LinkNode tmp = list->first; - - while (tmp && tmp->dat != dat) tmp = tmp->next; - - return tmp; -} - -/* This adds the completions for the flags in the given compctl. */ - -/**/ -static void -makecomplistflags(Compctl cc, char *s, int incmd, int compadd) -{ - int t, sf1, sf2, ooffs, um = usemenu, delit, oaw, gflags; - int mn = mnum, ohp = haspattern; - char *p, *sd = NULL, *tt, *s1, *s2, *os = dupstring(s); - struct cmlist ms; - - ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT)); - - if (incompfunc != 1 && findnode(ccstack, cc)) - return; - - MUSTUSEHEAP("complistflags"); - - addlinknode(ccstack, cc); - - if (incompfunc != 1 && allccs) { - if (findnode(allccs, cc)) { - uremnode(ccstack, firstnode(ccstack)); - return; - } - addlinknode(allccs, cc); - } - /* Go to the end of the word if complete_in_word is not set. */ - if (unset(COMPLETEINWORD) && cs != we) - cs = we, offs = strlen(s); - - s = dupstring(s); - delit = ispattern = 0; - usemenu = um; - patcomp = filecomp = NULL; - rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf = - fpre = fsuf = ipre = ripre = prpre = - qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL; - - curcc = cc; - - mflags = 0; - gflags = (((cc->mask2 & CC_NOSORT ) ? CGF_NOSORT : 0) | - ((cc->mask2 & CC_UNIQALL) ? CGF_UNIQALL : 0) | - ((cc->mask2 & CC_UNIQCON) ? CGF_UNIQCON : 0)); - if (cc->gname) { - endcmgroup(NULL); - begcmgroup(cc->gname, gflags); - } - if (cc->ylist) { - endcmgroup(NULL); - begcmgroup(NULL, gflags); - } - if (cc->mask & CC_REMOVE) - mflags |= CMF_REMOVE; - if (cc->explain) { - expl = (Cexpl) zhalloc(sizeof(struct cexpl)); - expl->count = expl->fcount = 0; - } else - expl = NULL; - /* compadd is the number of characters we have to ignore at the * - * beginning of the word. */ - if (compadd) { - ipre = dupstring(s); - ipre[compadd] = '\0'; - untokenize(ipre); - wb += compadd; - s += compadd; - if ((offs -= compadd) < 0) { - /* It's bigger than our word prefix, so we can't help here... */ - uremnode(ccstack, firstnode(ccstack)); - return; - } - } else - ipre = NULL; - - if (cc->matcher) { - ms.next = mstack; - ms.matcher = cc->matcher; - mstack = &ms; - - if (!mnum) - add_bmatchers(cc->matcher); - - addlinknode(matchers, cc->matcher); - cc->matcher->refc++; - } - if (mnum && (mstack || bmatchers)) - update_bmatchers(); - - /* Insert the prefix (compctl -P), if any. */ - if (cc->prefix) { - int pl = 0; - - if (*s) { - char *dp = rembslash(cc->prefix); - /* First find out how much of the prefix is already on the line. */ - sd = dupstring(s); - untokenize(sd); - pl = pfxlen(dp, sd); - s += pl; - sd += pl; - offs -= pl; - } - } - /* Does this compctl have a suffix (compctl -S)? */ - if (cc->suffix) { - char *sdup = rembslash(cc->suffix); - int sl = strlen(sdup), suffixll; - - /* Ignore trailing spaces. */ - for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--); - p[1] = '\0'; - - if (!sd) { - sd = dupstring(s); - untokenize(sd); - } - /* If the suffix is already there, ignore it (and don't add * - * it again). */ - if (*sd && (suffixll = strlen(sd)) >= sl && - offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl)) - s[suffixll - sl] = '\0'; - } - /* Do we have one of the special characters `~' and `=' at the beginning? */ - if (incompfunc || ((ic = *s) != Tilde && ic != Equals)) - ic = 0; - - /* Check if we have to complete a parameter name... */ - if (!incompfunc && (p = check_param(s, 1, 0))) { - s = p; - /* And now make sure that we complete parameter names. */ - cc = &cc_dummy; - cc_dummy.refc = 10000; - cc_dummy.mask = CC_PARAMS | CC_ENVVARS; - } - ooffs = offs; - /* If we have to ignore the word, do that. */ - if (cc->mask & CC_DELETE) { - delit = 1; - *s = '\0'; - offs = 0; - if (isset(AUTOMENU)) - usemenu = 1; - } - - /* Compute line prefix/suffix. */ - lpl = offs; - lpre = zhalloc(lpl + 1); - memcpy(lpre, s, lpl); - lpre[lpl] = '\0'; - qlpre = quotename(lpre, NULL); - lsuf = dupstring(s + offs); - lsl = strlen(lsuf); - qlsuf = quotename(lsuf, NULL); - - /* First check for ~.../... */ - if (ic == Tilde) { - for (p = lpre + lpl; p > lpre; p--) - if (*p == '/') - break; - - if (*p == '/') - ic = 0; - } - /* Compute real prefix/suffix. */ - - noreal = !delit; - for (p = lpre; *p && *p != String && *p != Tick; p++); - tt = ic && !ispar ? lpre + 1 : lpre; - rpre = (*p || *lpre == Tilde || *lpre == Equals) ? - (noreal = 0, getreal(tt)) : - dupstring(tt); - qrpre = quotename(rpre, NULL); - - for (p = lsuf; *p && *p != String && *p != Tick; p++); - rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf); - qrsuf = quotename(rsuf, NULL); - - /* Check if word is a pattern. */ - - for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1; - p >= rpre && (ispattern != 3 || !sf1); - p--) - if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde))) - ispattern |= sf1 ? 1 : 2; - else if (*p == '/') { - sf1++; - if (!s1) - s1 = p; - } - rsl = strlen(rsuf); - for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++) - if (itok(*p)) - t |= sf2 ? 4 : 2; - else if (*p == '/') { - sf2++; - if (!s2) - s2 = p; - } - ispattern = ispattern | t; - - /* But if we were asked not to do glob completion, we never treat the * - * thing as a pattern. */ - if (!comppatmatch || !*comppatmatch) - ispattern = 0; - - if (ispattern) { - /* The word should be treated as a pattern, so compute the matcher. */ - p = (char *) zhalloc(rpl + rsl + 2); - strcpy(p, rpre); - if (rpl && p[rpl - 1] != Star && - (!comppatmatch || *comppatmatch == '*')) { - p[rpl] = Star; - strcpy(p + rpl + 1, rsuf); - } else - strcpy(p + rpl, rsuf); - patcomp = patcompile(p, 0, NULL); - haspattern = 1; - } - if (!patcomp) { - untokenize(rpre); - untokenize(rsuf); - - rpl = strlen(rpre); - rsl = strlen(rsuf); - } - untokenize(lpre); - untokenize(lsuf); - - if (!(cc->mask & CC_DELETE)) - hasmatched = 1; - - /* Handle completion of files specially (of course). */ - - if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) { - /* s1 and s2 point to the last/first slash in the prefix/suffix. */ - if (!s1) - s1 = rpre; - if (!s2) - s2 = rsuf + rsl; - - /* Compute the path prefix/suffix. */ - if (*s1 != '/') - ppre = ""; - else - ppre = dupstrpfx(rpre, s1 - rpre + 1); - psuf = dupstring(s2); - - if (cs != wb) { - char save = line[cs]; - - line[cs] = 0; - lppre = dupstring((char *) line + wb + - (qipre && *qipre ? - (strlen(qipre) - - (*qipre == '\'' || *qipre == '\"')) : 0)); - line[cs] = save; - if (brbeg) { - Brinfo bp; - - for (bp = brbeg; bp; bp = bp->next) - strcpy(lppre + bp->qpos, - lppre + bp->qpos + strlen(bp->str)); - } - if ((p = strrchr(lppre, '/'))) { - p[1] = '\0'; - lppl = strlen(lppre); - } else if (!sf1) { - lppre = NULL; - lppl = 0; - } else { - lppre = ppre; - lppl = strlen(lppre); - } - } else { - lppre = NULL; - lppl = 0; - } - if (cs != we) { - int end = we; - char save = line[end]; - - if (qisuf && *qisuf) { - int ql = strlen(qisuf); - - end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"'); - } - line[end] = 0; - lpsuf = dupstring((char *) (line + cs)); - line[end] = save; - if (brend) { - Brinfo bp; - char *p; - int bl; - - for (bp = brend; bp; bp = bp->next) { - p = lpsuf + (we - cs) - bp->qpos - (bl = strlen(bp->str)); - strcpy(p, p + bl); - } - } - if (!(lpsuf = strchr(lpsuf, '/')) && sf2) - lpsuf = psuf; - lpsl = (lpsuf ? strlen(lpsuf) : 0); - } else { - lpsuf = NULL; - lpsl = 0; - } - - /* And get the file prefix. */ - fpre = dupstring(((s1 == s || s1 == rpre || ic) && - (*s != '/' || cs == wb)) ? s1 : s1 + 1); - qfpre = quotename(fpre, NULL); - /* And the suffix. */ - fsuf = dupstrpfx(rsuf, s2 - rsuf); - qfsuf = quotename(fsuf, NULL); - - if (comppatmatch && *comppatmatch && (ispattern & 2)) { - int t2; - - /* We have to use globbing, so compute the pattern from * - * the file prefix and suffix with a `*' between them. */ - p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2); - strcpy(p, fpre); - if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star && - (!comppatmatch || *comppatmatch == '*')) - p[t2++] = Star; - strcpy(p + t2, fsuf); - filecomp = patcompile(p, 0, NULL); - } - if (!filecomp) { - untokenize(fpre); - untokenize(fsuf); - - fpl = strlen(fpre); - fsl = strlen(fsuf); - } - addwhat = -1; - - /* Completion after `~', maketildelist adds the usernames * - * and named directories. */ - if (ic == Tilde) { - char *oi = ipre; - - ipre = (ipre ? dyncat("~", ipre) : "~"); - maketildelist(); - ipre = oi; - } else if (ic == Equals) { - /* Completion after `=', get the command names from * - * the cmdnamtab and aliases from aliastab. */ - char *oi = ipre; - - ipre = (ipre ? dyncat("=", ipre) : "="); - if (isset(HASHLISTALL)) - cmdnamtab->filltable(cmdnamtab); - dumphashtable(cmdnamtab, -7); - dumphashtable(aliastab, -2); - ipre = oi; - } else { - /* Normal file completion... */ - if (ispattern & 1) { - /* But with pattern matching. */ - LinkList l = newlinklist(); - LinkNode n; - int ng = opts[NULLGLOB]; - - opts[NULLGLOB] = 1; - - addwhat = 0; - p = (char *) zhalloc(lpl + lsl + 3); - strcpy(p, lpre); - if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*') - strcat(p, "*"); - strcat(p, lsuf); - if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')') - strcat(p, "*"); - - /* Do the globbing. */ - tokenize(p); - remnulargs(p); - addlinknode(l, p); - globlist(l); - - if (nonempty(l)) { - /* And add the resulting words. */ - mflags |= CMF_FILE; - for (n = firstnode(l); n; incnode(n)) - addmatch(getdata(n), NULL); - mflags &= !CMF_FILE; - } - opts[NULLGLOB] = ng; - } else { - char **dirs = 0, *ta[2]; - - /* No pattern matching. */ - addwhat = CC_FILES; - - if (cc->withd) { - char **pp, **npp, *tp; - int tl = strlen(ppre) + 2, pl; - - if ((pp = get_user_var(cc->withd))) { - dirs = npp = - (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1)); - while (*pp) { - pl = strlen(*pp); - tp = (char *) zhalloc(strlen(*pp) + tl); - strcpy(tp, *pp); - tp[pl] = '/'; - strcpy(tp + pl + 1, ppre); - *npp++ = tp; - pp++; - } - *npp = '\0'; - } - } - if (!dirs) { - dirs = ta; - if (cc->withd) { - char *tp; - int pl = strlen(cc->withd); - - ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2); - strcpy(tp, cc->withd); - tp[pl] = '/'; - strcpy(tp + pl + 1, ppre); - } else - ta[0] = ppre; - ta[1] = NULL; - } - while (*dirs) { - prpre = *dirs; - - if (sf2) - /* We are in the path, so add only directories. */ - gen_matches_files(1, 0, 0); - else { - if (cc->mask & CC_FILES) - /* Add all files. */ - gen_matches_files(0, 0, 1); - else if (cc->mask & CC_COMMPATH) { - /* Completion of command paths. */ - if (sf1 || cc->withd) - /* There is a path prefix, so add * - * directories and executables. */ - gen_matches_files(1, 1, 0); - else { - /* No path prefix, so add the things * - * reachable via the PATH variable. */ - char **pc = path, *pp = prpre; - - for (; *pc; pc++) - if (!**pc || (pc[0][0] == '.' && !pc[0][1])) - break; - if (*pc) { - prpre = "./"; - gen_matches_files(1, 1, 0); - prpre = pp; - } - } - } else if (cc->mask & CC_DIRS) - gen_matches_files(1, 0, 0); - /* The compctl has a glob pattern (compctl -g). */ - if (cc->glob) { - int ns, pl = strlen(prpre), o, paalloc; - char *g = dupstring(cc->glob), *pa; - char *p2, *p3; - int ne = noerrs, md = opts[MARKDIRS]; - - /* These are used in the globbing code to make * - * things a bit faster. */ - if (ispattern || mstack) - glob_pre = glob_suf = NULL; - else { - glob_pre = fpre; - glob_suf = fsuf; - } - noerrs = 1; - addwhat = -6; - o = strlen(prpre); - pa = (char *)zalloc(paalloc = o + PATH_MAX); - strcpy(pa, prpre); - opts[MARKDIRS] = 0; - - /* The compctl -g string may contain more than * - * one pattern, so we need a loop. */ - while (*g) { - LinkList l = newlinklist(); - int ng; - - /* Find the blank terminating the pattern. */ - while (*g && inblank(*g)) - g++; - /* Oops, we already reached the end of the - string. */ - if (!*g) - break; - for (p = g + 1; *p && !inblank(*p); p++) - if (*p == '\\' && p[1]) - p++; - /* Get the pattern string. */ - tokenize(g = dupstrpfx(g, p - g)); - if (*g == '=') - *g = Equals; - if (*g == '~') - *g = Tilde; - remnulargs(g); - if ((*g == Equals || *g == Tilde) && !cc->withd) { - /* The pattern has a `~' or `=' at the * - * beginning, so we expand this and use * - * the result. */ - filesub(&g, 0); - addlinknode(l, dupstring(g)); - } else if (*g == '/' && !cc->withd) - /* The pattern is a full path (starting * - * with '/'), so add it unchanged. */ - addlinknode(l, dupstring(g)); - else { - /* It's a simple pattern, so append it to * - * the path we have on the command line. */ - int minlen = o + strlen(g); - if (minlen >= paalloc) - pa = (char *) - zrealloc(pa, paalloc = minlen+1); - strcpy(pa + o, g); - addlinknode(l, dupstring(pa)); - } - /* Do the globbing. */ - ng = opts[NULLGLOB]; - opts[NULLGLOB] = 1; - globlist(l); - opts[NULLGLOB] = ng; - /* Get the results. */ - if (nonempty(l) && peekfirst(l)) { - for (p2 = (char *)peekfirst(l); *p2; p2++) - if (itok(*p2)) - break; - if (!*p2) { - if ((*g == Equals || *g == Tilde || - *g == '/') || cc->withd) { - /* IF the pattern started with `~', * - * `=', or `/', add the result only, * - * if it really matches what we have * - * on the line. * - * Do this if an initial directory * - * was specified, too. */ - while ((p2 = (char *)ugetnode(l))) - if (strpfx(prpre, p2)) - addmatch(p2 + pl, NULL); - } else { - /* Otherwise ignore the path we * - * prepended to the pattern. */ - while ((p2 = p3 = - (char *)ugetnode(l))) { - for (ns = sf1; *p3 && ns; p3++) - if (*p3 == '/') - ns--; - - addmatch(p3, NULL); - } - } - } - } - pa[o] = '\0'; - g = p; - } - glob_pre = glob_suf = NULL; - noerrs = ne; - opts[MARKDIRS] = md; - - zfree(pa, paalloc); - } - } - dirs++; - } - prpre = NULL; - } - } - lppre = lpsuf = NULL; - lppl = lpsl = 0; - } - if (ic) { - /* Now change the `~' and `=' tokens to the real characters so * - * that things starting with these characters will be added. */ - rpre = dyncat((ic == Tilde) ? "~" : "=", rpre); - rpl++; - qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre); - } - if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) { - /* If we have to complete commands, add alias names, * - * shell functions and builtins too. */ - dumphashtable(aliastab, -3); - dumphashtable(reswdtab, -3); - dumphashtable(shfunctab, -3); - dumphashtable(builtintab, -3); - if (isset(HASHLISTALL)) - cmdnamtab->filltable(cmdnamtab); - dumphashtable(cmdnamtab, -3); - /* And parameter names if autocd and cdablevars are set. */ - if (isset(AUTOCD) && isset(CDABLEVARS)) - dumphashtable(paramtab, -4); - } - oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG; - - if (cc->mask & CC_NAMED) - /* Add named directories. */ - dumphashtable(nameddirtab, addwhat); - if (cc->mask & CC_OPTIONS) - /* Add option names. */ - dumphashtable(optiontab, addwhat); - if (cc->mask & CC_VARS) { - /* And parameter names. */ - dumphashtable(paramtab, -9); - addwhat = oaw; - } - if (cc->mask & CC_BINDINGS) { - /* And zle function names... */ - dumphashtable(thingytab, CC_BINDINGS); - addwhat = oaw; - } - if (cc->keyvar) { - /* This adds things given to the compctl -k flag * - * (from a parameter or a list of words). */ - char **usr = get_user_var(cc->keyvar); - - if (usr) - while (*usr) - addmatch(*usr++, NULL); - } - if (cc->mask & CC_USERS) { - /* Add user names. */ - maketildelist(); - addwhat = oaw; - } - if (cc->widget) { - char **ocfa = cfargs; - int ocfr = cfret; - - cfargs = zlenoargs; - callcompfunc(os, cc->widget); - cfargs = ocfa; - cfret = ocfr; - } - if (cc->func) { - /* This handles the compctl -K flag. */ - List list; - char **r; - int lv = lastval; - - /* Get the function. */ - if ((list = getshfunc(cc->func)) != &dummy_list) { - /* We have it, so build a argument list. */ - LinkList args = newlinklist(); - int osc = sfcontext; - - addlinknode(args, cc->func); - - if (delit) { - p = dupstrpfx(os, ooffs); - untokenize(p); - addlinknode(args, p); - p = dupstring(os + ooffs); - untokenize(p); - addlinknode(args, p); - } else { - addlinknode(args, lpre); - addlinknode(args, lsuf); - } - - /* This flag allows us to use read -l and -c. */ - if (incompfunc != 1) - incompctlfunc = 1; - sfcontext = SFC_COMPLETE; - /* Call the function. */ - doshfunc(cc->func, list, args, 0, 1); - sfcontext = osc; - incompctlfunc = 0; - /* And get the result from the reply parameter. */ - if ((r = get_user_var("reply"))) - while (*r) - addmatch(*r++, NULL); - } - lastval = lv; - } - if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) { - /* Get job names. */ - int i; - char *j; - - for (i = 0; i < MAXJOB; i++) - if ((jobtab[i].stat & STAT_INUSE) && - jobtab[i].procs && jobtab[i].procs->text) { - int stopped = jobtab[i].stat & STAT_STOPPED; - - j = dupstring(jobtab[i].procs->text); - if ((cc->mask & CC_JOBS) || - (stopped && (cc->mask & CC_STOPPED)) || - (!stopped && (cc->mask & CC_RUNNING))) - addmatch(j, NULL); - } - } - if (cc->str) { - /* Get the stuff from a compctl -s. */ - LinkList foo = newlinklist(); - LinkNode n; - int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb; - char *tmpbuf; - - opts[NULLGLOB] = 1; - - /* Put the string in the lexer buffer and call the lexer to * - * get the words we have to expand. */ - zleparse = 1; - lexsave(); - tmpbuf = (char *)zhalloc(strlen(cc->str) + 5); - sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */ - inpush(tmpbuf, 0, NULL); - strinbeg(0); - noaliases = 1; - do { - ctxtlex(); - if (tok == ENDINPUT || tok == LEXERR) - break; - if (!first && tokstr && *tokstr) - addlinknode(foo, ztrdup(tokstr)); - first = 0; - } while (tok != ENDINPUT && tok != LEXERR); - noaliases = 0; - strinend(); - inpop(); - errflag = zleparse = 0; - lexrestore(); - /* Fine, now do full expansion. */ - prefork(foo, 0); - if (!errflag) { - globlist(foo); - if (!errflag) - /* And add the resulting words as matches. */ - for (n = firstnode(foo); n; incnode(n)) - addmatch((char *)n->dat, NULL); - } - opts[NULLGLOB] = ng; - we = oowe; - wb = oowb; - } - if (cc->hpat) { - /* We have a pattern to take things from the history. */ - Patprog pprogc = NULL; - char *e, *h, hpatsav; - int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum; - Histent he = quietgethistent(i, GETHIST_UPWARD); - - /* Parse the pattern, if it isn't the null string. */ - if (*(cc->hpat)) { - char *thpat = dupstring(cc->hpat); - - tokenize(thpat); - pprogc = patcompile(thpat, 0, NULL); - } - /* n holds the number of history line we have to search. */ - if (!n) - n = -1; - - /* Now search the history. */ - while (n-- && he) { - int iwords; - for (iwords = he->nwords - 1; iwords >= 0; iwords--) { - h = he->text + he->words[iwords*2]; - e = he->text + he->words[iwords*2+1]; - hpatsav = *e; - *e = '\0'; - /* We now have a word from the history, ignore it * - * if it begins with a quote or `$'. */ - if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' && - (!pprogc || pattry(pprogc, h))) - /* Otherwise add it if it was matched. */ - addmatch(dupstring(h), NULL); - if (hpatsav) - *e = hpatsav; - } - he = up_histent(he); - } - } - if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS | - CC_READONLYS | CC_SPECIALS | CC_PARAMS))) - /* Add various flavours of parameters. */ - dumphashtable(paramtab, t); - if ((t = cc->mask & CC_SHFUNCS)) - /* Add shell functions. */ - dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - if ((t = cc->mask & CC_BUILTINS)) - /* Add builtins. */ - dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - if ((t = cc->mask & CC_EXTCMDS)) { - /* Add external commands */ - if (isset(HASHLISTALL)) - cmdnamtab->filltable(cmdnamtab); - dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - } - if ((t = cc->mask & CC_RESWDS)) - /* Add reserved words */ - dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - if ((t = cc->mask & (CC_ALREG | CC_ALGLOB))) - /* Add the two types of aliases. */ - dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS))); - if (keypm && cc == &cc_dummy) { - /* Add the keys of the parameter in keypm. */ - scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0); - keypm = NULL; - cc_dummy.suffix = NULL; - } - if (!errflag && cc->ylist) { - /* generate the user-defined display list: if anything fails, * - * we silently allow the normal completion list to be used. */ - char **yaptr = NULL, *uv = NULL; - List list; - - if (cc->ylist[0] == '$' || cc->ylist[0] == '(') { - /* from variable */ - uv = cc->ylist + (cc->ylist[0] == '$'); - } else if ((list = getshfunc(cc->ylist)) != &dummy_list) { - /* from function: pass completions as arg list */ - LinkList args = newlinklist(); - LinkNode ln; - Cmatch m; - int osc = sfcontext; - - addlinknode(args, cc->ylist); - for (ln = firstnode(matches); ln; ln = nextnode(ln)) { - m = (Cmatch) getdata(ln); - if (m->ppre) { - char *p = (char *) zhalloc(strlen(m->ppre) + strlen(m->str) + - strlen(m->psuf) + 1); - - sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf); - addlinknode(args, dupstring(p)); - } else - addlinknode(args, dupstring(m->str)); - } - - /* No harm in allowing read -l and -c here, too */ - if (incompfunc != 1) - incompctlfunc = 1; - sfcontext = SFC_COMPLETE; - doshfunc(cc->ylist, list, args, 0, 1); - sfcontext = osc; - incompctlfunc = 0; - uv = "reply"; - } - if (uv) - yaptr = get_user_var(uv); - if ((tt = cc->explain)) { - tt = dupstring(tt); - if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { - singsub(&tt); - untokenize(tt); - } - expl->str = tt; - if (cc->gname) { - endcmgroup(yaptr); - begcmgroup(cc->gname, gflags); - addexpl(); - } else { - addexpl(); - endcmgroup(yaptr); - begcmgroup("default", 0); - } - } - } else if ((tt = cc->explain)) { - tt = dupstring(tt); - if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { - singsub(&tt); - untokenize(tt); - } - expl->str = tt; - addexpl(); - } - if (cc->subcmd) { - /* Handle -l sub-completion. */ - char **ow = clwords, *os = cmdstr, *ops = NULL; - int oldn = clwnum, oldp = clwpos, br; - unsigned long occ = ccont; - - ccont = CC_CCCONT; - - /* So we restrict the words-array. */ - if (brange >= clwnum) - brange = clwnum - 1; - if (brange < 1) - brange = 1; - if (erange >= clwnum) - erange = clwnum - 1; - if (erange < 1) - erange = 1; - clwnum = erange - brange + 1; - clwpos = clwpos - brange; - br = brange; - - if (cc->subcmd[0]) { - /* And probably put the command name given to the flag * - * in the array. */ - clwpos++; - clwnum++; - incmd = 0; - ops = clwords[br - 1]; - clwords[br - 1] = ztrdup(cc->subcmd); - cmdstr = ztrdup(cc->subcmd); - clwords += br - 1; - } else { - cmdstr = ztrdup(clwords[br]); - incmd = !clwpos; - clwords += br; - } - /* Produce the matches. */ - makecomplistcmd(s, incmd, CFN_FIRST); - - /* And restore the things we changed. */ - clwords = ow; - zsfree(cmdstr); - cmdstr = os; - clwnum = oldn; - clwpos = oldp; - if (ops) { - zsfree(clwords[br - 1]); - clwords[br - 1] = ops; - } - ccont = occ; - } - if (cc->substr) - sep_comp_string(cc->substr, s, offs, 1); - uremnode(ccstack, firstnode(ccstack)); - if (cc->matcher) - mstack = mstack->next; - - if (mn == mnum) - haspattern = ohp; -} - -/* Invalidate the completion list. */ - -/**/ -void -invalidatelist(void) +void +invalidatelist(void) { if (showinglist == -2) listmatches(); @@ -7812,14 +5748,14 @@ addexpl(void) for (n = firstnode(expls); n; incnode(n)) { e = (Cexpl) getdata(n); - if (!strcmp(expl->str, e->str)) { - e->count += expl->count; - e->fcount += expl->fcount; + if (!strcmp(curexpl->str, e->str)) { + e->count += curexpl->count; + e->fcount += curexpl->fcount; return; } } - addlinknode(expls, expl); + addlinknode(expls, curexpl); newmatches = 1; } @@ -7880,7 +5816,6 @@ permmatches(int last) Cmgroup g = amatches, n; Cmatch *p, *q; Cexpl *ep, *eq, e, o; - Compctl *cp, *cq; LinkList mlist; static int fi = 0; int nn, nl, ll, gn = 1, mn = 1, rn; @@ -7922,7 +5857,6 @@ permmatches(int last) NULL, NULL); g->ccount = 0; - g->ccs = NULL; } LASTALLOC; nmatches += g->mcount; @@ -7966,14 +5900,6 @@ permmatches(int last) } else n->expls = NULL; - if ((n->ccount = g->ccount)) { - n->ccs = cp = (Compctl *) ncalloc((n->ccount + 1) * - sizeof(Compctl)); - for (cq = g->ccs; *cq; cq++, cp++) - *cp = *cq; - *cp = NULL; - } else - n->ccs = NULL; n->widths = NULL; g = g->next; @@ -8080,7 +6006,6 @@ freematches(Cmgroup g) Cmgroup n; Cmatch *m; Cexpl *e; - Compctl *c; while (g) { n = g->next; @@ -8099,14 +6024,6 @@ freematches(Cmgroup g) } free(g->expls); } - if ((c = g->ccs)) { - while (*c) { - if (*c != &cc_dummy) - freecompctl(*c); - c++; - } - free(g->ccs); - } free(g); g = n; diff --git a/Src/Zle/zleparameter.c b/Src/Zle/zleparameter.c new file mode 100644 index 000000000..8a5bc0bc2 --- /dev/null +++ b/Src/Zle/zleparameter.c @@ -0,0 +1,257 @@ +/* + * zleparameter.c - parameter interface to zle internals + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1999 Sven Wischnowsky + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Sven Wischnowsky or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Sven Wischnowsky and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Sven Wischnowsky and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zleparameter.mdh" +#include "zleparameter.pro" + +/* Empty dummy function for special hash parameters. */ + +/**/ +static void +shempty(void) +{ +} + +/* Create a simple special hash parameter. */ + +/**/ +static Param +createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan) +{ + Param pm; + HashTable ht; + + if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_REMOVABLE|PM_HASHED))) + return NULL; + + pm->level = pm->old ? locallevel : 0; + pm->gets.hfn = hashgetfn; + pm->sets.hfn = hashsetfn; + pm->unsetfn = stdunsetfn; + pm->u.hash = ht = newhashtable(7, name, NULL); + + ht->hash = hasher; + ht->emptytable = (TableFunc) shempty; + ht->filltable = NULL; + ht->addnode = (AddNodeFunc) shempty; + ht->getnode = ht->getnode2 = get; + ht->removenode = (RemoveNodeFunc) shempty; + ht->disablenode = NULL; + ht->enablenode = NULL; + ht->freenode = (FreeNodeFunc) shempty; + ht->printnode = printparamnode; + ht->scantab = scan; + + return pm; +} + +/* Functions for the zlewidgets special parameter. */ + +/**/ +static char * +widgetstr(Widget w) +{ + if (w->flags & WIDGET_INT) + return dupstring("builtin"); + if (w->flags & WIDGET_NCOMP) { + char *t = (char *) zhalloc(13 + strlen(w->u.comp.wid) + + strlen(w->u.comp.func)); + + strcpy(t, "completion:"); + strcat(t, w->u.comp.wid); + strcat(t, ":"); + strcat(t, w->u.comp.func); + + return t; + } + return dyncat("user:", w->u.fnnam); +} + +/**/ +static HashNode +getpmwidgets(HashTable ht, char *name) +{ + Param pm = NULL; + Thingy th; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((th = (Thingy) thingytab->getnode(thingytab, name)) && + !(th->flags & DISABLED)) + pm->u.str = widgetstr(th->widget); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; +} + +/**/ +static void +scanpmwidgets(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i; + HashNode hn; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < thingytab->hsize; i++) + for (hn = thingytab->nodes[i]; hn; hn = hn->next) { + pm.nam = hn->nam; + if (func != scancountparams) + pm.u.str = widgetstr(((Thingy) hn)->widget); + func((HashNode) &pm, flags); + } +} + +/* Functions for the zlekeymaps special parameter. */ + +static char ** +keymapsgetfn(Param pm) +{ + int i; + HashNode hn; + char **ret, **p; + + p = ret = (char **) zhalloc((keymapnamtab->ct + 1) * sizeof(char *)); + + for (i = 0; i < keymapnamtab->hsize; i++) + for (hn = keymapnamtab->nodes[i]; hn; hn = hn->next) + *p++ = dupstring(hn->nam); + *p = NULL; + + return ret; +} + +/* Table for defined parameters. */ + +struct pardef { + char *name; + int flags; + GetNodeFunc getnfn; + ScanTabFunc scantfn; + void (*hsetfn) _((Param, HashTable)); + void (*setfn) _((Param, char **)); + char **(*getfn) _((Param)); + void (*unsetfn) _((Param, int)); + Param pm; +}; + +static struct pardef partab[] = { + { "zlewidgets", PM_READONLY, + getpmwidgets, scanpmwidgets, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "zlekeymaps", PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_READONLY, + NULL, NULL, NULL, + arrsetfn, keymapsgetfn, stdunsetfn, NULL }, + { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +/**/ +int +setup_zleparameter(Module m) +{ + return 0; +} + +/**/ +int +boot_zleparameter(Module m) +{ + struct pardef *def; + + for (def = partab; def->name; def++) { + unsetparam(def->name); + + if (def->getnfn) { + if (!(def->pm = createspecialhash(def->name, def->getnfn, + def->scantfn))) + return 1; + def->pm->flags |= def->flags; + if (def->hsetfn) + def->pm->sets.hfn = def->hsetfn; + } else { + if (!(def->pm = createparam(def->name, def->flags))) + return 1; + def->pm->sets.afn = def->setfn; + def->pm->gets.afn = def->getfn; + def->pm->unsetfn = def->unsetfn; + } + } + return 0; +} + +#ifdef MODULE + +/**/ +int +cleanup_zleparameter(Module m) +{ + Param pm; + struct pardef *def; + + for (def = partab; def->name; def++) { + if ((pm = (Param) paramtab->getnode(paramtab, def->name)) && + pm == def->pm) { + pm->flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 1); + } + } + return 0; +} + +/**/ +int +finish_zleparameter(Module m) +{ + return 0; +} + +#endif diff --git a/Src/Zle/zleparameter.mdd b/Src/Zle/zleparameter.mdd new file mode 100644 index 000000000..f9d528661 --- /dev/null +++ b/Src/Zle/zleparameter.mdd @@ -0,0 +1,5 @@ +moddeps="zle" + +autoparams="zlewidgets zlekeymaps" + +objects="zleparameter.o" diff --git a/Src/hashtable.c b/Src/hashtable.c index 86258e66f..d4c832f16 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -1119,6 +1119,17 @@ printaliasnode(HashNode hn, int printflags) /* Named Directory Hash Table Functions */ /****************************************/ +#ifdef HAVE_NIS_PLUS +# include +#else +# ifdef HAVE_NIS +# include +# include +# include +# include +# endif +#endif + /* hash table containing named directories */ /**/ @@ -1168,12 +1179,102 @@ emptynameddirtable(HashTable ht) /* Add all the usernames in the password file/database * * to the named directories table. */ +#ifdef HAVE_NIS_PLUS +static int +add_userdir(nis_name table, nis_object *object, void *userdata) +{ + if (object->zo_data.objdata_u.en_data.en_cols.en_cols >= 6) { + static char name[40], dir[PATH_MAX + 1]; + register entry_col *ec = + object->zo_data.objdata_u.en_data.en_cols.en_cols_val; + register int nl = minimum(ec[0].ec_value.ec_value_len, 39); + register int dl = minimum(ec[5].ec_value.ec_value_len, PATH_MAX); + + memcpy(name, ec[0].ec_value.ec_value_val, nl); + name[nl] = '\0'; + memcpy(dir, ec[5].ec_value.ec_value_val, dl); + dir[dl] = '\0'; + + adduserdir(name, dir, ND_USERNAME, 1); + } + return 0; +} +#else +# ifdef HAVE_NIS +static int +add_userdir(int status, char *key, int keylen, char *val, int vallen, char *dummy) +{ + char *p, *d, *de; + + if (status != YP_TRUE) + return 1; + + if (vallen > keylen && *(p = val + keylen) == ':') { + *p++ = '\0'; + if ((de = strrchr(p, ':'))) { + *de = '\0'; + if ((d = strrchr(p, ':'))) { + if (*++d && val[0]) + adduserdir(val, d, ND_USERNAME, 1); + } + } + } + return 0; +} +# endif /* HAVE_NIS */ +#endif /* HAVE_NIS_PLUS */ + /**/ static void fillnameddirtable(HashTable ht) { -#ifdef HAVE_GETPWENT if (!allusersadded) { +#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS) + FILE *pwf; + char buf[BUFSIZ], *p, *d, *de; + int skipping; + +# ifndef HAVE_NIS_PLUS + char domain[YPMAXDOMAIN]; + struct ypall_callback cb; + + /* Get potential matches from NIS and cull those without local accounts */ + if (getdomainname(domain, YPMAXDOMAIN) == 0) { + cb.foreach = (int (*)()) add_userdir; + cb.data = NULL; + yp_all(domain, PASSWD_MAP, &cb); + } +# else /* HAVE_NIS_PLUS */ + /* Maybe we should turn this string into a #define'd constant...? */ + + nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH, + add_userdir, 0); +# endif + /* Don't forget the non-NIS matches from the flat passwd file */ + if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) { + skipping = 0; + while (fgets(buf, BUFSIZ, pwf) != NULL) { + if (strchr(buf, '\n') != NULL) { + if (!skipping) { + if ((p = strchr(buf, ':')) != NULL) { + *p++ = '\0'; + if ((de = strrchr(p, ':'))) { + *de = '\0'; + if ((d = strrchr(p, ':'))) { + if (*++d && buf[0]) + adduserdir(buf, d, ND_USERNAME, 1); + } + } + } + } else + skipping = 0; + } else + skipping = 1; + } + fclose(pwf); + } +#else /* no NIS or NIS_PLUS */ +#ifdef HAVE_GETPWENT struct passwd *pw; setpwent(); @@ -1181,13 +1282,13 @@ fillnameddirtable(HashTable ht) /* loop through the password file/database * * and add all entries returned. */ while ((pw = getpwent()) && !errflag) - adduserdir(ztrdup(pw->pw_name), pw->pw_dir, ND_USERNAME, 1); + adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1); endpwent(); +#endif /* HAVE_GETPWENT */ +#endif allusersadded = 1; } - return; -#endif /* HAVE_GETPWENT */ } /* Add an entry to the named directory hash * diff --git a/Src/module.c b/Src/module.c index 9922f84df..e620073ce 100644 --- a/Src/module.c +++ b/Src/module.c @@ -65,6 +65,21 @@ register_module(char *n) } LASTALLOC; } +/* Check if a module is linked in. */ + +/**/ +int +module_linked(char *name) +{ + LinkNode node; + + for (node = firstnode(bltinmodules); node; incnode(node)) + if (!strcmp((char *) getdata(node), name)) + return 1; + + return 0; +} + /* addbuiltin() can be used to add a new builtin. It returns zero on * * success, 1 on failure. The only possible type of failure is that * * a builtin with the specified name already exists. An autoloaded * @@ -1401,7 +1416,7 @@ addhookdefs(char const *nam, Hookdef h, int size) while (size--) { if (addhookdef(h)) { - zwarnnam(nam, "name clash when adding condition `%s'", h->name, 0); + zwarnnam(nam, "name clash when adding hook `%s'", h->name, 0); hadf = 1; } else hads = 2; diff --git a/Src/params.c b/Src/params.c index 64aa7c865..4c4f89978 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2012,7 +2012,7 @@ strsetfn(Param pm, char *x) /* Function to get value of an array parameter */ /**/ -static char ** +char ** arrgetfn(Param pm) { static char *nullarray = NULL; @@ -2023,7 +2023,7 @@ arrgetfn(Param pm) /* Function to set value of an array parameter */ /**/ -static void +void arrsetfn(Param pm, char **x) { if (pm->u.arr && pm->u.arr != x) diff --git a/Src/subst.c b/Src/subst.c index ffe6217f0..00b22da28 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1076,6 +1076,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) val = dyncat(val, "-export"); if (f & PM_UNIQUE) val = dyncat(val, "-unique"); + if (f & PM_HIDE) + val = dyncat(val, "-hide"); + if (f & PM_SPECIAL) + val = dyncat(val, "-special"); vunset = 0; } else val = dupstring(""); diff --git a/Src/xmods.conf b/Src/xmods.conf index 7e5e2928a..def807911 100644 --- a/Src/xmods.conf +++ b/Src/xmods.conf @@ -1,6 +1,6 @@ rlimits -comp1 zle +complete compctl sched complist -- cgit 1.4.1