From 20d67907c95265356b51dbdce8ecc0c1ede9e66b Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Thu, 15 Apr 1999 18:11:42 +0000 Subject: zsh-3.1.5-pws-5 --- Config/version.mk | 4 +- Doc/Zsh/builtins.yo | 7 +- Doc/Zsh/compctl.yo | 24 +- Doc/Zsh/mod_zftp.yo | 21 ++ Etc/MACHINES | 6 +- Misc/.distfiles | 1 + Misc/new-completion-examples | 453 +++++++++++++++++++++++++++++++++ Src/Builtins/rlimits.c | 14 +- Src/Modules/.distfiles | 1 + Src/Modules/example.mdd | 3 + Src/Modules/files.c | 6 +- Src/Modules/stat.c | 11 +- Src/Modules/zftp.c | 142 +++++++++-- Src/Zle/comp1.c | 44 +++- Src/Zle/comp1.export | 13 + Src/Zle/compctl.c | 582 ++++++++++++++++++++++++++++++++++++++++--- Src/Zle/compctl.mdd | 4 +- Src/Zle/iwidgets.list | 16 +- Src/Zle/zle.h | 18 +- Src/Zle/zle_keymap.c | 4 +- Src/Zle/zle_main.c | 28 ++- Src/Zle/zle_refresh.c | 105 ++++---- Src/Zle/zle_thingy.c | 50 +++- Src/Zle/zle_tricky.c | 448 +++++++++++++++++++++++++++++---- Src/builtin.c | 296 ++++++++++++---------- Src/cond.c | 6 +- Src/exec.c | 18 +- Src/glob.c | 4 +- Src/hashtable.c | 6 +- Src/hist.c | 6 +- Src/input.c | 3 +- Src/jobs.c | 17 +- Src/lex.c | 25 +- Src/loop.c | 2 +- Src/mem.c | 54 ++-- Src/mkbltnmlst.sh | 8 +- Src/mkmakemod.sh | 23 +- Src/module.c | 2 - Src/params.c | 12 +- Src/subst.c | 5 +- Src/watch.c | 3 +- Src/zsh.export | 9 + Src/zsh.h | 34 +++ Util/zsh-development-guide | 21 +- aczsh.m4 | 2 +- configure.in | 19 +- patchlist.txt | 53 ++++ 47 files changed, 2210 insertions(+), 423 deletions(-) create mode 100644 Misc/new-completion-examples diff --git a/Config/version.mk b/Config/version.mk index 309c19772..05e8483fd 100644 --- a/Config/version.mk +++ b/Config/version.mk @@ -27,5 +27,5 @@ # This must also serve as a shell script, so do not add spaces around the # `=' signs. -VERSION=3.1.5-pws-4 -VERSION_DATE='December 17, 1998' +VERSION=3.1.5-pws-5 +VERSION_DATE='January 19, 1998' diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 2df70cd98..82562fcbe 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -947,9 +947,10 @@ otherwise it is determined by the width of the value of the first assignment. ) item(tt(-a))( -The names refer to array parameters. For historical reasons, scalar -parameters are created even when this flag is specified, but the -output is restricted to arrays (including associative arrays). +The names refer to array parameters. An array parameter may be +created this way, but it may not be assigned to in the tt(typeset) +statement. When displaying, both normal and associative arrays are +shown. ) item(tt(-f))( The names refer to functions rather than parameters. No assignments diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo index 37296b716..e9634a26f 100644 --- a/Doc/Zsh/compctl.yo +++ b/Doc/Zsh/compctl.yo @@ -85,7 +85,7 @@ the standard behavior for all commands. For example, if your access to the user database is too slow and/or it contains too many users (so that completion after `tt(~)' is too slow to be usable), you can use -nofill(tt(compctl -Tx 'C[0,*/*]' -f - 's[~]' -k friends -S/)) +nofill(tt(compctl -Tx 'C[0,*/*]' -f - 's[~]' -k friends -S/ -tn)) to complete the strings in the array tt(friends) after a `tt(~)'. The first argument is necessary so that this form of ~-completion is @@ -432,16 +432,18 @@ the sorted ones. I.e. it is possible to have a sorted and a unsorted group with the same name and the matches in those groups will not be mixed. ) item(tt(-t) var(continue))( -The var(continue)-string contains a set of characters that specify if -and when completion should continue to produce matches where it normally -would not do that. The character tt(c) means that completion continues -with the next suitable compctl (i.e. if you don't specify this in a -tt(compctl -T), compctls for commands are never used). The character -tt(PLUS()) is used to continue with the matches for the next alternative -completion (see below). The characters tt(-) and tt(x) may be used in -sub-lists for extended completion (see below). They will make the completion -code use the flag list after the next tt(-) (if the corresponding pattern -matches) and the default flag list (those before the tt(-x)), respectively. +The var(continue)-string contains a character that specifies which set +of completion flags should be used next. Normally those of the next +matching compctl are used, i.e. pattern compctls and normal compctls +after tt(-T) and after a pattern compctl. If var(continue) is the +character tt(PLUS()) the flags for the next alternative completion +(see below) are used. The characters tt(-) and tt(x) can be used in +sub-lists for extended completion (see below). They will make the +completion code use the flag list after the next tt(-) (if the +corresponding pattern matches) and the default flag list (those before +the tt(-x)), respectively. if var(continue) is the character tt(n) no +other flag lists are used, i.e. the generation of matches stops +immediately. ) item(tt(-M) var(match-spec))( This defines additional matching control specifications that should be used diff --git a/Doc/Zsh/mod_zftp.yo b/Doc/Zsh/mod_zftp.yo index a15be60d6..c08130bf5 100644 --- a/Doc/Zsh/mod_zftp.yo +++ b/Doc/Zsh/mod_zftp.yo @@ -92,6 +92,27 @@ nofill(tt(LPAR()zftp open; zftp get foo >bar; zftp close)tt(RPAR() &)) --- here, the connection is restricted to a background subshell and you are free to open a simultaneous connection in the foreground. ) +item(tt(test))( +Test the connection; if the server has reported +that it has closed the connection (maybe due to a timeout), return +status 2; if no connection was open anyway, return status 1; else +return status 0. The tt(test) subcommand is +silent, apart from messages printed by the tt($ZFTP_VERBOSE) +mechanism, or error messages if the connection closes. There is no +network overhead for this test. + +The test is only supported on systems with either the tt(select(2)) or +tt(poll(2)) system calls; otherwise the message tt(not +supported on this system) is printed instead. + +It is useful to put the code + +nofill(tt([[ -n $ZFTP_HOST ]] && zftp test)) + +into the shell function tt(precmd) for testing the connection before +every prompt. However, tt(zftp) will call tt(test) at the start of any +other subcommand when a connection is open. +) item(tt(cd) var(directory))( Change the remote directory to var(directory). Also alters the shell variable tt(ZFTP_PWD). diff --git a/Etc/MACHINES b/Etc/MACHINES index 303508a93..7002f6db4 100644 --- a/Etc/MACHINES +++ b/Etc/MACHINES @@ -45,7 +45,8 @@ HP: HP-UX 9, 10.20 Should build `out-of-the-box'. IBM: AIX - Should build `out-of-the-box'. + Should build `out-of-the-box'. On AIX 3.x (at least), + --enable-zsh-mem will not work. Linux: Linux (i386) [3.1.4] If you are using an early minor version of libc 5, then a bug @@ -92,3 +93,6 @@ Sun: Solaris 2.* To avoid this, make sure you compile zsh without any reference to /usr/ucblib in your LD_LIBRARY_PATH. You can easily do this by just unsetting LD_LIBRARY_PATH before building zsh. + + Under Solaris 2.7, dynamically loaded library support with + --enable-dynamic currently does not work. diff --git a/Misc/.distfiles b/Misc/.distfiles index a02614511..fab78c0d1 100644 --- a/Misc/.distfiles +++ b/Misc/.distfiles @@ -1,4 +1,5 @@ DISTFILES_SRC=' .distfiles c2z compctl-examples globtests globtests.ksh lete2ctl + new-completion-examples zftp-functions ' diff --git a/Misc/new-completion-examples b/Misc/new-completion-examples new file mode 100644 index 000000000..659679891 --- /dev/null +++ b/Misc/new-completion-examples @@ -0,0 +1,453 @@ +# Define a new widget behaving like `expand-or-complete' but calling the +# function `main-complete' to generate matches. + +zle -c my-comp expand-or-complete main-complete + +bindkey '\C-i' my-comp + + +# Below is a proposed main loop and the things it needs. + +# One associative array for normal completions and one for patterns. + +typeset -A comps + + +# These may be used to define completion handlers. First argument is the +# name of the function/variable containing the definition, the other +# arguments are the command names for which this definition should be used. +# With only one argument the function/variable-name __$1 is used. + +defcomp() { + local v + + if [[ $# -eq 1 ]] then + comps[$1]="__$1" + else + v="$1" + shift + for i; do + comps[$i]="$v" + done + fi +} + +defpatcomp() { + if [[ ${+patcomps} == 1 ]] then + patcomps=("$patcomps[@]" "$2 $1" ) + else + patcomps=( "$2 $1" ) + fi +} + + +# These can be used to easily save and restore the state of the special +# variables used by the completion code. + +alias compsave='local _opre _oipre _oargs _ocur;_opre="$PREFIX";_oipre="$IPREFIX";_oargs=( "$@" );_ocur="$CURRENT"' +alias compreset='PREFIX="$_opre";IPREFIX="$_oipre";argv=( "$_oargs[@]" );CURRENT="$_ocur"' + +# This is an easy way to get completion for sub-commands. + +alias compsub='do-complete "$@" || return 1' + +# This searches $1 in the array for normal completions and calls the result. + +compalso() { + 1="$comps[$1]" + [[ -z "$1" ]] || call-complete "$@" +} + +# This generates matches. The first argument is something we got from one +# of the associative arrays above. This is expected to be either the name +# of a variable in which case we use its value as arguments to complist, +# or it is the name of a function in which case we call this function with +# the arguments from the command line as its arguments. + +call-complete() { + local var + + eval var\=\$\{\+$1\} + if [[ "$var" == 0 ]] then + "$@" + else + eval complist \$\{${1}\[\@\]\} + fi +} + +# The main loop of the competion code. This is what is called when TAB is +# pressed. The completion code gives us the special variables and the +# arguments from the command line are gives as positional parameters. + +main-complete() { + emulate -R zsh + local comp + setopt localoptions nullglob rcexpandparam globdots + unsetopt markdirs globsubst shwordsplit nounset + + # An entry for `--first--' is the replacement for `compctl -T' + # The `|| return 1' is used throughout: if a function producing matches + # returns non-zero this is interpreted as `do not try to produce more matches' + # (this is the replacement for `compctl -t'). + + comp="$comps[--first--]" + [[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1 + + # For arguments we use the `do-complete' function below called via the + # convenience alias `compsub'. + + if [[ $CONTEXT == argument || $CONTEXT == command ]] then + compsub + else + # Let's see if we have a special completion definition for the other + # possible contexts. + + comp='' + + case $CONTEXT in + redirect) comp="$comps[--redir--]";; + math) comp="$comps[--math--]";; + subscript) comp="$comps[--subscr--]";; + value) comp="$comps[--value--]";; + condition) comp="$comps[--cond--]";; + esac + + # If not, we use default completion, if any. + + [[ -z "$comp" ]] && comp="$comps[--default--]" + [[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1 + fi +} + +# This does completion for a command (in command position and for the +# arguments). + +do-complete() { + local comp cmd1 cmd2 pat val + + # Completing in command position? If not we set up `cmd1' and `cmd2' as + # two strings we have search in the completion definition arrays (e.g. + # a path and the last path name component). + + if [[ $CONTEXT == command ]] then + comp="$comps[--command--]" + [[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1 + return 0 + elif [[ "$COMMAND[1]" == '=' ]] then + eval cmd1\=$COMMAND + cmd2="$COMMAND[2,-1]" + elif [[ "$COMMAND" == */* ]] then + cmd1="$COMMAND" + cmd2="${COMMAND:t}" + else + cmd1="$COMMAND" + eval cmd2=$(whence -p $COMMAND) + fi + + # See if there are any matching pattern completions. + + for i in "$patcomps[@]"; do + pat="${i% *}" + val="${i#* }" + if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]] then + call-complete "$val" "$@" || return 1 + fi + done + + # Now look up the two names in the normal completion array. + + comp="${comps[$cmd1]:-$comps[$cmd2]}" + + # And generate the matches, probably using default completion. + + [[ -z "$comp" ]] && comp="$comps[--default--]" + [[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1 +} + +# Do sub-completion for pre-command modifiers. + +defcomp __precmd - noglob nocorrect exec command builtin +__precmd() { + COMMAND="$1" + shift + (( CURRENT-- )) + if [[ CURRENT -eq 0 ]] then + CONTEXT=command + else + CONTEXT=argument + fi + compsub +} + +# Utility function for in-path completion. +# First argument should be an complist-option (e.g. -f, -/, -g). The other +# arguments should be glob patterns, one per argument. +# E.g.: files -g '*.tex' '*.texi' +# This is intended as a replacement for `complist -f', `complist -/', and +# `complist -g ...' (but don't use it with other options). +# This function behaves as if you have a matcher definition like: +# compctl -M 'r:|[-.,_/]=* r:|=* m:{a-z}={A-Z} m:-=_ m:.=,' +# so you may want to modify this. + +pfiles() { + local nm str pa pre epre a b c s rest + + setopt localoptions nullglob rcexpandparam globdots extendedglob + unsetopt markdirs globsubst shwordsplit nounset + + nm=$NMATCHES + if [[ $# -eq 0 ]] then + complist -f + elif [[ "$1" = -g ]] then + complist -g "$argv[2,-1]" + shift + else + complist $1 + shift + fi + [[ -nmatches nm ]] || return + + str="$PREFIX*$SUFFIX" + + [[ -z "$1" ]] && 1='*' + if [[ $str[1] = \~ ]] then + pre="${str%%/*}/" + eval epre\=$pre + str="${str#*/}" + pa='' + else + pre='' + epre='' + if [[ $str[1] = / ]] then + str="$str[2,-1]" + pa='/' + else + pa='' + fi + fi + str="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*." + while [[ "$str" = */* ]] do + rest="${str#*/}" + a="${epre}${pa}(#l)${str%%/*}(-/)" + a=( $~a ) + if [[ $#a -eq 0 ]] then + return + elif [[ $#a -gt 1 ]] then + c=() + s=( $rest$@ ) + s=( "${(@)s:gs.**.*.}" ) + for i in $a; do + b=( $~i/(#l)$~s ) + eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \) + [[ $#b -ne 0 ]] && c=( $c $i ) + done + if [[ $#c -eq 0 ]] then + return + elif [[ $#c -ne 1 ]] then + a="$epre$pa" + c=( $~c/(#l)$~s ) + eval c\=\( \$\{c:/\*\(${(j:|:)fignore}\)\} \) + c=( ${c#$a} ) + for i in $c; do + compadd -p "$pre$pa" -W "$a" -s "/${i#*/}" -f "${i%%/*}" + done + return + fi + a=( "$c[1]" ) + fi + a="$a[1]" + pa="$pa${a##*/}/" + str="$rest" + done + a="$epre$pa" + s=( $str$@ ) + s=( "${(@)s:gs.**.*.}" ) + b=( $~a(#l)$~s ) + eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \) + compadd -p "$pre$pa" -W "$epre$pa" -f ${b#$a} +} + +# Utility function for completing files of a given type or any file. +# In many cases you will want to call this one instead of pfiles(). + +files() { + local nm + + nm=$NMATCHES + pfiles "$@" + + [[ $# -ne 0 && -nmatches nm ]] && pfiles +} + +# Simple default, command, and math completion defined with variables. + +defcomp __default --default-- +__default() { + files +} + +defcomp __command --command-- +__command=( -c ) + +defcomp __math --math-- +__math=( -v ) + +defcomp __subscr --subscr-- +__subscr() { + compalso --math-- "$@" + # ...probably other stuff +} + +# A simple pattern completion, just as an example. + +defpatcomp __x_options '*/X11/*' +__x_options() { + complist -J options -k '(-display -name -xrm)' +} + +# A better example: completion for `find'. + +defcomp find +__find() { + compsave + + if [[ -mbetween -(ok|exec) \\\; ]] then + compsub + elif [[ -iprefix - ]] then + complist -s 'daystart {max,min,}depth follow noleaf version xdev \ + {a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \ + {i,}{l,}name {no,}{user,group} path perm regex size true uid used \ + exec {f,}print{f,0,} ok prune ls' + compreset + elif [[ -position 1 ]] then + complist -g '. ..' + files -g '(-/)' + elif [[ -mcurrent -1 -((a|c|)newer|fprint(|0|f)) ]] then + files + elif [[ -current -1 -fstype ]] then + complist -k '(ufs 4.2 4.3 nfs tmp mfs S51K S52K)' + elif [[ -current -1 -group ]] then + complist -k groups + elif [[ -current -1 -user ]] then + complist -u + fi +} + +# Various completions... + +defcomp __gunzip gunzip zcat +__gunzip() { + files -g '*.[gG][z]' +} + +defcomp gzip +__gzip() { + files -g '*~*.[gG][zZ]' +} + +defcomp xfig +__xfig() { + files -g '*.fig' +} + +defcomp __make make gmake +__make() { + complist -s "\$(awk '/^[a-zA-Z0-9][^/ ]+:/ {print \$1}' FS=: [mM]akefile)" +} + +defcomp __ps gs ghostview gview psnup psselect pswrap pstops pstruct lpr +__ps() { + files -g '*([pP][sS]|eps)' +} + +defcomp __which which whence +__which=( -caF ) + +defcomp __rlogin rlogin rsh ssh +__rlogin() { + if [[ -position 1 ]] then + complist -k hosts + elif [[ -position 2 ]] then + complist -k '(-l)' + elif [[ -position 3 && -word 1 artus ]] then + complist -k '(puck root)' + fi +} + +defcomp __dvi xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype +__dvi() { + files -g '*.(dvi|DVI)' +} + +defcomp __dirs rmdir df du dircmp cd +__dirs() { + files -/ '*(-/)' +} + +defcomp __jobs fg bg jobs +__jobs=(-j -P '%?') + +defcomp kill +__kill() { + if [[ -iprefix '-' ]] then + complist -k signals + else + complist -P '%?' -j + fi +} + +defcomp __uncompress uncompress zmore +__uncompress() { + files -g '*.Z' +} + +defcomp compress +__compress() { + files -g '*~*.Z' +} + +defcomp __tex tex latex glatex slitex gslitex +__tex() { + files -g '*.(tex|TEX|texinfo|texi)' +} + +defcomp __options setopt unsetopt +__options=(-M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o) + +defcomp __funcs unfunction +__funcs=(-F) + +defcomp __aliases unalias +__aliases=(-a) + +defcomp __vars unset +__vars=(-v) + +defcomp __enabled disable +__enabled=(-FBwa) + +defcomp __disabled enable +__disabled=(-dFBwa) + +defcomp __pdf acroread +__pdf() { + files -g '*.(pdf|PDF)' +} + +defcomp tar +__tar() { + local nm tf + compsave + + tf="$2" + nm=$NMATCHES + if [[ ( -mword 1 *t*f* || -mword 1 *x*f* ) && -position 3 100000 ]] then + complist -k "( $(tar tf $tf) )" + compreset + elif [[ -mword 1 *c*f* && -position 3 100000 ]] then + files + compreset + elif [[ -mcurrent -1 *f* && -position 2 ]] then + files -g '*.(tar|TAR)' + fi +} diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index 31dcb71b1..2af8dc6ac 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -264,7 +264,7 @@ bin_limit(char *nam, char **argv, char *ops, int func) * together more than one of these. It's easier to understand from * * the code: */ val = zstrtorlimt(s, &s, 10); - if (*s) + if (*s) { if ((*s == 'h' || *s == 'H') && !s[1]) val *= 3600L; else if ((*s == 'm' || *s == 'M') && !s[1]) @@ -275,6 +275,7 @@ bin_limit(char *nam, char **argv, char *ops, int func) zwarnnam("limit", "unknown scaling factor: %s", s, 0); return 1; } + } } # ifdef RLIMIT_NPROC else if (lim == RLIMIT_NPROC) @@ -339,12 +340,12 @@ bin_unlimit(char *nam, char **argv, char *ops, int func) /* Without arguments, remove all limits. */ if (!*argv) { for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) { - if (hard) + if (hard) { if (euid && current_limits[limnum].rlim_max != RLIM_INFINITY) ret++; else limits[limnum].rlim_max = RLIM_INFINITY; - else + } else limits[limnum].rlim_cur = limits[limnum].rlim_max; } if (ops['s']) @@ -373,13 +374,13 @@ bin_unlimit(char *nam, char **argv, char *ops, int func) return 1; } /* remove specified limit */ - if (hard) + if (hard) { if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) { zwarnnam(nam, "can't remove hard limits", NULL, 0); ret++; } else limits[lim].rlim_max = RLIM_INFINITY; - else + } else limits[lim].rlim_cur = limits[lim].rlim_max; if (ops['s'] && zsetlimit(lim, nam)) ret++; @@ -478,11 +479,12 @@ bin_ulimit(char *name, char **argv, char *ops, int func) } } if (!*argv || **argv == '-') { - if (res < 0) + if (res < 0) { if (*argv || nres) continue; else res = RLIMIT_FSIZE; + } resmask |= 1 << res; nres++; continue; diff --git a/Src/Modules/.distfiles b/Src/Modules/.distfiles index 4c98f97ea..23c7eae3f 100644 --- a/Src/Modules/.distfiles +++ b/Src/Modules/.distfiles @@ -5,4 +5,5 @@ DISTFILES_SRC=' example.mdd example.c files.mdd files.c stat.mdd stat.c + zftp.mdd zftp.c ' diff --git a/Src/Modules/example.mdd b/Src/Modules/example.mdd index 89f12097c..f2cd50693 100644 --- a/Src/Modules/example.mdd +++ b/Src/Modules/example.mdd @@ -1,3 +1,6 @@ autobins="example" +autoinfixconds="ex" +autoprefixconds="len" + objects="example.o" diff --git a/Src/Modules/files.c b/Src/Modules/files.c index f52c54338..c1948e1fd 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -195,18 +195,18 @@ bin_ln(char *nam, char **args, char *ops, int func) if(func == BIN_MV) { - move = rename; + move = (MoveFunc) rename; flags = ops['f'] ? 0 : MV_ASKNW; flags |= MV_ATOMIC; } else { flags = ops['f'] ? MV_FORCE : 0; #ifdef HAVE_LSTAT if(ops['s']) - move = symlink; + move = (MoveFunc) symlink; else #endif { - move = link; + move = (MoveFunc) link; if(!ops['d']) flags |= MV_NODIRS; } diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index 5c56be5c6..6f80e2a96 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -346,7 +346,7 @@ bin_stat(char *name, char **args, char *ops, int func) } else { for (; *arg; arg++) { if (strchr("glLnNrstT", *arg)) - ops[*arg] = 1; + ops[STOUC(*arg)] = 1; else if (*arg == 'A') { if (arg[1]) { arrnam = arg+1; @@ -505,7 +505,7 @@ bin_stat(char *name, char **args, char *ops, int func) continue; } - if (flags & STF_FILE) + if (flags & STF_FILE) { if (arrnam) *arrptr++ = ztrdup(*args); else if (hashnam) { @@ -513,6 +513,7 @@ bin_stat(char *name, char **args, char *ops, int func) *hashptr++ = ztrdup(*args); } else printf("%s%s", *args, (flags & STF_PICK) ? " " : ":\n"); + } if (iwhich > -1) { statprint(&statbuf, outbuf, *args, iwhich, flags); if (arrnam) @@ -544,7 +545,7 @@ bin_stat(char *name, char **args, char *ops, int func) putchar('\n'); } - if (arrnam) + if (arrnam) { if (ret) freearray(array); else { @@ -552,8 +553,9 @@ bin_stat(char *name, char **args, char *ops, int func) if (errflag) return 1; } + } - if (hashnam) + if (hashnam) { if (ret) freearray(hash); else { @@ -561,6 +563,7 @@ bin_stat(char *name, char **args, char *ops, int func) if (errflag) return 1; } + } return ret; } diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index 4bcd80c7f..d71fa642f 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -62,9 +62,19 @@ /* it's a TELNET based protocol, but don't think I like doing this */ #include +/* + * We use poll() in preference to select because some subset of manuals says + * that's the thing to do, plus it's a bit less fiddly. I don't actually + * have access to a system with poll but not select, however, though + * both bits of the code have been tested on a machine with both. + */ +#ifdef HAVE_POLL_H +# include +#endif + /* pinch the definition from for deficient headers */ #ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff +# define INADDR_NONE 0xffffffff #endif /* @@ -124,7 +134,8 @@ enum { ZFTP_HERE = 0x0100, /* here rather than over there */ ZFTP_CDUP = 0x0200, /* CDUP rather than CWD */ ZFTP_REST = 0x0400, /* restart: set point in remote file */ - ZFTP_RECV = 0x0800 /* receive rather than send */ + ZFTP_RECV = 0x0800, /* receive rather than send */ + ZFTP_TEST = 0x1000 /* test command, don't test */ }; typedef struct zftpcmd *Zftpcmd; @@ -134,6 +145,7 @@ static struct zftpcmd zftpcmdtab[] = { { "params", zftp_params, 0, 4, 0 }, { "login", zftp_login, 0, 3, ZFTP_CONN }, { "user", zftp_login, 0, 3, ZFTP_CONN }, + { "test", zftp_test, 0, 0, ZFTP_TEST }, { "cd", zftp_cd, 1, 1, ZFTP_CONN|ZFTP_LOGI }, { "cdup", zftp_cd, 0, 0, ZFTP_CONN|ZFTP_LOGI|ZFTP_CDUP }, { "dir", zftp_dir, 0, -1, ZFTP_CONN|ZFTP_LOGI }, @@ -674,8 +686,8 @@ zfgetmsg() zfgetline(line, 256, tmout); ptr = line; - if (zfdrrrring || !isdigit((int)*ptr) || !isdigit((int)ptr[1]) || - !isdigit((int)ptr[2])) { + if (zfdrrrring || !isdigit(STOUC(*ptr)) || !isdigit(STOUC(ptr[1])) || + !isdigit(STOUC(ptr[2]))) { /* timeout, or not talking FTP. not really interested. */ zcfinish = 2; if (!zfclosing) @@ -820,7 +832,7 @@ zfopendata(char *name) zwarnnam(name, "Must set preference S or P to transfer data", NULL, 0); return 1; } - zdfd = zfmovefd(socket(AF_INET, SOCK_STREAM, 0)); + zdfd = socket(AF_INET, SOCK_STREAM, 0); if (zdfd < 0) { zwarnnam(name, "can't get data socket: %e", NULL, errno); return 1; @@ -851,7 +863,7 @@ zfopendata(char *name) * lastmsg already has the reply code expunged. */ for (ptr = lastmsg; *ptr; ptr++) - if (isdigit(*ptr)) + if (isdigit(STOUC(*ptr))) break; if (sscanf(ptr, "%d,%d,%d,%d,%d,%d", nums, nums+1, nums+2, nums+3, nums+4, nums+5) != 6) { @@ -986,11 +998,11 @@ zfgetdata(char *name, char *rest, char *cmd, int getsize) char *ptr = strstr(lastmsg, "bytes"); zfstatus |= ZFST_NOSZ|ZFST_TRSZ; if (ptr) { - while (ptr > lastmsg && !isdigit(*ptr)) + while (ptr > lastmsg && !isdigit(STOUC(*ptr))) ptr--; - while (ptr > lastmsg && isdigit(ptr[-1])) + while (ptr > lastmsg && isdigit(STOUC(ptr[-1]))) ptr--; - if (isdigit(*ptr)) { + if (isdigit(STOUC(*ptr))) { zfstatus &= ~ZFST_NOSZ; if (getsize) { long sz = zstrtol(ptr, NULL, 10); @@ -1017,6 +1029,13 @@ zfgetdata(char *name, char *rest, char *cmd, int getsize) return 1; } zdfd = newfd; /* this is now the actual data fd */ + } else { + /* + * We avoided dup'ing zdfd up to this point, to try to keep + * things simple, so we now need to move it out of the way + * of the user-visible fd's. + */ + zdfd = zfmovefd(zdfd); } @@ -1659,7 +1678,7 @@ zftp_open(char *name, char **args, int flags) } zsock.sin_port = zservp->s_port; - zcfd = zfmovefd(socket(zsock.sin_family, SOCK_STREAM, 0)); + zcfd = socket(zsock.sin_family, SOCK_STREAM, 0); if (zcfd < 0) { zwarnnam(name, "socket failed: %e", NULL, errno); zfunsetparam("ZFTP_HOST"); @@ -1667,12 +1686,6 @@ zftp_open(char *name, char **args, int flags) return 1; } -#if defined(F_SETFD) && defined(FD_CLOEXEC) - /* If the shell execs a program, we don't want this fd left open. */ - len = FD_CLOEXEC; - fcntl(zcfd, F_SETFD, &len); -#endif - /* * now connect the socket. manual pages all say things like `this is all * explained oh-so-wonderfully in some other manual page'. not. @@ -1708,6 +1721,19 @@ zftp_open(char *name, char **args, int flags) /* now we can talk to the control connection */ zcfinish = 0; + + /* + * Move the fd out of the user-visible range. We need to do + * this after the connect() on some systems. + */ + zcfd = zfmovefd(zcfd); + +#if defined(F_SETFD) && defined(FD_CLOEXEC) + /* If the shell execs a program, we don't want this fd left open. */ + len = FD_CLOEXEC; + fcntl(zcfd, F_SETFD, &len); +#endif + len = sizeof(zsock); if (getsockname(zcfd, (struct sockaddr *)&zsock, &len) < 0) { zwarnnam(name, "getsockname failed: %e", NULL, errno); @@ -2022,6 +2048,69 @@ zftp_login(char *name, char **args, int flags) return zfgetcwd(); } +/* + * See if the server wants to tell us something. On a timeout, we usually + * have a `421 Timeout' or something such waiting for us, so we read + * it here. As well as being called explicitly by the user + * (precmd is a very good place for this, it's cheap since it has + * no network overhead), we call it in the bin_zftp front end if we + * have a connection and weren't going to call it anyway. + * + * Poll-free and select-free systems are few and far between these days, + * but I'm willing to consider suggestions. + */ + +/**/ +static int +zftp_test(char *name, char **args, int flags) +{ +#if defined(HAVE_POLL) || defined(HAVE_SELECT) + int ret; +# ifdef HAVE_POLL + struct pollfd pfd; +# else + fd_set f; + struct timeval tv; +# endif /* HAVE_POLL */ + + if (zcfd == -1) + return 1; + +# ifdef HAVE_POLL +# ifndef POLLIN + /* safety first, though I think POLLIN is more common */ +# define POLLIN POLLNORM +# endif /* HAVE_POLL */ + pfd.fd = zcfd; + pfd.events = POLLIN; + if ((ret = poll(&pfd, 1, 0)) < 0 && errno != EINTR && errno != EAGAIN) + zfclose(); + else if (ret > 0 && pfd.revents) { + /* handles 421 (maybe a bit noisily?) */ + zfgetmsg(); + } +# else + FD_ZERO(&f); + FD_SET(zcfd, &f); + tv.tv_sec = 0; + tv.tv_usec = 0; + if ((ret = select(zcfd +1, (SELECT_ARG_2_T) &f, NULL, NULL, &tv)) < 0 + && errno != EINTR) + zfclose(); + else if (ret > 0) { + /* handles 421 */ + zfgetmsg(); + } +# endif /* HAVE_POLL */ + /* if we now have zcfd == -1, then we've just been dumped out. */ + return (zcfd == -1) ? 2 : 0; +#else + zfwarnnam(name, "not supported on this system.", NULL, 0); + return 3; +#endif /* defined(HAVE_POLL) || defined(HAVE_SELECT) */ +} + + /* do ls or dir on the remote directory */ /**/ @@ -2476,7 +2565,7 @@ bin_zftp(char *name, char **args, char *ops, int func) char fullname[11] = "zftp "; char *cnam = *args++, *prefs, *ptr; Zftpcmd zptr; - int n, ret; + int n, ret = 0; for (zptr = zftpcmdtab; zptr->nam; zptr++) if (!strcmp(zptr->nam, cnam)) @@ -2521,8 +2610,25 @@ bin_zftp(char *name, char **args, char *ops, int func) "B" : "S"), ZFPM_READONLY); } } +#if defined(HAVE_SELECT) || defined (HAVE_POLL) + if (zcfd != -1 && !(zptr->flags & ZFTP_TEST)) { + /* + * Test the connection for a bad fd or incoming message, but + * only if the connection was last heard of open, and + * if we are not about to call the test command anyway. + * Not worth it unless we have select() or poll(). + */ + ret = zftp_test("zftp test", NULL, 0); + } +#endif if ((zptr->flags & ZFTP_CONN) && zcfd == -1) { - zwarnnam(fullname, "not connected.", NULL, 0); + if (ret != 2) { + /* + * with ret == 2, we just got dumped out in the test, + * so enough messages already. + */ + zwarnnam(fullname, "not connected.", NULL, 0); + } return 1; } diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c index a0c013901..42bc92bb2 100644 --- a/Src/Zle/comp1.c +++ b/Src/Zle/comp1.c @@ -41,7 +41,7 @@ struct compctl cc_compos, cc_default, cc_first, cc_dummy; /**/ Cmlist cmatcher; -/* pointers to functions required by zle */ +/* pointers to functions required by zle and defined by compctl */ /**/ void (*printcompctlptr) _((char *, Compctl, int, int)); @@ -49,6 +49,24 @@ void (*printcompctlptr) _((char *, Compctl, int, int)); /**/ Compctl (*compctl_widgetptr) _((char *, char **)); +/**/ +void (*makecompparamsptr) _((void)); + +/* pointers to functions required by compctl and defined by zle */ + +/**/ +void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, int, int, int, int, int, char **)); + +/**/ +char *(*comp_strptr) _((int*,int*)); + +/**/ +int (*getcpatptr) _((char *, int, char *, int)); + +/**/ +void (*makecomplistcallptr) _((Compctl)); + + /* Hash table for completion info for commands */ /**/ @@ -72,6 +90,23 @@ char **clwords; /**/ int incompctlfunc; +/* != 0 if we are in a new style completion function */ + +/**/ +int incompfunc; + +/* global variables for shell parameters in new style completion */ + +/**/ +long compcurrent, + compnmatches; + +/**/ +char *compcontext, + *compcommand, + *compprefix, + *compsuffix, + *compiprefix; /* This variable and the functions rembslash() and quotename() came from * * zle_tricky.c, but are now used in compctl.c, too. */ @@ -443,6 +478,8 @@ setup_comp1(Module m) cc_first.refc = 10000; cc_first.mask = 0; cc_first.mask2 = CC_CCCONT; + compcontext = compcommand = compprefix = compsuffix = + compiprefix = NULL; return 0; } @@ -469,6 +506,11 @@ finish_comp1(Module m) deletehashtable(compctltab); zfree(clwords, clwsize * sizeof(char *)); compctlreadptr = fallback_compctlread; + zsfree(compcontext); + zsfree(compcommand); + zsfree(compprefix); + zsfree(compiprefix); + zsfree(compsuffix); return 0; } diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export index 1ac9195df..c90161740 100644 --- a/Src/Zle/comp1.export +++ b/Src/Zle/comp1.export @@ -1,4 +1,5 @@ #! +addmatchesptr cc_compos cc_default cc_dummy @@ -8,14 +9,26 @@ clwords clwpos clwsize cmatcher +compcommand +compcontext compctl_widgetptr compctltab +compcurrent +compiprefix +compnmatches +compprefix +comp_strptr +compsuffix freecmatcher freecmlist freecompcond freecompctl +getcpatptr incompctlfunc +incompfunc instring +makecomplistcallptr +makecompparamsptr patcomps printcompctlptr quotename diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 4d192fef8..1913d3828 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -373,7 +373,7 @@ parse_class(Cpattern p, unsigned char *s, unsigned char e) /**/ static int -get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) +get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl) { /* Parse the basic flags for completion: * first is a flag that we are not in extended completion, @@ -394,12 +394,17 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) if(argv[0] && argv[0][0] == '-') argv++; *av = argv; - freecompctl(cc); - cclist = COMP_REMOVE; - return 0; + if (cl) + return 1; + else { + freecompctl(cc); + cclist = COMP_REMOVE; + return 0; + } } memset((void *)&cct, 0, sizeof(cct)); + cct.mask2 = CC_CCCONT; /* Loop through the flags until we have no more: * * those with arguments are not properly allocated yet, * @@ -505,6 +510,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) { char *p; + if (cl) { + zerrnam(name, "illegal option -%c", NULL, **argv); + return 1; + } if ((*argv)[1]) { p = (*argv) + 1; *argv = "" - 1; @@ -516,26 +525,28 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) p = *++argv; *argv = "" - 1; } - while (*p) { - switch (*p) { - case '+': - cct.mask2 |= CC_XORCONT; - break; - case 'c': - cct.mask2 |= CC_CCCONT; - break; - case '-': - cct.mask2 |= CC_PATCONT; - break; - case 'x': - cct.mask2 |= CC_DEFCONT; - break; - default: - zwarnnam(name, "invalid retry specification character `%c'", - NULL, *p); - return 1; - } - p++; + switch (*p) { + case '+': + cct.mask2 = CC_XORCONT; + break; + case 'n': + cct.mask2 = 0; + break; + case '-': + cct.mask2 = CC_PATCONT; + break; + case 'x': + cct.mask2 = CC_DEFCONT; + break; + default: + zwarnnam(name, "invalid retry specification character `%c'", + NULL, *p); + return 1; + } + if (p[1]) { + zwarnnam(name, "too many retry specification characters: `%s'", + p + 1, 0); + return 1; } } break; @@ -645,7 +656,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) } break; case 'l': - if ((*argv)[1]) { + if (cl) { + zerrnam(name, "illegal option -%c", NULL, **argv); + return 1; + } else if ((*argv)[1]) { cct.subcmd = (*argv) + 1; *argv = "" - 1; } else if (!argv[1]) { @@ -745,6 +759,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) *argv = "" - 1; break; case 'C': + if (cl) { + zerrnam(name, "illegal option -%c", NULL, **argv); + return 1; + } if (first && !hx) { cclist |= COMP_COMMAND; } else { @@ -754,6 +772,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) } break; case 'D': + if (cl) { + zerrnam(name, "illegal option -%c", NULL, **argv); + return 1; + } if (first && !hx) { isdef = 1; cclist |= COMP_DEFAULT; @@ -764,7 +786,11 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) } break; case 'T': - if (first && !hx) { + if (cl) { + zerrnam(name, "illegal option -%c", NULL, **argv); + return 1; + } + if (first && !hx) { cclist |= COMP_FIRST; } else { zwarnnam(name, "misplaced first completion (-T) flag", @@ -773,6 +799,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) } break; case 'L': + if (cl) { + zerrnam(name, "illegal option -%c", NULL, **argv); + return 1; + } if (!first || hx) { zwarnnam(name, "illegal use of -L flag", NULL, 0); return 1; @@ -780,6 +810,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) cclist |= COMP_LIST; break; case 'x': + if (cl) { + zerrnam(name, "extended completion not allowed", NULL, 0); + return 1; + } if (!argv[1]) { zwarnnam(name, "condition expected after -%c", NULL, **argv); @@ -811,6 +845,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) if (*++argv && (!ready || ready == 2) && **argv == '+' && !argv[0][1]) { + if (cl) { + zerrnam(name, "xor'ed completion illegal", NULL, 0); + return 1; + } /* There's an alternative (+) completion: assign * what we have so far before moving on to that. */ @@ -835,6 +873,7 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef) cc->xor = (Compctl) zcalloc(sizeof(*cc)); cc = cc->xor; memset((void *)&cct, 0, sizeof(cct)); + cct.mask2 = CC_CCCONT; } } } @@ -1084,7 +1123,7 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef) (*next)->cond = m; argv++; /* End of the condition; get the flags that go with it. */ - if (get_compctl(name, &argv, *next, 0, isdef)) + if (get_compctl(name, &argv, *next, 0, isdef, 0)) return 1; if ((!argv || !*argv) && (cclist & COMP_SPECIAL)) /* default, first, or command completion finished */ @@ -1362,17 +1401,16 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat) t >>= 1; } } - if (flags2 & (CC_XORCONT | CC_CCCONT | CC_PATCONT | CC_DEFCONT)) { + if (flags2 & (CC_XORCONT | CC_PATCONT | CC_DEFCONT)) { printf(" -t"); if (flags2 & CC_XORCONT) putchar('+'); - if (flags2 & CC_CCCONT) - putchar('c'); if (flags2 & CC_PATCONT) putchar('-'); if (flags2 & CC_DEFCONT) putchar('x'); - } + } else if (!(flags2 & CC_CCCONT)) + printf(" -tn"); /* now flags with arguments */ printif(cc->mstr, 'M'); if (flags2 & CC_NOSORT) @@ -1518,7 +1556,7 @@ bin_compctl(char *name, char **argv, char *ops, int func) return ret - 1; cc = (Compctl) zcalloc(sizeof(*cc)); - if (get_compctl(name, &argv, cc, 1, 0)) { + if (get_compctl(name, &argv, cc, 1, 0, 0)) { freecompctl(cc); return 1; } @@ -1610,7 +1648,7 @@ compctl_widget(char *name, char **argv) cclist = 0; showmask = 0; - if (get_compctl(name, &argv, cc, 1, 0)) { + if (get_compctl(name, &argv, cc, 1, 0, 0)) { freecompctl(cc); return NULL; } @@ -1632,8 +1670,478 @@ compctl_widget(char *name, char **argv) return cc; } +/**/ +static int +bin_complist(char *name, char **argv, char *ops, int func) +{ + Compctl cc; + int ret = 0; + + if (!incompfunc) { + 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 + makecomplistcallptr(cc); + + freecompctl(cc); + return ret; +} + +/**/ +static int +bin_compadd(char *name, char **argv, char *ops, int func) +{ + char *p, **sp, *e; + char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL; + char *pre = NULL, *suf = NULL, *group = NULL; + int f = 0, q = 0, m = 0, ns = 0, a = 0; + + if (!incompfunc) { + zerrnam(name, "can only be called from completion function", NULL, 0); + return 1; + } + for (; *argv && **argv == '-'; argv++) { + for (p = *argv + 1; *p; p++) { + sp = NULL; + switch (*p) { + case 'q': + f |= CMF_REMOVE; + break; + case 'Q': + q = 1; + break; + case 'f': + f |= CMF_FILE; + break; + case 'n': + f |= CMF_NOLIST; + break; + case 'U': + m = 1; + break; + case 'P': + sp = ⪯ + e = "string expected after -%c"; + break; + case 'S': + sp = &suf; + e = "string expected after -%c"; + break; + case 'J': + sp = &group; + e = "group name expected after -%c"; + break; + case 'V': + if (!group) + ns = 1; + sp = &group; + e = "group name expected after -%c"; + break; + case 'i': + sp = &ipre; + e = "string expected after -%c"; + break; + case 'p': + sp = &ppre; + e = "string expected after -%c"; + break; + case 's': + sp = &psuf; + e = "string expected after -%c"; + break; + case 'W': + sp = &prpre; + e = "string expected after -%c"; + break; + case 'a': + a = 1; + break; + case '-': + argv++; + goto ca_args; + default: + zerrnam(name, "bad option: -%c", NULL, *p); + return 1; + } + if (sp) { + if (*sp) { + zerrnam(name, "doubled option: -%c", NULL, *p); + return 1; + } + if (p[1]) { + *sp = p + 1; + p = "" - 1; + } else if (argv[1]) { + *sp = *++argv; + p = "" - 1; + } else { + zerrnam(name, e, NULL, *p); + return 1; + } + } + } + } + ca_args: + if (!*argv) { + zerrnam(name, "missing completions", NULL, 0); + return 1; + } + addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group, + f, q, m, ns, a, argv); + return 0; +} + +#define VAR(X) ((void *) (&(X))) +static struct compparam { + char *name; + int type; + void *var; +} compparams[] = { + { "CURRENT", PM_INTEGER, VAR(compcurrent) }, + { "CONTEXT", PM_SCALAR, VAR(compcontext) }, + { "COMMAND", PM_SCALAR, VAR(compcommand) }, + { "PREFIX", PM_SCALAR, VAR(compprefix) }, + { "SUFFIX", PM_SCALAR, VAR(compsuffix) }, + { "IPREFIX", PM_SCALAR, VAR(compiprefix) }, + { "NMATCHES", PM_INTEGER, VAR(compnmatches) }, + { NULL, 0, NULL } +}; + +/**/ +void makecompparams(void) +{ + struct compparam *cp; + + for (cp = compparams; cp->name; cp++) { + Param pm = createparam(cp->name, cp->type | PM_SPECIAL); + if (!pm) + pm = (Param) paramtab->getnode(paramtab, cp->name); + DPUTS(!pm, "param not set in makecompparams"); + + pm->level = locallevel; + 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; + break; + } + pm->unsetfn = compunsetfn; + } +} + +/**/ +static void +compunsetfn(Param pm, int exp) +{ + if (exp) + stdunsetfn(pm, exp); +} + +/**/ +static int +comp_wrapper(List list, FuncWrap w, char *name) +{ + if (!incompfunc) + return 1; + else { + char *octxt, *ocmd, *opre, *osuf, *oipre; + long ocur; + + ocur = compcurrent; + octxt = dupstring(compcontext); + ocmd = dupstring(compcommand); + opre = dupstring(compprefix); + osuf = dupstring(compsuffix); + oipre = dupstring(compiprefix); + + runshfunc(list, w, name); + + compcurrent = ocur; + zsfree(compcontext); + compcontext = ztrdup(octxt); + zsfree(compcommand); + compcommand = ztrdup(ocmd); + zsfree(compprefix); + compprefix = ztrdup(opre); + zsfree(compsuffix); + compsuffix = ztrdup(osuf); + zsfree(compiprefix); + compiprefix = ztrdup(oipre); + + return 0; + } +} + +/**/ +static void +ignore_prefix(int l) +{ + char *o, sav = compprefix[l]; + + compprefix[l] = '\0'; + o = compiprefix; + compiprefix = tricat(o, compprefix, ""); + zsfree(o); + compprefix[l] = sav; + o = compprefix; + compprefix = ztrdup(o + l); + zsfree(o); +} + +/**/ +static int +comp_check(void) +{ + if (!incompfunc) { + zerr("condition can only be used in completion function", NULL, 0); + return 0; + } + return 1; +} + +/**/ +static void +restrict_range(int b, int e) +{ + int i = e - b; + char **p = (char **) zcalloc((i + 1) * sizeof(char *)), **q, **pp; + + for (q = p, pp = pparams + b + 1; i; i--, q++, pp++) + *q = ztrdup(*pp); + zsfree(compcommand); + compcommand = ztrdup(pparams[b]); + freearray(pparams); + pparams = p; + zsfree(compcontext); + if ((compcurrent -= b + 1)) + compcontext = ztrdup("arg"); + else + compcontext = ztrdup("cmd"); +} + +/**/ +static int +cond_prefix(char **a, int id) +{ + if (comp_check()) + return strpfx(cond_str(a, 0), compprefix); + return 0; +} + +/**/ +static int +cond_iprefix(char **a, int id) +{ + if (comp_check()) { + char *s = cond_str(a, 0); + + if (strpfx(s, compprefix)) { + ignore_prefix(strlen(s)); + return 1; + } + } + return 0; +} + +/**/ +static int +cond_position(char **a, int id) +{ + if (comp_check()) { + int b = cond_val(a, 0), e = (a[1] ? cond_val(a, 1) : b); + int l = arrlen(pparams), t, i = compcurrent - 1; + + if (b > 0) + b--; + if (e > 0) + e--; + if (b < 0) + b += l; + if (e < 0) + e += l; + t = (b >= 0 && e >= 0 && i >= b && i <= e && b <= e); + + if (t && a[1]) { + if (b > l) + b = l; + if (e > l) + e = l; + restrict_range(b, e); + } + return t; + } + return 0; +} + +/**/ +static int +cond_word(char **a, int id) +{ + if (comp_check()) { + int o = ((id & 2) ? compcurrent : 0) + cond_val(a, 0); + int l = arrlen(pparams); + char *s; + + if (o < 0) + o += l; + + o--; + if (o < 0 || o >= l) + return 0; + + s = pparams[o]; + return ((id & 1) ? cond_match(a, 1, s) : !strcmp(s, cond_str(a, 1))); + } + return 0; +} + +/**/ +static int +cond_strcl(char **a, int id) +{ + if (comp_check()) { + char *s; + int i; + + if (a[1]) { + s = cond_str(a, 1); + i = cond_val(a, 0); + } else { + s = cond_str(a, 0); + i = -1; + } + if (!getcpatptr) { + zerr("zle not loaded, zle condition not available", NULL, 0); + return 1; + } + i = getcpatptr(comp_strptr(NULL, NULL), i, s, id); + if (i != -1) { + ignore_prefix(i); + return 1; + } + } + return 0; +} + +/**/ +static int +cond_words(char **a, int id) +{ + if (comp_check()) { + int b = cond_val(a, 0), e = (a[1] ? cond_val(a, 1) : -1); + int l = arrlen(pparams); + + return (l >= b && l <= e); + } + return 0; +} + +/**/ +static int +cond_range(char **a, int id) +{ + if (comp_check()) { + char *s, **p; + int i, l = arrlen(pparams), t = 0, b = 0, e = l - 1; + Comp c; + + i = compcurrent - 1; + if (i < 0 || i >= l) + return 0; + + if (id & 1) { + s = a[0]; + singsub(&s); + c = parsereg(s); + } else + s = cond_str(a, 0); + + for (i--, p = pparams + i; i >= 0; p--, i--) { + if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) { + b = i + 1; + t = 1; + break; + } + } + if (t && (id & 2)) { + int tt = 0; + + if (id & 1) { + s = a[1]; + singsub(&s); + c = parsereg(s); + } else + s = cond_str(a, 1); + + for (i++, p = pparams + i; i < l; p++, i++) { + if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) { + e = i - 1; + tt = 1; + break; + } + } + if (tt && i < compcurrent) + t = 0; + } + if (e < b) + t = 0; + if (t) + restrict_range(b, e); + return t; + } + return 0; +} + +/**/ +static int +cond_nmatches(char **a, int id) +{ + if (comp_check()) + return compnmatches == cond_val(a, 0); + return 0; +} + static struct builtin bintab[] = { BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL), + BUILTIN("complist", 0, bin_complist, 1, -1, 0, NULL, NULL), + BUILTIN("compadd", 0, bin_compadd, 1, -1, 0, NULL, NULL), +}; + +static struct conddef cotab[] = { + CONDDEF("prefix", 0, cond_prefix, 1, 1, 0), + CONDDEF("iprefix", 0, cond_iprefix, 1, 1, 0), + CONDDEF("position", 0, cond_position, 1, 2, 0), + CONDDEF("word", 0, cond_word, 2, 2, 0), + CONDDEF("mword", 0, cond_word, 2, 2, 1), + CONDDEF("current", 0, cond_word, 2, 2, 2), + CONDDEF("mcurrent", 0, cond_word, 2, 2, 3), + CONDDEF("string", 0, cond_strcl, 1, 2, 0), + CONDDEF("class", 0, cond_strcl, 1, 2, 1), + CONDDEF("words", 0, cond_words, 1, 2, 0), + CONDDEF("between", 0, cond_range, 2, 2, 2), + CONDDEF("mbetween", 0, cond_range, 2, 2, 3), + CONDDEF("after", 0, cond_range, 1, 1, 0), + CONDDEF("mafter", 0, cond_range, 1, 1, 1), + CONDDEF("nmatches", 0, cond_nmatches, 1, 1, 0), +}; + +static struct funcwrap wrapper[] = { + WRAPDEF(comp_wrapper), }; /**/ @@ -1643,6 +2151,7 @@ setup_compctl(Module m) compctltab->printnode = printcompctlp; printcompctlptr = printcompctl; compctl_widgetptr = compctl_widget; + makecompparamsptr = makecompparams; return 0; } @@ -1650,7 +2159,9 @@ setup_compctl(Module m) int boot_compctl(Module m) { - if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab))) + if(!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) | + addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) | + !addwrapper(m, wrapper))) return 1; return 0; } @@ -1662,6 +2173,8 @@ int cleanup_compctl(Module m) { deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)); + deletewrapper(m, wrapper); return 0; } @@ -1672,6 +2185,7 @@ finish_compctl(Module m) compctltab->printnode = NULL; printcompctlptr = NULL; compctl_widgetptr = NULL; + makecompparamsptr = NULL; return 0; } diff --git a/Src/Zle/compctl.mdd b/Src/Zle/compctl.mdd index c83ecda29..48aabe38a 100644 --- a/Src/Zle/compctl.mdd +++ b/Src/Zle/compctl.mdd @@ -1,5 +1,7 @@ moddeps="comp1" -autobins="compctl" +autobins="compctl complist compadd" + +autoprefixconds="prefix iprefix position word mword current mcurrent string class words between mbetween after mafter nmatches" objects="compctl.o" diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index 52e70fad5..12425d872 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -25,11 +25,11 @@ "beginning-of-line-hist", beginningoflinehist, 0 "capitalize-word", capitalizeword, 0 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND -"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "copy-prev-word", copyprevword, 0 "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX "delete-char", deletechar, ZLE_KEEPSUFFIX -"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "delete-word", deleteword, ZLE_KEEPSUFFIX "describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND @@ -48,8 +48,8 @@ "execute-named-cmd", NULL, 0 "expand-cmd-path", expandcmdpath, 0 "expand-history", expandhistory, 0 -"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "expand-word", expandword, 0 "forward-char", forwardchar, 0 "forward-word", forwardword, 0 @@ -68,11 +68,11 @@ "kill-region", killregion, ZLE_KILL | ZLE_KEEPSUFFIX "kill-whole-line", killwholeline, ZLE_KILL | ZLE_KEEPSUFFIX "kill-word", killword, ZLE_KILL | ZLE_KEEPSUFFIX -"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_ISCOMP "list-expand", listexpand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "magic-space", magicspace, 0 -"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "overwrite-mode", overwritemode, 0 "pound-insert", poundinsert, 0 @@ -84,7 +84,7 @@ "quote-region", quoteregion, 0 "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "redo", redo, 0 -"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "self-insert", selfinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 71a929f87..f12505bd3 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -47,23 +47,29 @@ struct widget { ZleIntFunc fn; /* pointer to internally implemented widget */ char *fnnam; /* name of the shell function for user-defined widget */ Compctl cc; /* for use with a WIDGET_COMP widget */ + struct { + ZleIntFunc fn; /* internal widget function to call */ + char *wid; /* name of widget to call */ + char *func; /* name of shell function to call */ + } comp; } u; }; #define WIDGET_INT (1<<0) /* widget is internally implemented */ #define WIDGET_COMP (1<<1) /* Special completion widget */ -#define ZLE_MENUCMP (1<<2) /* DON'T invalidate completion list */ -#define ZLE_YANK (1<<3) -#define ZLE_LINEMOVE (1<<4) /* command is a line-oriented movement */ -#define ZLE_LASTCOL (1<<5) /* command maintains lastcol correctly */ -#define ZLE_KILL (1<<6) +#define WIDGET_NCOMP (1<<2) /* new style completion widget */ +#define ZLE_MENUCMP (1<<3) /* DON'T invalidate completion list */ +#define ZLE_YANK (1<<4) +#define ZLE_LINEMOVE (1<<5) /* command is a line-oriented movement */ +#define ZLE_LASTCOL (1<<6) /* command maintains lastcol correctly */ +#define ZLE_KILL (1<<7) #define ZLE_KEEPSUFFIX (1<<9) /* DON'T remove added suffix */ #define ZLE_USEMENU (1<<10) /* Do ) use menu completion for */ #define ZLE_NOMENU (1<<11) /* Don't ) widget, else use default */ #define ZLE_USEGLOB (1<<12) /* Do ) use glob completion for */ #define ZLE_NOGLOB (1<<13) /* Don't ) widget, else use default */ #define ZLE_NOTCOMMAND (1<<14) /* widget should not alter lastcmd */ - +#define ZLE_ISCOMP (1<<15) /* usable for new style completion */ /* thingies */ struct thingy { diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 7de96bd03..1ffe6f156 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -598,10 +598,10 @@ bin_bindkey(char *name, char **argv, char *ops, int func) int n; /* select operation and ensure no clashing arguments */ - for(op = opns; op->o && !ops[op->o]; op++) ; + for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ; if(op->o) for(opp = op; (++opp)->o; ) - if(ops[opp->o]) { + if(ops[STOUC(opp->o)]) { zwarnnam(name, "incompatible operation selection options", NULL, 0); return 1; diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 515405a0d..2c0d3655e 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -443,9 +443,9 @@ zleread(char *lp, char *rp, int ha) insmode = unset(OVERSTRIKE); eofsent = 0; resetneeded = 0; - lpptbuf = promptexpand(lp, 1, NULL, NULL); + lpromptbuf = promptexpand(lp, 1, NULL, NULL); pmpt_attr = txtchange; - rpptbuf = promptexpand(rp, 1, NULL, NULL); + rpromptbuf = promptexpand(rp, 1, NULL, NULL); rpmpt_attr = txtchange; histallowed = ha; PERMALLOC { @@ -529,8 +529,8 @@ zleread(char *lp, char *rp, int ha) statusline = NULL; invalidatelist(); trashzle(); - free(lpptbuf); - free(rpptbuf); + free(lpromptbuf); + free(rpromptbuf); zleactive = 0; alarm(0); } LASTALLOC; @@ -565,13 +565,14 @@ execzlefunc(Thingy func) showmsg(msg); zsfree(msg); feep(); - } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_COMP)) { + } else if((w = func->widget)->flags & + (WIDGET_INT|WIDGET_COMP | WIDGET_NCOMP)) { int wflags = w->flags; if(!(wflags & ZLE_KEEPSUFFIX)) removesuffix(); if(!(wflags & ZLE_MENUCMP) || - ((wflags & WIDGET_COMP) && compwidget != w)) { + ((wflags & (WIDGET_COMP|WIDGET_NCOMP)) && compwidget != w)) { /* If we are doing a special completion, and the widget * is not the one currently in use for special completion, * we are starting a new completion. @@ -586,6 +587,9 @@ execzlefunc(Thingy func) if (wflags & WIDGET_COMP) { compwidget = w; completespecial(); + } else if (wflags & WIDGET_NCOMP) { + compwidget = w; + completecall(); } else w->u.fn(); if (!(wflags & ZLE_NOTCOMMAND)) @@ -855,7 +859,7 @@ trashzle(void) static struct builtin bintab[] = { BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaMldDANmrsLR", NULL), BUILTIN("vared", 0, bin_vared, 1, 7, 0, NULL, NULL), - BUILTIN("zle", 0, bin_zle, 0, -1, 0, "lDANCLmMgG", NULL), + BUILTIN("zle", 0, bin_zle, 0, -1, 0, "lDANCLmMgGc", NULL), }; /**/ @@ -869,6 +873,11 @@ setup_zle(Module m) spaceinlineptr = spaceinline; zlereadptr = zleread; + addmatchesptr = addmatches; + comp_strptr = comp_str; + getcpatptr = getcpat; + makecomplistcallptr = makecomplistcall; + /* initialise the thingies */ init_thingies(); @@ -931,6 +940,11 @@ finish_zle(Module m) spaceinlineptr = noop_function_int; zlereadptr = fallback_zleread; + addmatchesptr = NULL; + comp_strptr = NULL; + getcpatptr = NULL; + makecomplistcallptr = NULL; + return 0; } diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 27e9f2071..c8d6c70a7 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -33,7 +33,7 @@ /* Expanded prompts */ /**/ -char *lpptbuf, *rpptbuf; +char *lpromptbuf, *rpromptbuf; /* Text attributes after displaying prompts */ @@ -77,17 +77,17 @@ int cost; /* Oct/Nov 94: some code savagely redesigned to fix several bugs - refreshline() & tc_rightcurs() majorly rewritten; zrefresh() fixed - I've put my fingers into just about every routine in here - - any queries about updates to mason@werple.net.au */ + any queries about updates to mason@primenet.com.au */ static char **nbuf = NULL, /* new video buffer line-by-line char array */ **obuf = NULL; /* old video buffer line-by-line char array */ static int more_start, /* more text before start of screen? */ more_end, /* more stuff after end of screen? */ - lppth, /* lines taken up by the prompt */ olnct, /* previous number of lines */ ovln, /* previous video cursor position line */ - pptw, rpw, /* prompt widths on screen */ - rppth, /* right prompt height */ + lpromptw, rpromptw, /* prompt widths on screen */ + lprompth, /* lines taken up by the prompt */ + rprompth, /* right prompt height */ vcs, vln, /* video cursor position column & line */ vmaxln, /* video maximum number of lines */ winw, winh, rwinh, /* window width & height */ @@ -100,7 +100,6 @@ resetvideo(void) int ln; static int lwinw = -1, lwinh = -1; /* last window width & height */ - genprompts(); winw = columns; /* terminal width */ if (termflags & TERM_SHORT) winh = 1; @@ -132,13 +131,16 @@ resetvideo(void) *obuf[ln] = '\0'; } - if (pptw) { - memset(nbuf[0], ' ', pptw); - memset(obuf[0], ' ', pptw); - nbuf[0][pptw] = obuf[0][pptw] = '\0'; + countprompt(lpromptbuf, &lpromptw, &lprompth); + countprompt(rpromptbuf, &rpromptw, &rprompth); + + if (lpromptw) { + memset(nbuf[0], ' ', lpromptw); + memset(obuf[0], ' ', lpromptw); + nbuf[0][lpromptw] = obuf[0][lpromptw] = '\0'; } - vcs = pptw; + vcs = lpromptw; olnct = nlnct = 0; if (showinglist > 0) showinglist = -2; @@ -280,21 +282,25 @@ zrefresh(void) tsetcap(TCSTANDOUTEND, 0); tsetcap(TCUNDERLINEEND, 0); - if (!clearflag) + if (!clearflag) { if (tccan(TCCLEAREOD)) tcout(TCCLEAREOD); else cleareol = 1; /* request: clear to end of line */ + } if (t0 > -1) olnct = t0; if (termflags & TERM_SHORT) vcs = 0; - else if (!clearflag && lpptbuf[0]) - zputs(lpptbuf, shout); + else if (!clearflag && lpromptbuf[0]) { + zputs(lpromptbuf, shout); + if (lpromptw == 0) + zputs("\n", shout); /* works with both hasam and !hasam */ + } if (clearflag) { zputc('\r', shout); vcs = 0; - moveto(0, pptw); + moveto(0, lpromptw); } fflush(shout); clearf = clearflag; @@ -326,7 +332,7 @@ zrefresh(void) if (!*nbuf) *nbuf = (char *)zalloc(winw + 2); - s = (unsigned char *)(nbuf[ln = 0] + pptw); + s = (unsigned char *)(nbuf[ln = 0] + lpromptw); t = line; sen = (unsigned char *)(*nbuf + winw); for (; t < line+ll; t++) { @@ -425,15 +431,16 @@ zrefresh(void) /* determine whether the right-prompt exists and can fit on the screen */ if (!more_start) - put_rpmpt = rppth == 1 && rpptbuf[0] && !strchr(rpptbuf, '\t') && - (int)strlen(nbuf[0]) + rpw < winw - 1; + put_rpmpt = rprompth == 1 && rpromptbuf[0] && + !strchr(rpromptbuf, '\t') && + (int)strlen(nbuf[0]) + rpromptw < winw - 1; else { /* insert >.... on first line if there is more text before start of screen */ - memset(nbuf[0], ' ', pptw); - t0 = winw - pptw; + memset(nbuf[0], ' ', lpromptw); + t0 = winw - lpromptw; t0 = t0 > 5 ? 5 : t0; - strncpy(nbuf[0] + pptw, ">....", t0); - memset(nbuf[0] + pptw + t0, ' ', winw - t0 - pptw); + strncpy(nbuf[0] + lpromptw, ">....", t0); + memset(nbuf[0] + lpromptw + t0, ' ', winw - t0 - lpromptw); nbuf[0][winw] = nbuf[0][winw + 1] = '\0'; } @@ -477,8 +484,8 @@ zrefresh(void) /* output the right-prompt if appropriate */ if (put_rpmpt && !ln && !oput_rpmpt) { - moveto(0, winw - 1 - rpw); - zputs(rpptbuf, shout); + moveto(0, winw - 1 - rpromptw); + zputs(rpromptbuf, shout); vcs = winw - 1; /* reset character attributes to that set by the main prompt */ txtchange = pmpt_attr; @@ -659,12 +666,12 @@ refreshline(int ln) /* 2c: if we're on the first line, start checking at the end of the prompt; we shouldn't be doing anything within the prompt */ - if (ln == 0 && pptw) { - i = pptw - ccs; + if (ln == 0 && lpromptw) { + i = lpromptw - ccs; j = strlen(ol); nl += i; ol += (i > j ? j : i); /* if ol is too short, point it to '\0' */ - ccs = pptw; + ccs = lpromptw; } /* 3: main display loop - write out the buffer using whatever tricks we can */ @@ -815,7 +822,7 @@ moveto(int ln, int cl) instead of TCDOWN */ while (ln > vln) { - if (vln < vmaxln - 1) + if (vln < vmaxln - 1) { if (ln > vmaxln - 1) { if (tc_downcurs(vmaxln - 1 - vln)) vcs = 0; @@ -826,6 +833,7 @@ moveto(int ln, int cl) vln = ln; continue; } + } zputc('\r', shout), vcs = 0; /* safety precaution */ while (ln > vln) { zputc('\n', shout); @@ -893,21 +901,23 @@ tc_rightcurs(int cl) /* otherwise _carefully_ write the contents of the video buffer. if we're anywhere in the prompt, goto the left column and write the whole - prompt out unless ztrlen(lpptbuf) == pptw : we can cheat then */ - if (vln == 0 && i < pptw) { - if (strlen(lpptbuf) == pptw) - fputs(lpptbuf + i, shout); - else if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpptbuf))) + prompt out unless ztrlen(lpromptbuf) == lpromptw : we can cheat then */ + if (vln == 0 && i < lpromptw) { + if (strlen(lpromptbuf) == lpromptw) + fputs(lpromptbuf + i, shout); + else if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpromptbuf))) /* it is cheaper to send TCRIGHT than reprint the whole prompt */ - for (ct = pptw - i; ct--; ) + for (ct = lpromptw - i; ct--; ) tcout(TCRIGHT); else { if (i != 0) zputc('\r', shout); - tc_upcurs(lppth - 1); - zputs(lpptbuf, shout); + tc_upcurs(lprompth - 1); + zputs(lpromptbuf, shout); + if (lpromptw == 0) + zputs("\n", shout); /* works with both hasam and !hasam */ } - i = pptw; + i = lpromptw; ct = cl - i; } @@ -969,7 +979,7 @@ redisplay(void) { moveto(0, 0); zputc('\r', shout); /* extra care */ - tc_upcurs(lppth - 1); + tc_upcurs(lprompth - 1); resetneeded = 1; clearflag = 0; } @@ -987,7 +997,7 @@ singlerefresh(void) nlnct = 1; /* generate the new line buffer completely */ - for (vsiz = 1 + pptw, t0 = 0; t0 != ll; t0++, vsiz++) + for (vsiz = 1 + lpromptw, t0 = 0; t0 != ll; t0++, vsiz++) if (line[t0] == '\t') vsiz = (vsiz | 7) + 1; else if (icntrl(line[t0])) @@ -1002,9 +1012,10 @@ singlerefresh(void) cs = 0; } - memcpy(vbuf, strchr(lpptbuf, 0) - pptw, pptw); /* only use last part of prompt */ - vbuf[pptw] = '\0'; - vp = vbuf + pptw; + /* only use last part of prompt */ + memcpy(vbuf, strchr(lpromptbuf, 0) - lpromptw, lpromptw); + vbuf[lpromptw] = '\0'; + vp = vbuf + lpromptw; for (t0 = 0; t0 != ll; t0++) { if (line[t0] == '\t') @@ -1104,13 +1115,3 @@ singmoveto(int pos) } } } - -/* recheck size of prompts */ - -/**/ -static void -genprompts(void) -{ - countprompt(lpptbuf, &pptw, &lppth); - countprompt(rpptbuf, &rpw, &rppth); -} diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 68329be65..2e21b5add 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -246,7 +246,10 @@ freewidget(Widget w) { if ((w->flags & WIDGET_COMP) && w->u.cc) freecompctl(w->u.cc); - else if(!(w->flags & WIDGET_INT)) + else if (w->flags & WIDGET_NCOMP) { + zsfree(w->u.comp.wid); + zsfree(w->u.comp.func); + } else if(!(w->flags & WIDGET_INT)) zsfree(w->u.fnnam); zfree(w, sizeof(*w)); } @@ -337,16 +340,17 @@ bin_zle(char *name, char **args, char *ops, int func) { 'A', bin_zle_link, 2, 2 }, { 'N', bin_zle_new, 1, 2 }, { 'C', bin_zle_compctl, 1, -1}, + { 'c', bin_zle_complete, 3, 3 }, { 0, bin_zle_call, 0, -1 }, }; struct opn const *op, *opp; int n; /* select operation and ensure no clashing arguments */ - for(op = opns; op->o && !ops[op->o]; op++) ; + for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ; if(op->o) for(opp = op; (++opp)->o; ) - if(ops[opp->o]) { + if(ops[STOUC(opp->o)]) { zerrnam(name, "incompatible operation selection options", NULL, 0); return 1; @@ -395,6 +399,11 @@ scanlistwidgets(HashNode hn, int list) if (w->flags & WIDGET_COMP) { if (printcompctlptr && w->u.cc) printcompctlptr(NULL, w->u.cc, PRINT_LIST, 0); + } else if (w->flags & WIDGET_NCOMP) { + fputc(' ', stdout); + quotedzputs(w->u.comp.wid, stdout); + fputc(' ', stdout); + quotedzputs(w->u.comp.func, stdout); } else if(strcmp(t->nam, w->u.fnnam)) { fputc(' ', stdout); quotedzputs(w->u.fnnam, stdout); @@ -405,6 +414,11 @@ scanlistwidgets(HashNode hn, int list) fputs(" -C", stdout); if (printcompctlptr && w->u.cc) printcompctlptr(NULL, w->u.cc, PRINT_TYPE, 0); + } else if (w->flags & WIDGET_NCOMP) { + fputs(" -c ", stdout); + nicezputs(w->u.comp.wid, stdout); + fputc(' ', stdout); + nicezputs(w->u.comp.func, stdout); } else if(strcmp(t->nam, w->u.fnnam)) { fputs(" (", stdout); nicezputs(w->u.fnnam, stdout); @@ -504,13 +518,41 @@ bin_zle_compctl(char *name, char **args, char *ops, char func) return 0; } +/**/ +static int +bin_zle_complete(char *name, char **args, char *ops, char func) +{ + Thingy t; + Widget w, cw; + + t = rthingy(args[1]); + cw = t->widget; + unrefthingy(t); + if (!(cw->flags & ZLE_ISCOMP)) { + zerrnam(name, "invalid widget `%s'", args[1], 0); + return 1; + } + w = zalloc(sizeof(*w)); + w->flags = WIDGET_NCOMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX; + w->first = NULL; + w->u.comp.fn = cw->u.fn; + w->u.comp.wid = ztrdup(args[1]); + w->u.comp.func = ztrdup(args[2]); + if (bindwidget(w, rthingy(args[0]))) { + freewidget(w); + zerrnam(name, "widget name `%s' is protected", args[0], 0); + return 1; + } + return 0; +} + /**/ static int bin_zle_call(char *name, char **args, char *ops, char func) { Thingy t; - if(!zleactive || incompctlfunc) { + if(!zleactive || incompctlfunc || incompfunc) { zerrnam(name, "widgets can only be called when ZLE is active", NULL, 0); return 1; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 8c976449e..a958752ca 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -237,6 +237,15 @@ struct aminfo { static Aminfo ainfo, fainfo; +/* This contains the name of the function to call if this is for a new * + * style completion. */ + +static char *compfunc = NULL; + +/* The memory heap to use for new style completion generation. */ + +static Heap compheap; + /* Find out if we have to insert a tab (instead of trying to complete). */ /**/ @@ -272,6 +281,15 @@ completespecial(void) docomplete(compwidget->u.cc ? COMP_WIDGET : COMP_COMPLETE); } +/**/ +void +completecall(void) +{ + compfunc = compwidget->u.comp.func; + compwidget->u.comp.fn(); + compfunc = NULL; +} + /**/ void completeword(void) @@ -408,11 +426,15 @@ reversemenucomplete(void) void acceptandmenucomplete(void) { + int sl = suffixlen[' ']; + if (!menucmp) { feep(); return; } - cs = menuend + menuinsc; + cs = menupos + menulen + menuinsc; + if (sl) + backdel(sl); inststrlen(" ", 1, 1); menuinsc = menulen = 0; menupos = cs; @@ -439,6 +461,13 @@ static int lastambig; static char *cmdstr; +/* This hold the name of the variable we are working on. */ + +static char *varname; + +/* != 0 if we are in a subscript */ + +static int insubscr; /* Check if the given string is the name of a parameter and if this * * parameter is one worth expanding. */ @@ -614,16 +643,13 @@ docomplete(int lst) lst = COMP_EXPAND; else { int t0, n = 0; - char *fc; struct hashnode *hn; for (t0 = cmdnamtab->hsize - 1; t0 >= 0; t0--) for (hn = cmdnamtab->nodes[t0]; hn; hn = hn->next) { - if (strpfx(q, hn->nam) && (fc = findcmd(hn->nam))) { - zsfree(fc); + if (strpfx(q, hn->nam) && findcmd(hn->nam, 0)) n++; - } if (n == 2) break; } @@ -889,7 +915,7 @@ unmetafy_line(void) static char * get_comp_string(void) { - int t0, tt0, i, j, k, cp, rd, sl, ocs; + int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins; char *s = NULL, *linptr, *tmp, *p, *tt = NULL; zsfree(brbeg); @@ -943,13 +969,16 @@ get_comp_string(void) linredir = inredir; zsfree(cmdstr); cmdstr = NULL; + zsfree(varname); + varname = NULL; + insubscr = 0; zleparse = 1; clwpos = -1; lexsave(); inpush(dupstrspace((char *) linptr), 0, NULL); strinbeg(); stophist = 2; - i = tt0 = cp = rd = 0; + i = tt0 = cp = rd = ins = oins = 0; /* This loop is possibly the wrong way to do this. It goes through * * the previously massaged command line using the lexer. It stores * @@ -963,8 +992,10 @@ get_comp_string(void) * this would be to pass the command line through the parser too, * * and get the arguments that way. Maybe in 3.1... */ do { - lincmd = incmdpos; - linredir = inredir; + lincmd = ((incmdpos && !ins) || (oins == 2 && i == 2) || + (ins == 3 && i == 1)); + linredir = (inredir && !ins); + oins = ins; /* Get the next token. */ ctxtlex(); if (tok == DINPAR) @@ -973,7 +1004,9 @@ get_comp_string(void) /* We reached the end. */ if (tok == ENDINPUT) break; - if (tok == BAR || tok == AMPER || + if ((ins && (tok == DO || tok == SEPER)) || + (ins == 2 && i == 2) || (ins == 3 && i == 3) || + tok == BAR || tok == AMPER || tok == BARAMP || tok == AMPERBANG || ((tok == DBAR || tok == DAMPER) && !incond)) { /* This is one of the things that separate commands. If we * @@ -982,11 +1015,13 @@ get_comp_string(void) if (tt) break; /* Otherwise reset the variables we are collecting data in. */ - i = tt0 = cp = rd = 0; + i = tt0 = cp = rd = ins = 0; } - if (lincmd && tok == STRING) { + if (lincmd && (tok == STRING || tok == FOR || tok == FOREACH || + tok == SELECT || tok == REPEAT || tok == CASE)) { /* The lexer says, this token is in command position, so * * store the token string (to find the right compctl). */ + ins = (tok == REPEAT ? 2 : (tok != STRING)); zsfree(cmdstr); cmdstr = ztrdup(tokstr); i = 0; @@ -1004,9 +1039,13 @@ get_comp_string(void) rd = linredir; if (inwhat == IN_NOTHING && incond) inwhat = IN_COND; - } + } else if (linredir) + continue; if (!tokstr) continue; + /* Hack to allow completion after `repeat n do'. */ + if (oins == 2 && !i && !strcmp(tokstr, "do")) + ins = 3; /* We need to store the token strings of all words (for some of * * the more complicated compctl -x things). They are stored in * * the clwords array. Make this array big enough. */ @@ -1069,10 +1108,16 @@ get_comp_string(void) /* We found a simple string. */ s = ztrdup(clwords[clwpos]); } else if (t0 == ENVSTRING) { + char sav; /* The cursor was inside a parameter assignment. */ for (s = tt; iident(*s); s++); + sav = *s; + *s = '\0'; + zsfree(varname); + varname = ztrdup(tt); + *s = sav; if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb) - s = NULL, inwhat = IN_MATH; + s = NULL, inwhat = IN_MATH, insubscr = 1; else if (*s == '=') { s++; wb += s - tt; @@ -1110,14 +1155,32 @@ get_comp_string(void) * foo[_ wrong (note no $). If we are in a subscript, treat it * * as being in math. */ if (inwhat != IN_MATH) { - int i = 0; + int i = 0, hn = 0; + char *nb = (*s == String ? s + 1 : NULL), *ne = NULL; + for (tt = s; ++tt < s + cs - wb;) - if (*tt == Inbrack) + if (*tt == String) { + hn = 0; + nb = tt + 1; + } else if (*tt == Inbrack) { i++; - else if (i && *tt == Outbrack) + if (nb && !hn) { + hn = 1; + ne = tt; + } + } else if (i && *tt == Outbrack) i--; - if (i) + if (i) { inwhat = IN_MATH; + insubscr = 1; + if (hn && nb && ne) { + char sav = *ne; + *ne = '\0'; + zsfree(varname); + varname = ztrdup(nb); + *ne = sav; + } + } } if (inwhat == IN_MATH) { /* In mathematical expression, we complete parameter names (even * @@ -2336,6 +2399,119 @@ instmatch(Cmatch m) return r; } +/**/ +void +addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, + char *suf, char *group, + int flags, int quote, int menu, int nosort, int alt, char **argv) +{ + char *s, *t; + int lpl, lsl, i; + Aminfo ai = (alt ? fainfo : ainfo); + Cmatch cm; + + if (menu && isset(AUTOMENU)) + usemenu = 1; + SWITCHHEAPS(compheap) { + HEAPALLOC { + if (ipre) + ipre = dupstring(ipre); + if (ppre) { + ppre = dupstring(ppre); + lpl = strlen(ppre); + } else + lpl = 0; + if (psuf) { + psuf = dupstring(psuf); + lsl = strlen(psuf); + } else + lsl = 0; + if (pre) + pre = dupstring(pre); + if (suf) + suf = dupstring(suf); + if (!prpre && (prpre = ppre)) { + singsub(&prpre); + untokenize(prpre); + } else + prpre = dupstring(prpre); + if (group) { + endcmgroup(NULL); + begcmgroup(group, nosort); + if (nosort) + mgroup->flags |= CGF_NOSORT; + } + if (ai->pprefix) { + if (pre) + ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0'; + else + ai->pprefix[0] = '\0'; + } else + ai->pprefix = dupstring(pre ? pre : ""); + + for (; (s = *argv); argv++) { + if (ai->firstm) { + if ((i = pfxlen(ai->firstm->str, s)) < ai->prerest) + ai->prerest = i; + if ((i = sfxlen(ai->firstm->str, s)) < ai->suflen) + ai->suflen = i; + } + t = s; + if (ppre) + t = dyncat(ppre, t); + if (ipre && *ipre) { + ai->noipre = 0; + if (ai->icpl > lpl) + ai->icpl = lpl; + if (ai->icsl > lsl) + ai->icsl = lsl; + if (ai->iaprefix) + ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0'; + else + ai->iaprefix = dupstring(t); + if (ai->iprefix) { + if (strcmp(ipre, ai->iprefix)) + ai->iprefix = ""; + } else + ai->iprefix = dupstring(ipre); + + t = dyncat(ipre, t); + } else + ai->iprefix = ""; + if (ai->cpl > lpl) + ai->cpl = lpl; + if (ai->csl > lsl) + ai->csl = lsl; + if (ai->aprefix) + ai->aprefix[pfxlen(ai->aprefix, t)] = '\0'; + else + ai->aprefix = dupstring(t); + mnum++; + ai->count++; + + cm = (Cmatch) halloc(sizeof(struct cmatch)); + cm->ppre = ppre; + cm->psuf = psuf; + cm->prpre = prpre; + if (!quote) + s = quotename(s, NULL, NULL, NULL); + cm->str = dupstring(s); + cm->ipre = cm->ripre = ipre; + cm->pre = pre; + cm->suf = suf; + cm->flags = flags; + cm->brpl = brpl; + cm->brsl = brsl; + addlinknode((alt ? fmatches : matches), cm); + + if (expl) + expl->fcount++; + if (!ai->firstm) + ai->firstm = cm; + } + } LASTALLOC; + } SWITCHBACKHEAPS; +} /* 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 * @@ -2351,7 +2527,7 @@ addmatch(char *s, char *t) { int test = 0, sl = strlen(s), pl = rpl, cc = 0, isf = 0; int mpl = 0, msl = 0, bpl = brpl, bsl = brsl; - char *e = NULL, *tt, *te, *fc, *ms = NULL; + char *e = NULL, *tt, *te, *ms = NULL; Comp cp = patcomp; HashNode hn; Param pm; @@ -2377,6 +2553,8 @@ addmatch(char *s, char *t) hn = (HashNode) t; pm = (Param) t; + if (incompfunc) + s = dupstring(s); if (!addwhat) { test = 1; } else if (addwhat == -1 || addwhat == -5 || addwhat == -6 || @@ -2427,11 +2605,8 @@ addmatch(char *s, char *t) } } if (test) { - fc = NULL; - if (addwhat == -7 && !(fc = findcmd(s))) + if (addwhat == -7 && !findcmd(s, 0)) return; - if (fc) - zsfree(fc); isf = CMF_FILE; if (addwhat == CC_FILES || addwhat == -6 || @@ -2515,7 +2690,6 @@ addmatch(char *s, char *t) } if (!test) return; - if (!ms && !ispattern && ai->firstm) { if ((test = sl - pfxlen(ai->firstm->str, s)) < ai->prerest) ai->prerest = test; @@ -2602,8 +2776,13 @@ addmatch(char *s, char *t) cm->str = (ms ? ms : s); cm->ipre = (ipre && *ipre ? ipre : NULL); cm->ripre = (ripre && *ripre ? ripre : NULL); - cm->pre = curcc->prefix; - cm->suf = curcc->suffix; + if (incompfunc) { + cm->pre = dupstring(curcc->prefix); + cm->suf = dupstring(curcc->suffix); + } else { + cm->pre = curcc->prefix; + cm->suf = curcc->suffix; + } cm->flags = mflags | isf; cm->brpl = bpl; cm->brsl = bsl; @@ -2729,7 +2908,7 @@ maketildelist(void) /* This does the check for compctl -x `n' and `N' patterns. */ /**/ -static int +int getcpat(char *str, int cpatindex, char *cpat, int class) { char *s, *t, *p; @@ -2951,6 +3130,8 @@ docompletion(char *s, int lst, int incmd) else if (nmatches == 1) { /* Only one match. */ + while (!amatches->mcount) + amatches = amatches->next; do_single(amatches->matches[0]); invalidatelist(); } @@ -3051,7 +3232,98 @@ makecomplist(char *s, int incmd, int lst) ccused = newlinklist(); ccstack = newlinklist(); - makecomplistglobal(s, incmd, lst); + if (compfunc) { + List list; + int lv = lastval; + + if ((list = getshfunc(compfunc)) != &dummy_list) { + LinkList args = newlinklist(); + char **p, *tmp; + int aadd = 0, usea = 1; + + addlinknode(args, compfunc); + + zsfree(compcontext); + zsfree(compcommand); + compcommand = ""; + if (inwhat == IN_MATH) { + if (insubscr) { + compcontext = "subscript"; + compcommand = varname ? varname : ""; + } else + compcontext = "math"; + usea = 0; + } else if (lincmd) + compcontext = (insubscr ? "subscript" : "command"); + else if (linredir) + compcontext = "redirect"; + else + switch (inwhat) { + case IN_ENV: + compcontext = "value"; + compcommand = varname; + usea = 0; + break; + case IN_COND: + compcontext = "condition"; + break; + default: + if (cmdstr) { + compcontext = "argument"; + compcommand = cmdstr; + } else { + compcontext = "value"; + if (clwords[0]) + compcommand = clwords[0]; + } + aadd = 1; + } + compcontext = ztrdup(compcontext); + tmp = quotename(compcommand, NULL, NULL, NULL); + untokenize(tmp); + compcommand = ztrdup(tmp); + if (usea && (!aadd || clwords[0])) + for (p = clwords + aadd; *p; p++) { + tmp = dupstring(*p); + untokenize(tmp); + addlinknode(args, tmp); + } + zsfree(compprefix); + zsfree(compsuffix); + if (unset(COMPLETEINWORD)) { + tmp = quotename(s, NULL, NULL, NULL); + untokenize(tmp); + compprefix = ztrdup(tmp); + compsuffix = ztrdup(""); + } else { + char *ss = s + offs, sav; + + tmp = quotename(s, &ss, NULL, NULL); + sav = *ss; + *ss = '\0'; + untokenize(tmp); + compprefix = ztrdup(tmp); + *ss = sav; + untokenize(ss); + compsuffix = ztrdup(ss); + } + zsfree(compiprefix); + compiprefix = ztrdup(""); + compcurrent = (usea ? (clwpos + 1 - aadd) : 1); + compnmatches = mnum; + incompfunc = 1; + startparamscope(); + makecompparamsptr(); + NEWHEAPS(compheap) { + doshfunc(compfunc, list, args, 0, 1); + } OLDHEAPS; + endparamscope(); + lastcmd = 9; + incompfunc = 0; + } + lastval = lv; + } else + makecomplistglobal(s, incmd, lst); endcmgroup(NULL); @@ -3082,6 +3354,83 @@ makecomplist(char *s, int incmd, int lst) return 1; } +/* This should probably be moved into tokenize(). */ + +static char * +ctokenize(char *p) +{ + char *r = p; + int bslash = 0; + + tokenize(p); + + for (p = r; *p; p++) { + if (*p == '\\') + bslash = 1; + else { + if (*p == '$') { + if (bslash) + p[-1] = Bnull; + else + *p = String; + } + bslash = 0; + } + } + return r; +} + +/**/ +char * +comp_str(int *ipl, int *pl) +{ + char *p = dupstring(compprefix); + char *s = dupstring(compsuffix); + char *ip = dupstring(compiprefix); + char *str; + int lp, ls, lip; + + ctokenize(p); + remnulargs(p); + ctokenize(s); + remnulargs(s); + ctokenize(ip); + remnulargs(ip); + ls = strlen(s); + lip = strlen(ip); + lp = strlen(p); + str = halloc(lip + lp + ls + 1); + strcpy(str, ip); + strcat(str, p); + strcat(str, s); + + if (ipl) + *ipl = lip; + if (pl) + *pl = lp; + + return str; +} + +/**/ +void +makecomplistcall(Compctl cc) +{ + SWITCHHEAPS(compheap) { + HEAPALLOC { + int ooffs = offs, lip, lp; + char *str = comp_str(&lip, &lp); + + offs = lip + lp; + cc->refc++; + ccont = 0; + makecomplistor(cc, str, lincmd, lip, 0); + offs = ooffs; + compnmatches = mnum; + } LASTALLOC; + } SWITCHBACKHEAPS; +} + /* This function gets the compctls for the given command line and * * adds all completions for them. */ @@ -3159,7 +3508,7 @@ makecomplistcmd(char *os, int incmd) /* If the command string starts with `=', try the path name of the * * command. */ if (cmdstr && cmdstr[0] == Equals) { - char *c = findcmd(cmdstr + 1); + char *c = findcmd(cmdstr + 1, 1); if (c) { zsfree(cmdstr); @@ -3191,7 +3540,7 @@ makecomplistpc(char *os, int incmd) { Patcomp pc; Comp pat; - char *s = findcmd(cmdstr); + char *s = findcmd(cmdstr, 1); for (pc = patcomps; pc; pc = pc->next) { if ((pat = parsereg(pc->pat)) && @@ -3468,12 +3817,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT)); - if (findnode(ccstack, cc)) + if (!incompfunc && findnode(ccstack, cc)) return; addlinknode(ccstack, cc); - if (allccs) { + if (!incompfunc && allccs) { if (findnode(allccs, cc)) { uremnode(ccstack, firstnode(ccstack)); return; @@ -4107,7 +4456,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) } /* This flag allows us to use read -l and -c. */ - incompctlfunc = 1; + if (!incompfunc) + incompctlfunc = 1; sfcontext = SFC_COMPLETE; /* Call the function. */ doshfunc(cc->func, list, args, 0, 1); @@ -4126,7 +4476,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) char *j, *jj; for (i = 0; i < MAXJOB; i++) - if (jobtab[i].stat & STAT_INUSE) { + if ((jobtab[i].stat & STAT_INUSE) && + jobtab[i].procs && jobtab[i].procs->text) { int stopped = jobtab[i].stat & STAT_STOPPED; j = jj = dupstring(jobtab[i].procs->text); @@ -4274,7 +4625,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) } /* No harm in allowing read -l and -c here, too */ - incompctlfunc = 1; + if (!incompfunc) + incompctlfunc = 1; sfcontext = SFC_COMPLETE; doshfunc(cc->ylist, list, args, 0, 1); sfcontext = osc; @@ -4528,10 +4880,12 @@ makearray(LinkList l, int s, int *np, int *nlp) for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--); ap = bp; /* Mark those, that would show the same string in the list. */ - for (; bp[1] && !strcmp((*ap)->str, (bp[1])->str); bp++) { - (bp[1])->flags |= CMF_NOLIST; nl++; - } + for (; bp[1] && !strcmp((*ap)->str, (bp[1])->str); bp++) + (bp[1])->flags |= CMF_NOLIST; } + for (ap = rp; *ap; ap++) + if ((*ap)->flags & CMF_NOLIST) + nl++; *cp = NULL; } if (np) @@ -4991,10 +5345,14 @@ do_single(Cmatch m) if (m->suf) { havesuff = 1; menuinsc = ztrlen(m->suf); - if (menuwe && (m->flags & CMF_REMOVE)) { - makesuffix(menuinsc); - if (menuinsc == 1) - suffixlen[m->suf[0]] = 1; + menulen -= menuinsc; + if (menuwe) { + menuend += menuinsc; + if (m->flags & CMF_REMOVE) { + makesuffix(menuinsc); + if (menuinsc == 1) + suffixlen[STOUC(m->suf[0])] = 1; + } } } else { /* There is no user-specified suffix, * @@ -5463,7 +5821,8 @@ listmatches(void) } if (n) { putc('\n', shout); - p = skipnolist(p + 1); + if (n && nl) + p = skipnolist(p + 1); } } } @@ -5671,7 +6030,7 @@ expandcmdpath(void) feep(); return; } - str = findcmd(s); + str = findcmd(s, 1); zsfree(s); if (!str) { feep(); @@ -5686,7 +6045,6 @@ expandcmdpath(void) cs += cmdwe - cmdwb + strlen(str); if (cs > ll) cs = ll; - zsfree(str); } /* Extra function added by AR Iano-Fletcher. */ diff --git a/Src/builtin.c b/Src/builtin.c index 6c41ce2fd..f6941286d 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -680,7 +680,7 @@ bin_cd(char *nam, char **argv, char *ops, int func) goto brk; } } while (*++s); - for (s = *argv; *++s; ops[*s] = 1); + for (s = *argv; *++s; ops[STOUC(*s)] = 1); } brk: chaselinks = ops['P'] || (isset(CHASELINKS) && !ops['L']); @@ -790,7 +790,7 @@ cd_get_dest(char *nam, char **argv, char *ops, int func) zsfree(remnode(dirstack, dir)); return NULL; } - if (dest != getdata(dir)) { + if (dest != (char *)getdata(dir)) { zsfree(getdata(dir)); setdata(dir, dest); } @@ -1224,13 +1224,14 @@ bin_fc(char *nam, char **argv, char *ops, int func) if (!editor) editor = DEFAULT_FCEDIT; - if (fcedit(editor, fil)) + if (fcedit(editor, fil)) { if (stuff(fil)) zwarnnam("fc", "%e: %s", s, errno); else { loop(0,1); retval = lastval; } + } } } unlink(fil); @@ -1464,6 +1465,117 @@ getasg(char *s) return &asg; } +/* function to set a single parameter */ + +/**/ +int +typeset_single(char *cname, char *pname, Param pm, int func, + int on, int off, int roff, char *value) +{ + int usepm, tc; + + /* use the existing pm? */ + usepm = pm && !(pm->flags & PM_UNSET); + + /* Always use an existing pm if special at current locallevel */ + if (pm && (pm->flags & PM_SPECIAL) && pm->level == locallevel) + usepm = 1; + + /* + * Don't use a non-special existing param if + * - the local level has changed, and + * - the function is not `export'. + */ + if (usepm && !(pm->flags & PM_SPECIAL) && + locallevel != pm->level && func != BIN_EXPORT) + usepm = 0; + + /* attempting a type conversion? */ + if ((tc = usepm && (((off & pm->flags) | (on & ~pm->flags)) & + (PM_INTEGER|PM_HASHED|PM_ARRAY)))) + usepm = 0; + if (tc && (pm->flags & PM_SPECIAL)) { + zerrnam(cname, "%s: can't change type of a special parameter", + pname, 0); + return 1; + } + + if (usepm) { + if (!on && !roff && !value) { + paramtab->printnode((HashNode)pm, 0); + return 0; + } + if ((pm->flags & PM_RESTRICTED && isset(RESTRICTED))) { + zerrnam(cname, "%s: restricted", pname, 0); + return 1; + } + if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) && + !(pm->flags & PM_READONLY & ~off)) + uniqarray((*pm->gets.afn) (pm)); + pm->flags = (pm->flags | on) & ~off; + /* This auxlen/pm->ct stuff is a nasty hack. */ + if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && + auxlen) + pm->ct = auxlen; + if (!(pm->flags & (PM_ARRAY|PM_HASHED))) { + if (pm->flags & PM_EXPORTED) { + if (!(pm->flags & PM_UNSET) && !pm->env && !value) + pm->env = addenv(pname, getsparam(pname)); + } else if (pm->env) { + delenv(pm->env); + zsfree(pm->env); + pm->env = NULL; + } + if (value) + setsparam(pname, ztrdup(value)); + } else if (value) { + zwarnnam(cname, "can't assign new value for array %s", pname, 0); + return 1; + } + return 0; + } + + /* + * We're here either because we're creating a new parameter, + * or we're adding a parameter at a different local level, + * or we're converting the type of a parameter. In the + * last case only, we need to delete the old parameter. + */ + if (tc) { + if (pm->flags & PM_READONLY) { + on |= ~off & PM_READONLY; + pm->flags &= ~PM_READONLY; + } + /* + * Try to carry over a value, but not when changing from, + * to, or between non-scalar types. + */ + if (!value && !((pm->flags|on) & (PM_ARRAY|PM_HASHED))) + value = dupstring(getsparam(pname)); + /* pname may point to pm->nam which is about to disappear */ + pname = dupstring(pname); + unsetparam_pm(pm, 0, 1); + } + /* + * Create a new node for a parameter with the flags in `on' minus the + * readonly flag + */ + pm = createparam(pname, on & ~PM_READONLY); + DPUTS(!pm, "BUG: parameter not created"); + pm->ct = auxlen; + if (func != BIN_EXPORT) + pm->level = locallevel; + if (value && !(pm->flags & (PM_ARRAY|PM_HASHED))) + setsparam(pname, ztrdup(value)); + pm->flags |= (on & PM_READONLY); + if (value && (pm->flags & (PM_ARRAY|PM_HASHED))) { + zerrnam(cname, "%s: can't assign initial value for array", pname, 0); + return 1; + } + + return 0; +} + /* declare, export, integer, local, readonly, typeset */ /**/ @@ -1475,7 +1587,7 @@ bin_typeset(char *name, char **argv, char *ops, int func) Comp com; char *optstr = "aiALRZlurtxU"; int on = 0, off = 0, roff, bit = PM_ARRAY; - int initon, initoff, of, i; + int i; int returnval = 0, printflags = 0; /* hash -f is really the builtin `functions' */ @@ -1486,9 +1598,9 @@ bin_typeset(char *name, char **argv, char *ops, int func) * Unfortunately, this depends on the order * * these flags are defined in zsh.h */ for (; *optstr; optstr++, bit <<= 1) - if (ops[*optstr] == 1) + if (ops[STOUC(*optstr)] == 1) on |= bit; - else if (ops[*optstr] == 2) + else if (ops[STOUC(*optstr)] == 2) off |= bit; roff = off; @@ -1521,7 +1633,11 @@ bin_typeset(char *name, char **argv, char *ops, int func) /* With the -m option, treat arguments as glob patterns */ if (ops['m']) { + MUSTUSEHEAP("typeset -m"); while ((asg = getasg(*argv++))) { + LinkList pmlist = newlinklist(); + LinkNode pmnode; + tokenize(asg->name); /* expand argument */ if (!(com = parsereg(asg->name))) { untokenize(asg->name); @@ -1529,143 +1645,45 @@ bin_typeset(char *name, char **argv, char *ops, int func) returnval = 1; continue; } - /* If no options or values are given, display all * - * parameters matching the glob pattern. */ - if (!(on || roff || asg->value)) { - scanmatchtable(paramtab, com, 0, 0, paramtab->printnode, 0); - continue; - } - /* Since either options or values are given, we search * - * through the parameter table and change all parameters * - * matching the glob pattern to have these flags and/or * - * value. */ + /* + * Search through the parameter table and change all parameters + * matching the glob pattern to have these flags and/or value. + * Bad news: if the parameter gets altered, e.g. by + * a type conversion, then paramtab can be shifted around, + * so we need to store the parameters to alter on a separate + * list for later use. + */ for (i = 0; i < paramtab->hsize; i++) { - for (pm = (Param) paramtab->nodes[i]; pm; pm = (Param) pm->next) { + for (pm = (Param) paramtab->nodes[i]; pm; + pm = (Param) pm->next) { if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) continue; - if (domatch(pm->nam, com, 0)) { - /* set up flags if we have any */ - if (on || roff) { - if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) && - !(pm->flags & PM_READONLY & ~off)) - uniqarray((*pm->gets.afn) (pm)); - if ((on & ~pm->flags) & PM_HASHED) { - char *nam = ztrdup(pm->nam); - unsetparam(nam); - pm = createparam(nam, on & ~PM_READONLY); - DPUTS(!pm, "BUG: parameter not created"); - } - pm->flags = (pm->flags | on) & ~off; - if (PM_TYPE(pm->flags) != PM_ARRAY && - PM_TYPE(pm->flags) != PM_HASHED) { - if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && auxlen) - pm->ct = auxlen; - /* did we just export this? */ - if ((pm->flags & PM_EXPORTED) && !pm->env) { - pm->env = addenv(pm->nam, (asg->value) ? asg->value : getsparam(pm->nam)); - } else if (!(pm->flags & PM_EXPORTED) && pm->env) { - /* did we just unexport this? */ - delenv(pm->env); - zsfree(pm->env); - pm->env = NULL; - } - } - } - /* set up a new value if given */ - if (asg->value) { - setsparam(pm->nam, ztrdup(asg->value)); - } - } + if (domatch(pm->nam, com, 0)) + addlinknode(pmlist, pm); } } + for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) { + pm = (Param) getdata(pmnode); + if (typeset_single(name, pm->nam, pm, func, on, off, roff, + asg->value)) + returnval = 1; + } } return returnval; } - /* Save the values of on, off, and func */ - initon = on; - initoff = off; - of = func; - /* Take arguments literally. Don't glob */ while ((asg = getasg(*argv++))) { - /* restore the original values of on, off, and func */ - on = initon; - off = initoff; - func = of; - on &= ~PM_ARRAY; - /* check if argument is a valid identifier */ if (!isident(asg->name)) { zerr("not an identifier: %s", asg->name, 0); returnval = 1; continue; } - bit = 0; /* flag for switching int<->not-int */ - if ((pm = (Param)paramtab->getnode(paramtab, asg->name)) && - (((pm->flags & PM_SPECIAL) && pm->level == locallevel) || - (!(pm->flags & PM_UNSET) && - ((locallevel == pm->level) || func == BIN_EXPORT) && - !(bit = (((off & pm->flags) | (on & ~pm->flags)) & - (PM_INTEGER|PM_HASHED)))))) { - /* if no flags or values are given, just print this parameter */ - if (!on && !roff && !asg->value) { - paramtab->printnode((HashNode) pm, 0); - continue; - } - if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { - zerrnam(name, "%s: restricted", pm->nam, 0); - returnval = 1; - continue; - } - if((pm->flags & PM_SPECIAL) && - PM_TYPE((pm->flags | on) & ~off) != PM_TYPE(pm->flags)) { - zerrnam(name, "%s: cannot change type of a special parameter", - pm->nam, 0); - returnval = 1; - continue; - } - if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) && - !(pm->flags & PM_READONLY & ~off)) - uniqarray((*pm->gets.afn) (pm)); - pm->flags = (pm->flags | on) & ~off; - if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && - auxlen) - pm->ct = auxlen; - if (PM_TYPE(pm->flags) != PM_ARRAY && - PM_TYPE(pm->flags) != PM_HASHED) { - if (pm->flags & PM_EXPORTED) { - if (!(pm->flags & PM_UNSET) && !pm->env && !asg->value) - pm->env = addenv(asg->name, getsparam(asg->name)); - } else if (pm->env) { - delenv(pm->env); - zsfree(pm->env); - pm->env = NULL; - } - if (asg->value) - setsparam(asg->name, ztrdup(asg->value)); - } - } else { - if (bit) { - if (pm->flags & PM_READONLY) { - on |= ~off & PM_READONLY; - pm->flags &= ~PM_READONLY; - } - if (!asg->value) - asg->value = dupstring(getsparam(asg->name)); - unsetparam(asg->name); - } - /* create a new node for a parameter with the * - * flags in `on' minus the readonly flag */ - pm = createparam(ztrdup(asg->name), on & ~PM_READONLY); - DPUTS(!pm, "BUG: parameter not created"); - pm->ct = auxlen; - if (func != BIN_EXPORT) - pm->level = locallevel; - if (asg->value) - setsparam(asg->name, ztrdup(asg->value)); - pm->flags |= (on & PM_READONLY); - } + if (typeset_single(name, asg->name, + (Param)paramtab->getnode(paramtab, asg->name), + func, on, off, roff, asg->value)) + returnval = 1; } return returnval; } @@ -1989,7 +2007,7 @@ bin_whence(char *nam, char **argv, char *ops, int func) puts(wd ? ": none" : " not found"); returnval = 1; } - } else if ((cnam = findcmd(*argv))) { + } else if ((cnam = findcmd(*argv, 1))) { /* Found external command. */ if (wd) { printf("%s: command\n", *argv); @@ -2001,7 +2019,6 @@ bin_whence(char *nam, char **argv, char *ops, int func) print_if_link(cnam); fputc('\n', stdout); } - zsfree(cnam); } else { /* Not found at all. */ if (v || csh || wd) @@ -2328,9 +2345,17 @@ bin_print(char *name, char **args, char *ops, int func) args[n] = getkeystring(args[n], &len[n], func != BIN_ECHO && !ops['e'], &nnl); /* -P option -- interpret as a prompt sequence */ - if(ops['P']) - args[n] = unmetafy(promptexpand(metafy(args[n], len[n], - META_NOALLOC), 0, NULL, NULL), &len[n]); + if(ops['P']) { + /* + * promptexpand uses permanent storage: to avoid + * messy memory management, stick it on the heap + * instead. + */ + char *str = unmetafy(promptexpand(metafy(args[n], len[n], + META_NOALLOC), 0, NULL, NULL), &len[n]); + args[n] = dupstring(str); + free(str); + } /* -D option -- interpret as a directory, and use ~ */ if(ops['D']) { Nameddir d = finddir(args[n]); @@ -2778,8 +2803,9 @@ zexit(int val, int from_signal) LASTALLOC_RETURN; } } - if (in_exit++ && from_signal) + if (in_exit++ && from_signal) { LASTALLOC_RETURN; + } if (isset(MONITOR)) /* send SIGHUP to any jobs left running */ killrunjobs(from_signal); @@ -3181,13 +3207,14 @@ bin_read(char *name, char **args, char *ops, int func) } if (c == EOF || (c == '\n' && !zbuf)) break; - if (!bslash && isep(c) && bptr == buf) + if (!bslash && isep(c) && bptr == buf) { if (iwsep(c)) continue; else if (!first) { first = 1; continue; } + } bslash = c == '\\' && !bslash && !ops['r']; if (bslash) continue; @@ -3240,7 +3267,7 @@ zread(void) char cc, retry = 0; /* use zbuf if possible */ - if (zbuf) + if (zbuf) { /* If zbuf points to anything, it points to the next character in the buffer. This may be a null byte to indicate EOF. If reading from the buffer, move on the buffer pointer. */ @@ -3248,6 +3275,7 @@ zread(void) return zbuf++, STOUC(*zbuf++ ^ 32); else return (*zbuf) ? STOUC(*zbuf++) : EOF; + } for (;;) { /* read a character from readfd */ switch (read(readfd, &cc, 1)) { diff --git a/Src/cond.c b/Src/cond.c index 906e81938..a46833f66 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -54,7 +54,7 @@ evalcond(Cond c) int l = arrlen((char **) c->right); if (l < cd->min || (cd->max >= 0 && l > cd->max)) { - zerr("unrecognized condition: `-%s'", (char *) c->left, 0); + zerr("unrecognized condition: `%s'", (char *) c->left, 0); return 0; } } @@ -68,13 +68,13 @@ evalcond(Cond c) int l = arrlen(a); if (l < cd->min || (cd->max >= 0 && l > cd->max)) { - zerr("unrecognized condition: `-%s'", (char *) c->left, 0); + zerr("unrecognized condition: `%s'", (char *) c->left, 0); return 0; } a[0] = (char *) c->left; return cd->handler(a, cd->condid); } else - zerr("unrecognized condition: `-%s'", (char *) c->left, 0); + zerr("unrecognized condition: `%s'", (char *) c->left, 0); } return 0; } diff --git a/Src/exec.c b/Src/exec.c index 911559a02..e563db6a4 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -457,13 +457,17 @@ execute(Cmdnam not_used_yet, int dash) _exit(1); } -#define try(X) { if (iscom(X)) return ztrdup(X); } +#define RET_IF_COM(X) { if (iscom(X)) return docopy ? dupstring(X) : arg0; } -/* get the full pathname of an external command */ +/* + * Get the full pathname of an external command. + * If the second argument is zero, return the first argument if found; + * if non-zero, return the path using heap memory. (RET_IF_COM(X), above). + */ /**/ char * -findcmd(char *arg0) +findcmd(char *arg0, int docopy) { char **pp; char *z, *s, buf[MAXCMDLEN]; @@ -476,7 +480,7 @@ findcmd(char *arg0) return NULL; for (s = arg0; *s; s++) if (*s == '/') { - try(arg0); + RET_IF_COM(arg0); if (arg0 == s || unset(PATHDIRS)) { return NULL; } @@ -496,13 +500,13 @@ findcmd(char *arg0) *z++ = '/'; } strcpy(z, arg0); - try(buf); + RET_IF_COM(buf); } strcpy(nn, cn->u.name ? *(cn->u.name) : ""); strcat(nn, "/"); strcat(nn, cn->nam); } - try(nn); + RET_IF_COM(nn); } for (pp = path; *pp; pp++) { z = buf; @@ -511,7 +515,7 @@ findcmd(char *arg0) *z++ = '/'; } strcpy(z, arg0); - try(buf); + RET_IF_COM(buf); } return NULL; } diff --git a/Src/glob.c b/Src/glob.c index 9c1d08aa4..6536e0f06 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -2318,7 +2318,7 @@ doesmatch(Comp c) for (; *pptr; pptr++) { if (*pptr == Meta) pptr++; - else if (CHARMATCH(*pptr, looka)) + else if (CHARMATCH(STOUC(*pptr), STOUC(looka))) break; } if (!*(saves = pptr)) @@ -2688,7 +2688,7 @@ matchonce(Comp c) } continue; } - if (CHARMATCH(*pptr, *pat)) { + if (CHARMATCH(STOUC(*pptr), STOUC(*pat))) { /* just plain old characters */ pptr++; pat++; diff --git a/Src/hashtable.c b/Src/hashtable.c index 60fb3df80..b816f1892 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -172,7 +172,7 @@ addhashnode(HashTable ht, char *nam, void *nodeptr) ht->nodes[hashval] = hn; replacing: hn->next = hp->next; - if(ht->scan) + if(ht->scan) { if(ht->scan->sorted) { HashNode *tab = ht->scan->u.s.tab; int i; @@ -181,6 +181,7 @@ addhashnode(HashTable ht, char *nam, void *nodeptr) tab[i] = hn; } else if(ht->scan->u.u == hp) ht->scan->u.u = hn; + } ht->freenode(hp); return; } @@ -270,7 +271,7 @@ removehashnode(HashTable ht, char *nam) ht->nodes[hashval] = hp->next; gotit: ht->ct--; - if(ht->scan) + if(ht->scan) { if(ht->scan->sorted) { HashNode *tab = ht->scan->u.s.tab; int i; @@ -279,6 +280,7 @@ removehashnode(HashTable ht, char *nam) tab[i] = NULL; } else if(ht->scan->u.u == hp) ht->scan->u.u = hp->next; + } return hp; } diff --git a/Src/hist.c b/Src/hist.c index a4c5735c1..d77b2febd 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -392,7 +392,7 @@ histsubchar(int c) c = ingetc(); } *ptr = 0; - if (!*buf) + if (!*buf) { if (c != '%') { if (isset(CSHJUNKIEHISTORY)) ev = curhist - 1; @@ -408,6 +408,7 @@ histsubchar(int c) else ev = defev; evset = 0; + } } else if ((t0 = atoi(buf))) { ev = (t0 < 0) ? curhist + t0 : t0; evset = 1; @@ -749,11 +750,12 @@ hend(void) save = 0; else { *hptr = '\0'; - if (hptr[-1] == '\n') + if (hptr[-1] == '\n') { if (chline[1]) { *--hptr = '\0'; } else save = 0; + } if (!*chline || !strcmp(chline, "\n") || (isset(HISTIGNORESPACE) && spaceflag)) save = 0; diff --git a/Src/input.c b/Src/input.c index 45c65b2bf..66a21000c 100644 --- a/Src/input.c +++ b/Src/input.c @@ -219,7 +219,7 @@ inputline(void) char *ingetcline, *ingetcpmptl = NULL, *ingetcpmptr = NULL; /* If reading code interactively, work out the prompts. */ - if (interact && isset(SHINSTDIN)) + if (interact && isset(SHINSTDIN)) { if (!isfirstln) ingetcpmptl = prompt2; else { @@ -227,6 +227,7 @@ inputline(void) if (rprompt) ingetcpmptr = rprompt; } + } if (!(interact && isset(SHINSTDIN) && SHTTY != -1 && isset(USEZLE))) { /* * If not using zle, read the line straight from the input file. diff --git a/Src/jobs.c b/Src/jobs.c index 74fa8cda0..e94dd77e2 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -462,7 +462,7 @@ printjob(Job jn, int lng, int synch) if (jn->stat & STAT_SUPERJOB && jn->procs->status == SP_RUNNING && !pn->next) pn->status = SP_RUNNING; - if (pn->status != SP_RUNNING) + if (pn->status != SP_RUNNING) { if (WIFSIGNALED(pn->status)) { sig = WTERMSIG(pn->status); llen = strlen(sigmsg[sig]); @@ -483,6 +483,7 @@ printjob(Job jn, int lng, int synch) } else if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && WEXITSTATUS(pn->status)) sflag = 1; + } } /* print if necessary */ @@ -508,7 +509,7 @@ printjob(Job jn, int lng, int synch) break; len2 += strlen(qn->text) + 2; } - if (job != thisjob) + if (job != thisjob) { if (fline) fprintf(fout, "[%ld] %c ", (long)(jn - jobtab), @@ -516,7 +517,7 @@ printjob(Job jn, int lng, int synch) : (job == prevjob) ? '-' : ' '); else fprintf(fout, (job > 9) ? " " : " "); - else + } else fprintf(fout, "zsh: "); if (lng & 1) fprintf(fout, "%ld ", (long) pn->pid); @@ -531,18 +532,19 @@ printjob(Job jn, int lng, int synch) lng &= ~3; } else fprintf(fout, "%*s", skip, ""); - if (pn->status == SP_RUNNING) + if (pn->status == SP_RUNNING) { if (!conted) fprintf(fout, "running%*s", len - 7 + 2, ""); else fprintf(fout, "continued%*s", len - 9 + 2, ""); - else if (WIFEXITED(pn->status)) + } + else if (WIFEXITED(pn->status)) { if (WEXITSTATUS(pn->status)) fprintf(fout, "exit %-4d%*s", WEXITSTATUS(pn->status), len - 9 + 2, ""); else fprintf(fout, "done%*s", len - 4 + 2, ""); - else if (WIFSTOPPED(pn->status)) + } else if (WIFSTOPPED(pn->status)) fprintf(fout, "%-*s", len + 2, sigmsg[WSTOPSIG(pn->status)]); else if (WCOREDUMP(pn->status)) fprintf(fout, "%s (core dumped)%*s", @@ -1071,7 +1073,7 @@ bin_fg(char *name, char **argv, char *ops, int func) /* If you immediately type "exit" after "jobs", this * * will prevent zexit from complaining about stopped jobs */ stopmsg = 2; - if (!*argv) + if (!*argv) { /* This block handles all of the default cases (no arguments). bg, fg and disown act on the current job, and jobs and wait act on all the jobs. */ @@ -1100,6 +1102,7 @@ bin_fg(char *name, char **argv, char *ops, int func) waitjob(job, SIGINT); return 0; } + } /* Defaults have been handled. We now have an argument or two, or three... In the default case for bg, fg and disown, the argument will be provided by diff --git a/Src/lex.c b/Src/lex.c index 6f4f2dd20..b08dfed5b 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -457,11 +457,11 @@ add(int c) #define SETPARBEGIN {if (zleparse && !(inbufflags & INP_ALIAS) && cs >= ll+1-inbufct) parbegin = inbufct;} #define SETPAREND {\ - if (zleparse && !(inbufflags & INP_ALIAS) && parbegin != -1 && parend == -1)\ + if (zleparse && !(inbufflags & INP_ALIAS) && parbegin != -1 && parend == -1) {\ if (cs >= ll + 1 - inbufct)\ parbegin = -1;\ else\ - parend = inbufct;} + parend = inbufct;} } static int cmd_or_math(int cs_type) @@ -823,20 +823,22 @@ gettokstr(int c, int sub) case LX2_OUTPAR: if ((sub || in_brace_param) && isset(SHGLOB)) break; - if (!in_brace_param && !pct--) + if (!in_brace_param && !pct--) { if (sub) { pct = 0; break; } else goto brk; + } c = Outpar; break; case LX2_BAR: - if (!pct && !in_brace_param) + if (!pct && !in_brace_param) { if (sub) break; else goto brk; + } if (unset(SHGLOB) || (!sub && !in_brace_param)) c = Bar; break; @@ -912,8 +914,9 @@ gettokstr(int c, int sub) *bptr = '\0'; return STRING; } - if (in_brace_param) + if (in_brace_param) { cmdpush(CS_BRACE); + } bct++; } break; @@ -922,8 +925,9 @@ gettokstr(int c, int sub) break; if (!bct) break; - if (in_brace_param) + if (in_brace_param) { cmdpop(); + } if (bct-- == in_brace_param) in_brace_param = 0; c = Outbrace; @@ -933,11 +937,12 @@ gettokstr(int c, int sub) c = Comma; break; case LX2_OUTANG: - if (!intpos) + if (!intpos) { if (in_brace_param || sub) break; else goto brk; + } e = hgetc(); if (e != '(') { hungetc(e); @@ -1101,11 +1106,12 @@ gettokstr(int c, int sub) break; } add(c); - if (c == '\'') + if (c == '\'') { if ((inquote = !inquote)) STOPHIST else ALLOWHIST + } } if (inquote) ALLOWHIST @@ -1260,8 +1266,9 @@ dquote_parse(char endchar, int sub) } if (intick == 2) ALLOWHIST - if (intick) + if (intick) { cmdpop(); + } while (bct--) cmdpop(); if (lexstop) diff --git a/Src/loop.c b/Src/loop.c index 5fbf2b841..95ec4832a 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -52,7 +52,7 @@ execfor(Cmd cmd) List list; Forcmd node; char *str; - int val; + int val = 0; LinkList args; node = cmd->u.forcmd; diff --git a/Src/mem.c b/Src/mem.c index 32822ab8c..703920215 100644 --- a/Src/mem.c +++ b/Src/mem.c @@ -129,25 +129,51 @@ global_permalloc(void) return luh; } -/* heappush saves the current heap state using this structure */ +/* list of zsh heap */ -struct heapstack { - struct heapstack *next; /* next one in list for this heap */ - size_t used; -}; +static Heap heaps; -/* A zsh heap. */ +/* Use new heaps from now on. This returns the old heap-list. */ -struct heap { - struct heap *next; /* next one */ - size_t used; /* bytes used from the heap */ - struct heapstack *sp; /* used by pushheap() to save the value used */ -#define arena(X) ((char *) (X) + sizeof(struct heap)) -}; +/**/ +Heap +new_heaps(void) +{ + Heap h = heaps; -/* list of zsh heap */ + heaps = NULL; -static Heap heaps; + return h; +} + +/* Re-install the old heaps again, freeing the new ones. */ + +/**/ +void +old_heaps(Heap old) +{ + Heap h, n; + + for (h = heaps; h; h = n) { + n = h->next; + DPUTS(h->sp, "BUG: old_heaps() with pushed heaps"); + zfree(h, sizeof(*h)); + } + heaps = old; +} + +/* Temporarily switch to other heaps (or back again). */ + +/**/ +Heap +switch_heaps(Heap new) +{ + Heap h = heaps; + + heaps = new; + + return h; +} /* save states of zsh heaps */ diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh index 2f988ec8f..efaa803ad 100644 --- a/Src/mkbltnmlst.sh +++ b/Src/mkbltnmlst.sh @@ -24,11 +24,17 @@ for x_mod in $x_mods; do *" $x_mod "*) ;; *) echo "/* non-linked-in known module \`$x_mod' */" eval "loc=\$loc_$x_mod" - unset moddeps autobins + unset moddeps autobins autoinfixconds autoprefixconds . $srcdir/../$loc/${x_mod}.mdd for bin in $autobins; do echo " add_autobin(\"$bin\", \"$x_mod\");" done + for cond in $autoinfixconds; do + echo " add_autocond(\"$cond\", 1, \"$x_mod\");" + done + for cond in $autoprefixconds; do + echo " add_autocond(\"$cond\", 0, \"$x_mod\");" + done for dep in $moddeps; do case $bin_mods in *" $dep "*) diff --git a/Src/mkmakemod.sh b/Src/mkmakemod.sh index 9c90b75e5..2f79acbc3 100644 --- a/Src/mkmakemod.sh +++ b/Src/mkmakemod.sh @@ -17,15 +17,18 @@ # defines one module. The .mdd file is actually a shell script, which will # be sourced. It may define the following shell variables: # -# moddeps modules on which this module depends (default none) -# nozshdep non-empty indicates no dependence on the `zsh' pseudo-module -# alwayslink if non-empty, always link the module into the executable -# autobins builtins defined by the module, for autoloading -# objects .o files making up this module (*must* be defined) -# proto .pro files for this module (default generated from $objects) -# headers extra headers for this module (default none) -# hdrdeps extra headers on which the .mdh depends (default none) -# otherincs extra headers that are included indirectly (default none) +# moddeps modules on which this module depends (default none) +# nozshdep non-empty indicates no dependence on the `zsh' pseudo-module +# alwayslink if non-empty, always link the module into the executable +# autobins builtins defined by the module, for autoloading +# autoinfixconds infix condition codes defined by the module, for +# autoloading (without the leading `-') +# autoprefixconds like autoinfixconds, but for prefix condition codes +# objects .o files making up this module (*must* be defined) +# proto .pro files for this module (default generated from $objects) +# headers extra headers for this module (default none) +# hdrdeps extra headers on which the .mdh depends (default none) +# otherincs extra headers that are included indirectly (default none) # # The .mdd file may also include a Makefile.in fragment between lines # `:<<\Make' and `Make' -- this will be copied into Makemod.in. @@ -167,7 +170,7 @@ if $first_stage; then for module in $here_modules; do unset moddeps nozshdep alwayslink hasexport - unset autobins + unset autobins autoinfixconds autoprefixconds unset objects proto headers hdrdeps otherincs . $top_srcdir/$the_subdir/${module}.mdd test -n "${moddeps+set}" || moddeps= diff --git a/Src/module.c b/Src/module.c index 5780eb134..f91650a7f 100644 --- a/Src/module.c +++ b/Src/module.c @@ -227,7 +227,6 @@ deletewrapper(Module m, FuncWrap w) static char *dlerrstr[256]; -/**/ static void * load_and_bind(const char *fn) { @@ -277,7 +276,6 @@ load_and_bind(const char *fn) # define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) # define dlclose(handle) shl_unload((shl_t)(handle)) -/**/ static void * hpux_dlsym(void *handle, char *name) diff --git a/Src/params.c b/Src/params.c index 89a1ed6a1..3dc7e1a1b 100644 --- a/Src/params.c +++ b/Src/params.c @@ -765,7 +765,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) } else if (rev) { v->isarr |= SCANPM_WANTVALS; } - if (!down) + if (!down && PM_TYPE(v->pm->flags) == PM_HASHED) v->isarr &= ~SCANPM_MATCHMANY; *inv = ind; } @@ -1534,6 +1534,12 @@ setaparam(char *s, char **val) if (!(v = getvalue(&s, 1))) createparam(t, PM_ARRAY); *ss = '['; + if (PM_TYPE(v->pm->flags) == PM_HASHED) { + zerr("attempt to set slice of associative array", NULL, 0); + freearray(val); + errflag = 1; + return NULL; + } v = NULL; } else { if (!(v = getvalue(&s, 1))) @@ -1571,13 +1577,13 @@ sethparam(char *s, char **val) } if (strchr(s, '[')) { freearray(val); - zerr("attempt to set slice of associative array", NULL, 0); + zerr("nested associative arrays not yet supported", NULL, 0); errflag = 1; return NULL; } else { if (!(v = getvalue(&s, 1))) createparam(t, PM_HASHED); - else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) && + else if (!(PM_TYPE(v->pm->flags) & PM_HASHED) && !(v->pm->flags & PM_SPECIAL)) { unsetparam(t); createparam(t, PM_HASHED); diff --git a/Src/subst.c b/Src/subst.c index cc7e87d3d..5211ee0ee 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -387,11 +387,11 @@ filesubstr(char **namptr, int assign) for (pp = str + 1; !isend2(*pp); pp++); sav = *pp; *pp = 0; - if (!(cnam = findcmd(str + 1))) { + if (!(cnam = findcmd(str + 1, 1))) { Alias a = (Alias) aliastab->getnode(aliastab, str + 1); if (a) - cnam = ztrdup(a->text); + cnam = a->text; else { if (isset(NOMATCH)) zerr("%s not found", str + 1, 0); @@ -399,7 +399,6 @@ filesubstr(char **namptr, int assign) } } *namptr = dupstring(cnam); - zsfree(cnam); if (sav) { *pp = sav; *namptr = dyncat(*namptr, pp); diff --git a/Src/watch.c b/Src/watch.c index 2532e0a63..6fe77afcd 100644 --- a/Src/watch.c +++ b/Src/watch.c @@ -235,7 +235,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) # endif /* WATCH_UTMP_UT_HOST */ while (*fmt) - if (*fmt == '\\') + if (*fmt == '\\') { if (*++fmt) { if (prnt) putchar(*fmt); @@ -244,6 +244,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) return fmt; else break; + } else if (*fmt == fini) return ++fmt; else if (*fmt != '%') { diff --git a/Src/zsh.export b/Src/zsh.export index d8b0ddf19..c85bfbad4 100644 --- a/Src/zsh.export +++ b/Src/zsh.export @@ -23,6 +23,7 @@ closem cmdnamtab columns compctlreadptr +cond_match cond_str cond_val coprocin @@ -104,6 +105,8 @@ inredir insertlinknode install_handler intr +intvargetfn +intvarsetfn inwhat isfirstln jobtab @@ -126,6 +129,7 @@ mypgrp mypid nameddirtab ncalloc +new_heaps newhashtable newlinklist nicechar @@ -137,6 +141,7 @@ noerrs noholdintr noop_function noop_function_int +old_heaps optiontab opts paramtab @@ -148,6 +153,7 @@ path pathchecked popheap postedit +pparams ppid prefork prepromptfns @@ -197,6 +203,9 @@ strpfx strsfx strucpy struncpy +strvargetfn +strvarsetfn +switch_heaps tclen tcstr termflags diff --git a/Src/zsh.h b/Src/zsh.h index a31a7469b..dabcd90c8 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1305,6 +1305,22 @@ struct ttyinfo { * Memory management * *********************/ +/* heappush saves the current heap state using this structure */ + +struct heapstack { + struct heapstack *next; /* next one in list for this heap */ + size_t used; +}; + +/* A zsh heap. */ + +struct heap { + struct heap *next; /* next one */ + size_t used; /* bytes used from the heap */ + struct heapstack *sp; /* used by pushheap() to save the value used */ +#define arena(X) ((char *) (X) + sizeof(struct heap)) +}; + #ifndef DEBUG # define HEAPALLOC do { int nonlocal_useheap = global_heapalloc(); do @@ -1318,6 +1334,13 @@ struct ttyinfo { # define LASTALLOC_RETURN \ if ((nonlocal_useheap ? global_heapalloc() : \ global_permalloc()), 0) {;} else return + +# define NEWHEAPS(h) do { Heap oldheaps = h = new_heaps(); do +# define OLDHEAPS while (0); old_heaps(oldheaps); } while (0); + +# define SWITCHHEAPS(h) do { Heap oldheaps = switch_heaps(h); do +# define SWITCHBACKHEAPS while (0); switch_heaps(oldheaps); } while (0); + #else # define HEAPALLOC do { int nonlocal_useheap = global_heapalloc(); \ alloc_stackp++; do @@ -1333,6 +1356,17 @@ struct ttyinfo { # define LASTALLOC_RETURN \ if ((nonlocal_useheap ? global_heapalloc() : \ global_permalloc()),alloc_stackp--,0){;}else return + +# define NEWHEAPS(h) do { Heap oldheaps = h = new_heaps(); \ + alloc_stackp++; do +# define OLDHEAPS while (0); alloc_stackp--; \ + old_heaps(oldheaps); } while (0); + +# define SWITCHHEAPS(h) do { Heap oldheaps = switch_heaps(h); \ + alloc_stackp++; do +# define SWITCHBACKHEAPS while (0); alloc_stackp--; \ + switch_heaps(oldheaps); } while (0); + #endif /****************/ diff --git a/Util/zsh-development-guide b/Util/zsh-development-guide index 73ac120ab..f779b17b3 100644 --- a/Util/zsh-development-guide +++ b/Util/zsh-development-guide @@ -120,15 +120,18 @@ Modules are described by a file named `foo.mdd' for a module is build. To describe the module it can/should set the following shell variables: - - moddeps modules on which this module depends (default none) - - nozshdep non-empty indicates no dependence on the `zsh' pseudo-module - - alwayslink if non-empty, always link the module into the executable - - autobins builtins defined by the module, for autoloading - - objects .o files making up this module (*must* be defined) - - proto .pro files for this module (default generated from $objects) - - headers extra headers for this module (default none) - - hdrdeps extra headers on which the .mdh depends (default none) - - otherincs extra headers that are included indirectly (default none) + - moddeps modules on which this module depends (default none) + - nozshdep non-empty indicates no dependence on the `zsh' pseudo-module + - alwayslink if non-empty, always link the module into the executable + - autobins builtins defined by the module, for autoloading + - autoinfixconds infix condition codes defined by the module, for + autoloading (without the leading `-') + - autoprefixconds like autoinfixconds, but for prefix condition codes + - objects .o files making up this module (*must* be defined) + - proto .pro files for this module (default generated from $objects) + - headers extra headers for this module (default none) + - hdrdeps extra headers on which the .mdh depends (default none) + - otherincs extra headers that are included indirectly (default none) Be sure to put the values in quotes. For further enlightenment have a look at the `mkmakemod.sh' script in the Src directory of the diff --git a/aczsh.m4 b/aczsh.m4 index dfc40ce6f..08c167d2f 100644 --- a/aczsh.m4 +++ b/aczsh.m4 @@ -45,7 +45,7 @@ else fi echo ' extern char **environ; - void *symlist1[] = { + void *symlist1[[]] = { (void *)&environ, (void *)0 }; diff --git a/configure.in b/configure.in index 4fc45fe7c..1b2692ea1 100644 --- a/configure.in +++ b/configure.in @@ -340,7 +340,7 @@ AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ termios.h sys/param.h sys/filio.h string.h memory.h \ limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \ locale.h errno.h stdlib.h unistd.h sys/capability.h \ - utmp.h utmpx.h sys/types.h pwd.h grp.h) + utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h) if test $dynamic = yes; then AC_CHECK_HEADERS(dlfcn.h) AC_CHECK_HEADERS(dl.h) @@ -467,9 +467,22 @@ elif test $zsh_cv_decl_ospeed_must_define = yes; then fi dnl Check if tgetent accepts NULL (and will allocate its own termcap buffer) +dnl Some termcaps reportedly accept a zero buffer, but then dump core +dnl in tgetstr(). AC_CACHE_CHECK(if tgetent accepts NULL, zsh_cv_func_tgetent_accepts_null, -[AC_TRY_RUN([main(){int i = tgetent((char*)0,"vt100");exit(!i || i == -1);}], +[AC_TRY_RUN([ +main() +{ + int i = tgetent((char*)0,"vt100"); + if (i > 0) { + char tbuf[1024], *u; + u = tbuf; + tgetstr("cl", &u); + } + exit(!i || i == -1); +} +], zsh_cv_func_tgetent_accepts_null=yes, zsh_cv_func_tgetent_accepts_null=no, zsh_cv_func_tgetent_accepts_null=no)]) @@ -615,7 +628,7 @@ dnl need to integrate this function dnl AC_FUNC_STRFTIME AC_CHECK_FUNCS(memcpy memmove \ - strftime waitpid select tcsetpgrp tcgetattr strstr lstat \ + strftime waitpid select poll tcsetpgrp tcgetattr strstr lstat \ getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \ sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \ sigprocmask setuid seteuid setreuid setresuid setsid strerror \ diff --git a/patchlist.txt b/patchlist.txt index 2179665ca..6ec3ec351 100644 --- a/patchlist.txt +++ b/patchlist.txt @@ -213,3 +213,56 @@ Sven's ignored character fix, 4828 More Sven condition patches, 4837, 4842 Final (???) isident() fix from Sven, 4845 + + pws-5 + +Name of top level directory is now zsh-3.1.5-pws-5 + +Missing part of Bart's sethparam() changes, 4851 + +zftp test subcommand, 4852 + +Geoff's refresh fix for a line the same length as the terminal width, +4855 + +Bart's fix for array slices, 4874 + +Sven's accept-and-menu-complete-fix, 4878 + +Sven's group completion fix, 4879 + +Sven's module condition fixes, 4880 + +Oliver Kiddle's autoconf fix, 4887 + +My zftp fix (actually due to Andrej Borsenkow) for systems which only +allow dup'ing sockets after they are connected, 4888. + +Bart's fix to making setting associative array elements inside +substitutions consistent, 4893 + +My typeset neatness and -a and -m fix, 4902 + +My brief Etc/MACHINES addition, 4912 + +My modification to findcmd() for memory leaks, 4923, plus comment +alteration by Bart, 4924 + +Sven's patch for completion after various reserved words, 4930 + +My patch for compiler warnings, 4931 + +My configuration fix for when tgetent() accepts a null argument but +then tgetstr() dumps core, 4939 + +Sven's alteration of `-t' behaviour, 4940. This is slightly +incompatible with previous patched versions of 3.1.5 since now you don't +need '-tc' with -T. However, you now do need '-tn' in cases where you +don't want normal completion tried after a -T matches. + +Sven's new completion functions, 4850, 4881, 4941, 4942, 4943, 4944, +4946, 4949, plus my addition of function pointers, 4945. The example +file is now in Misc/new-completion-examples. + +(Effect of) fix from Helmut Jarausch in 4947 partly due to change +missed in patch. -- cgit 1.4.1