diff options
50 files changed, 822 insertions, 296 deletions
diff --git a/ChangeLog b/ChangeLog index 963e4ec89..feea8c23f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,146 @@ +1999-07-19 Peter Stephenson <pws@ibmth.df.unipi.it> + + * pws: version 3.1.6-test-2 + + * pws: 7192: Src/glob.c: bug with null in pattern if at end of + test string (matched as if a real character). + +1999-07-18 Peter Stephenson <pws@ibmth.df.unipi.it> + + * pws: 7185: Src/glob.c: don't use strcoll() for character + ranges [...] because it can have side effects. + + * pws: 7184: Src/lex.c: histactive didn't get get zeroed before + non-interactive history use, hence interactive lines with + remhist() were junked more than once. + + * Wayne: 7181: Doc/Zsh/options.y, Doc/Zsh/params.yo: history docs. + + * Wayne: 7180: Src/Zle/complist.c, Src/utils.c: warnings. + +1999-07-16 Peter Stephenson <pws@ibmth.df.unipi.it> + + * pws: 7172: Doc/Zsh/options.yo, Src/builtin.c, Src/exec.c, + Src/options.c, Src/utils.c, Src/zsh.h: minor bugs with + 7164 fixed; CHASE_DOTS resolves ..'s to physical path; + CHASE_LINKS doesn't do logical path rationalization; + xsymlink() and xsymlinks() simplified and option-dependence + removed. + + * Sven: 7171: Src/builtin.c: alternate form of Digital/gcc + bug workaround. + +1999-07-15 Peter Stephenson <pws@ibmth.df.unipi.it> + + * pws: 7164: Src/builtin.c, Src/exec.c: AUTOCD now allows + paths with symlinks (as real cd always did); simplify code + for testing for existing directory on foo/.. before removing + foo/..; as a side effect, relative cd's from directory which + has been deleted don't work. + + * Sven: 7161: Src/Zle/comp.h, Src/Zle/complist.c, + Src/Zle/zle_tricky.c, Completion/Base/_brace_parameter: + be more careful with quote-prefix/suffix and path-prefix/suffix + especially with accept-and-menu-complete. + + * pws: 7155: Functions/Zftp/zfgoto, Functions/Zftp/zfinit: + zfgoto can cd without needing to re-login; zfinit uses + zmodload -e to check for zftp. + + * Sven: 7154: Src/Zle/complist.c, Src/Zle/zle_refresh.c, + Src/Zle/zle_tricky.c, Doc/Zsh/mod_complist.yo: don't + list too many times on ambiguous completion; don't do + menu-selection if no alwayslastprompt behaviour. + +1999-07-14 Peter Stephenson <pws@ibmth.df.unipi.it> + + * pws: 7148: INSTALL: User subdirectory + + * Thomas Köhler: 7146: Completion/User/_make: didn't work. + + * pws: 7145: Completion/User/_sh: use compset -q for completing + after -c option + + * Sven: 7143: Src/Zle/complist.c, Src/Zle/zle_tricky.c: + listing got confused with only hidden matches. + + * Sven: 7141: Completion/Core/_path_files: if there are + no completions in a valid directory when we have a path ending + in /, use the directory as a completion, to avoid + correction/approximation of the existing directory. + + * pws: 7139: Src/builtin.c: not particularly pleasant fix + to problem that `cd nonexistent/..' silently did nothing, while + making `cd ..' work even if current directory has gone. + + * Sven: 7138: Completion/User/_hosts, Completion/User/_x_options: + _hosts passes arguments as options to compadd. + + * Oliver: 7136: Completion/User/_rlogin, + Completion/User/_x_options: use _hosts. + + * Sven: 7135: Src/Zle/zle_tricky.c, Completion/Core/_path_files: + change quoting of files again. + + * Sven: 7133: Doc/Zsh/expn.yo, Doc/Zsh/mod_complist.yo: + ZLS_COLOURS not required for complist to work. + + * pws: 7127: configure.in: help now shows --disable-dynamic + and --disable-lfs. + + * Sven: 7126: Src/Zle/comp.h, Src/Zle/comp1.c, Src/Zle/compctl.c, + Src/Zle/zle_tricky.c, Doc/Zsh/compwid.yo, + Functions/Zle/incremental-complete-word: compstate key + normal_nmatches; more i-c-w prompt escapes + + * Sven: 7123: Src/Zle/zle_tricky.c: clear list on expansion + failure. + +1999-07-13 Peter Stephenson <pws@ibmth.df.unipi.it> + + * pws: 7119: Src/Zle/zle_tricky.c: status from expansion + functions. + + * Sven: 7116: Doc/Zsh/compwid.yo, Doc/Zsh/expn.yo: minor + changes. + + * pws: 7114: Src/parse.c, Src/utils.c: line numbers again: + flushing line in a script made lineno appear one too large; + introduce zwarn() function. + + * Sven/pws: 7112: Src/Zle/zle_params.c, Doc/Zsh/zle.yo: change + array keys to scalar KEYS, works more like read -k; + documentation. + + * Sven: 7110: Src/Modules/zftp.c, Doc/Zsh/compsys.yo, + Etc/MACHINES: signed char warnings in zftp; document + _long_options options; Digital UNIX problem. + + * Sven: 2432: Src/builtin.c: workaround Digital UNIX 4.0 + + gcc 2.8.1 bug. + + * Sven: zsh-users/2430: Etc/NEWS: mention chmod-like mode glob + qualifier. + + * Sven: 7108: Misc/job-control-tests: more tests + + * Bart: 7107: configure.in: too many x's in lfs handling. + + * Sven: 7106: Functions/Zle/incremental-complete-word: prompting + changes. + + * pws: unposted: update .distfiles and .cvsignore: _sh, _su + zshcompsys.yo, zshcompwid.yo, zshzftp.yo, zshcompsys.1, + zshcompwid.1, zshzftp.1 were missing from the distribution. + + * Sven: 7105: Src/Zle/Zle_tricky.c: restore the command line + in more places. + 1999-07-12 Peter Stephenson <pws@ibmth.df.unipi.it> + * Sven: 7103: Src/Zle/zle_tricky.c, Doc/Zsh/compwid.yo: update + CURRENT with compset -q; modify test for quoted delimiters. + * pws: version 3.1.6-test-1 * Sven: 7099: Completion/Core/_main_complete, Doc/Zsh/compsys.yo: diff --git a/Completion/.cvsignore b/Completion/.cvsignore new file mode 100644 index 000000000..f3c7a7c5d --- /dev/null +++ b/Completion/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/Completion/.distfiles b/Completion/.distfiles index e85e122ef..f1e1c87b3 100644 --- a/Completion/.distfiles +++ b/Completion/.distfiles @@ -1,3 +1,3 @@ DISTFILES_SRC=' - .distfiles README Makefile.in + .cvsignore .distfiles README Makefile.in ' diff --git a/Completion/Base/_brace_parameter b/Completion/Base/_brace_parameter index 5993aecba..020dc81e9 100644 --- a/Completion/Base/_brace_parameter +++ b/Completion/Base/_brace_parameter @@ -18,4 +18,4 @@ q=${(M)lp%%\"#} [[ n -gt 0 ]] && suf='' -_parameters -s "${q[1,-n-1]}" -S "$suf" -r '-:?#%+=[/' +_parameters -Qs "${q[1,-n-1]}" -S "$suf" -r '-:?#%+=[/' diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files index e65f814e1..019e3abcf 100644 --- a/Completion/Core/_path_files +++ b/Completion/Core/_path_files @@ -244,7 +244,7 @@ for prepath in "$prepaths[@]"; do if [[ "$tmp2[1]" = */* ]]; then tmp2=( "${(@)tmp2#${prepath}${realpath}}" ) if [[ "$tmp2[1]" = */* ]]; then - exppaths=( "$exppaths[@]" ${^tmp2:h}/${tpre}${tsuf} ) + exppaths=( "$exppaths[@]" ${^tmp2:h:q}/${tpre}${tsuf} ) else exppaths=( "$exppaths[@]" ${tpre}${tsuf} ) fi @@ -252,6 +252,18 @@ for prepath in "$prepaths[@]"; do continue 2 fi elif (( ! $#tmp1 )); then + # A little extra hack: if we were completing `foo/<TAB>' and `foo' + # contains no files, this will normally produce no matches and other + # completers might think that's it's their time now. But if the next + # completer is _correct or something like that, this will result in + # an attempt to correct a valid directory name. So we just add the + # original string in such a case so that the command line doesn't + # change but other completers still think there are matches. + + if [[ -z "$tpre$tsuf" && "$pre" = */ && -z "$suf" ]]; then + compadd -nQS '' - "$linepath$donepath$orig" + tmp4=- + fi continue 2 fi @@ -310,33 +322,33 @@ for prepath in "$prepaths[@]"; do if [[ -n $menu ]]; then [[ -n "$compconfig[path_cursor]" ]] && compstate[to_end]='' if [[ "$tmp3" = */* ]]; then - compadd -Uf -p "$linepath$testpath" -s "/${tmp3#*/}" \ + compadd -QUf -p "$linepath${testpath:q}" -s "/${tmp3#*/}" \ -W "$prepath$realpath$testpath" "$ignore[@]" \ "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \ "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - - "${(@)tmp1%%/*}" + - "${(@)${(@)tmp1%%/*}:q}" else - compadd -Uf -p "$linepath$testpath" \ + compadd -QUf -p "$linepath${testpath:q}" \ -W "$prepath$realpath$testpath" "$ignore[@]" \ "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - - "$tmp1[@]" + - "${(@)tmp1:q}" fi else if [[ "$tmp3" = */* ]]; then for i in "$tmp1[@]"; do - compadd -Uf -p "$linepath$testpath" -s "/${i#*/}" \ + compadd -QUf -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \ -W "$prepath$realpath$testpath" "$ignore[@]" \ "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \ "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - - "${i%%/*}" + - "${${i%%/*}:q}" done else - compadd -Uf -p "$linepath$testpath" \ + compadd -QUf -p "$linepath${testpath:q}" \ -W "$prepath$realpath$testpath" "$ignore[@]" \ "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - - "$tmp1[@]" + - "${(@)tmp1:q}" fi fi tmp4=- @@ -361,11 +373,11 @@ for prepath in "$prepaths[@]"; do done if [[ -z "$tmp4" ]]; then - compadd -Uf -p "$linepath$testpath" \ + compadd -QUf -p "$linepath${testpath:q}" \ -W "$prepath$realpath$testpath" "$ignore[@]" \ "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - - "$tmp1[@]" + - "${(@)tmp1:q}" fi done @@ -376,9 +388,9 @@ done exppaths=( "${(@)exppaths:#$orig}" ) if [[ -n "$compconfig[path_expand]" && - $#exppaths -eq 1 && nm -eq compstate[nmatches] ]]; then + $#exppaths -gt 0 && nm -eq compstate[nmatches] ]]; then compadd -QU -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - -M 'r:|/=* r:|=*' -p "$linepath" - "${(@)exppaths}" + -M 'r:|/=* r:|=*' -p "$linepath" - "$exppaths[@]" fi [[ nm -ne compstate[nmatches] ]] diff --git a/Completion/User/.distfiles b/Completion/User/.distfiles index c96f2ba3e..9187fa520 100644 --- a/Completion/User/.distfiles +++ b/Completion/User/.distfiles @@ -1,6 +1,8 @@ DISTFILES_SRC=' .distfiles - _a2ps _chown _compress _configure _dd _dvi _find _groups _gunzip _gzip - _hosts _use_lo _make _man _mh _pdf _ps _rcs _rlogin _strip _stty + _a2ps _chown _compress _configure _dd _dvi _find + _gdb _groups _gunzip _gzip + _hosts _use_lo _make _man _mh _pdf _ps + _rcs _rlogin _sh _strip _stty _su _tar _tar_archive _tex _uncompress _x_options _xfig ' diff --git a/Completion/User/_gdb b/Completion/User/_gdb new file mode 100644 index 000000000..ff54e6a07 --- /dev/null +++ b/Completion/User/_gdb @@ -0,0 +1,50 @@ +#compdef gdb + +# This uses the configuration keys `ps_args' and `ps_listargs' +# described in the `_wait' function. + +local cur="$words[CURRENT]" prev w list ret=1 + +_long_options -t '*=(CORE|SYM)FILE' '_files' \ + '*=EXECFILE' '_files *(*)' \ + '*=TTY' 'compadd /dev/tty*' && return 0 + +if compset -P '-(cd|directory)='; then + _files -/ +elif compset -P '-tty='; then + compadd - /dev/tty* +elif compset -P '-(exec|se)='; then + _files -/g '*(*)' +elif compset -P '-(symbols|core|command)='; then + _files +elif compset -P -; then + compadd -QS '' - symbols\= exec\= se\= core\= command\= directory\= \ + cd\= tty\= + compadd - help h s e c x d nx n quiet q batch fullname f b +else + prev="$words[CURRENT-1]" + + case "$prev" in + (-d) _files -/ && return 0 ;; + (-e) _files -/g '*(*)' && return 0 ;; + (-[csx]) _files && return 0 ;; + (-b) compadd -V baud 0 50 75 110 134 150 200 300 600 1200 1800 2400 4800 \ + 9600 19200 38400 57600 115200 230400 && return 0 ;; + esac + w=( "${(@)words[2,-1]}" ) + while [[ "$w[1]" = -* ]]; do + [[ "$w[1]" = -[decsxb] ]] && shift 1 w + shift 1 w + done + + if [[ $#w -gt 1 ]]; then + _files && ret=0 + list=("${(F)${(@Mr:COLUMNS-1:)${(f)$(ps ${compconfig[ps_listargs]:-$compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ ]#${PREFIX}[0-9]#${SUFFIX}[ ]*${w[1]:t}}} +") + compadd -y list - ${${${(M)${(f)"$(ps $compconfig[ps_args] 2>/dev/null)"}:#*${w[1]:t}*}## #}%% *} && ret=0 + + return ret + else + _files -/g '*(*)' + fi +fi diff --git a/Completion/User/_hosts b/Completion/User/_hosts index a0aca0a62..d498425bc 100644 --- a/Completion/User/_hosts +++ b/Completion/User/_hosts @@ -2,4 +2,4 @@ : ${(A)hosts:=${(s: :)${(ps:\t:)${${(f)"$(</etc/hosts)"}%%\#*}##[:blank:]#[^[:blank:]]#}}} -compgen -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' -k hosts +compadd -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' "$@" - "$hosts[@]" diff --git a/Completion/User/_make b/Completion/User/_make index add58dbf0..7a19484f0 100644 --- a/Completion/User/_make +++ b/Completion/User/_make @@ -19,6 +19,6 @@ else fi [[ -n "$file" ]] && - compadd - $(awk '/^[a-zA-Z0-9][^/ ]+:/ {print $1}' FS=: $file) && ret=0 + compadd - $(awk '/^[a-zA-Z0-9][^\/ ]+:/ {print $1}' FS=: $file) && ret=0 (( ret )) && _files fi diff --git a/Completion/User/_rlogin b/Completion/User/_rlogin index b792ba0d1..36ee7ffe8 100644 --- a/Completion/User/_rlogin +++ b/Completion/User/_rlogin @@ -1,7 +1,7 @@ #compdef rlogin rsh ssh if [[ CURRENT -eq 2 ]]; then - compgen -k hosts + _hosts elif [[ CURRENT -eq 3 ]]; then compadd - -l else diff --git a/Completion/User/_sh b/Completion/User/_sh new file mode 100644 index 000000000..ea0fcfc11 --- /dev/null +++ b/Completion/User/_sh @@ -0,0 +1,8 @@ +#compdef sh ksh bash zsh csh tcsh rc + +if (( CURRENT == ${words[(i)-c]} + 1 )); then + compset -q + _normal +else + _default +fi diff --git a/Completion/User/_su b/Completion/User/_su new file mode 100644 index 000000000..e5bf09d80 --- /dev/null +++ b/Completion/User/_su @@ -0,0 +1,22 @@ +#compdef su + +local shell comp name usr base + +[[ $words[2] != - ]] +(( base=$?+2 )) + +if [[ CURRENT -eq base ]]; then + compgen -u && return + usr=root +elif [[ CURRENT -ge base+1 ]]; then + usr=$words[base] +else + return +fi + +shell=${"$(egrep "^$usr:" </etc/passwd)"##*:} +compset -n $base +for name in $shell $shell:t -default-; do + comp="$_comps[$name]" + [[ ! -z "$comp" ]] && "$comp" && return +done diff --git a/Completion/User/_x_options b/Completion/User/_x_options index a9c18b0d1..fddec2af4 100644 --- a/Completion/User/_x_options +++ b/Completion/User/_x_options @@ -3,7 +3,8 @@ # A simple pattern completion, just as an example. if [ "$words[CURRENT-1]" = "-display" ]; then - compgen -k hosts -S':0' + _compskip='' + _hosts -S :0 else - compadd -J options - -display -name -xrm + compadd -P- -J options - display name xrm fi diff --git a/Config/version.mk b/Config/version.mk index 30858c298..640fe6f8e 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.6-test-1 -VERSION_DATE='July 9, 1999' +VERSION=3.1.6-test-2 +VERSION_DATE='July 19, 1999' diff --git a/Doc/.distfiles b/Doc/.distfiles index 7fd1cdcc1..46f1e7406 100644 --- a/Doc/.distfiles +++ b/Doc/.distfiles @@ -2,11 +2,13 @@ DISTFILES_SRC=' .cvsignore .distfiles Makefile.in META-FAQ.yo intro.ms version.yo zmacros.yo zman.yo ztexi.yo - zsh.yo zshbuiltins.yo zshcompctl.yo zshexpn.yo zshmisc.yo - zshmodules.yo zshoptions.yo zshparam.yo zshzle.yo + zsh.yo zshbuiltins.yo zshcompctl.yo zshcompsys.yo zshcompwid.yo + zshexpn.yo zshmisc.yo zshmodules.yo zshoptions.yo zshparam.yo + zshzftpsys.yo zshzle.yo zsh.texi - zsh.1 zshbuiltins.1 zshcompctl.1 zshexpn.1 zshmisc.1 zshmodules.1 - zshoptions.1 zshparam.1 zshzle.1 zshall.1 + zsh.1 zshbuiltins.1 zshcompctl.1 zshcompsys.1 zshcompwid.1 zshexpn.1 + zshmisc.1 zshmodules.1 zshoptions.1 zshparam.1 zshzle.1 zshall.1 + zshzftpsys.1 ' DISTFILES_DOC=' diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index 477fbdc77..cca636cdc 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -31,7 +31,7 @@ next section. Usually, tt(compinstall) will insert code into tt(.zshrc), although if that is not writable it will save it in another file and tell you that -file's locations. Note that it is up to you to make sure that the lines +file's location. Note that it is up to you to make sure that the lines added to tt(.zshrc) are actually run; you may, for example, need to move them to an earlier place in the file if tt(.zshrc) usually returns early. So long as you keep them all together (including the comment lines at the @@ -92,7 +92,7 @@ will only need to run this yourself if you change the configuration (e.g. using tt(compdef)) and then want to dump the new one. The name of the old dumped file will be remembered for this purpose. -If the parameter tt(_compdir) is set, tt(compinit) uses it has a directory +If the parameter tt(_compdir) is set, tt(compinit) uses it as a directory where completion functions can be found; this is only necessary if they are not already in the function search path. @@ -489,7 +489,7 @@ non-empty string it should be an expression usable inside a `tt($((...)))' arithmetical expression. In this case, expansion of substitutions will be done if the expression evaluates to `tt(1)'. For example, with -example(compconf expand_substitute='NUMERIC != 1') +example(compconf expand_substitute='${NUMERIC:-1} != 1') substitution will be performed only if given an explicit numeric argument other than `tt(1)', as by typing `tt(ESC 2 TAB)'. @@ -720,8 +720,8 @@ is used. ) item(tt(_parameters))( This should be used to complete parameter names if you need some of the -extra options of tt(compadd). It first tries to complete only non-local -parameters. All arguments are passed unchanged to the tt(compadd) builtin. +extra options of tt(compadd). All arguments are passed unchanged to +the tt(compadd) builtin. ) item(tt(_options))( This can be used to complete option names. The difference to the @@ -780,9 +780,19 @@ descriptions that contain `tt(=DIR)' or `tt(=PATH)'. These builtin patterns can be overridden by patterns given as arguments, however. This function also accepts the `tt(-X)', `tt(-J)', and `tt(-V)' -options which are passed unchanged to `tt(compadd)'. Finally, it -accepts the option `tt(-t)'; if this is given, completion is only done -on words starting with two hyphens. +options which are passed unchanged to `tt(compadd)'. If the +option `tt(-t)' is given, completion is only done on words starting +with two hyphens. The option `tt(-i) var(patterns)' can be used to +give patterns for options which should not be completed. The patterns +can be given as the name of an array parameter or as a literal list in +parentheses. E.g. `tt(-i "(--(en|dis)able-FEATURE*)")' will ignore the +options `tt(--enable-FEATURE)' and `tt(--diable-FEATURE)'. Finally, +the option `tt(-s) var(pairs)' can be used to describe options +aliases. Each var(pair) consists of a pattern and a +replacement. E.g. some tt(configure)-scripts describe options only as +`tt(--enable-foo)', but also accept `tt(disable-foo)'. To allow +completion of the second form, one would use +`tt(-s "(#--enable- --disable-)")'. ) enditem() diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo index 4d439ab30..62c243740 100644 --- a/Doc/Zsh/compwid.yo +++ b/Doc/Zsh/compwid.yo @@ -175,6 +175,12 @@ is unset. item(tt(nmatches))( The number of matches generated and accepted by the completion code so far. ) +item(tt(normal_nmatches))( +Like tt(nmatches), but counts only matches in the normal set. I.e. file +names with one of the suffixes from the tt(fignore) array and matches +put into the alternate set using the tt(-a) option of the tt(compadd) +builtin command (see below) are not counted. +) item(tt(matcher))( When completion is performed with a global match specification as defined by @@ -410,7 +416,7 @@ item(tt(-I) var(ignored-suffix))( Like tt(-i), but gives an ignored suffix. ) item(tt(-y) var(array))( -This gives a number of string to display instead of the matches. This +This gives a number of strings to display instead of the matches. This is like the tt(-y) option of the tt(compctl) builtin command but the var(array) argument may only be the name of an array parameter or a literal array in parentheses containing the strings to display. @@ -483,7 +489,7 @@ from the first set are stored. Normally only the matches in the first set are used, but if this set is empty, the words from the alternate set are used. -The tt(compadd) builtin does not use tt(fignore) parameter and +The tt(compadd) builtin does not use the tt(fignore) parameter and normally stores all words in the first set. With the tt(-a)-flag given, however, the given var(words) are stored in the alternate set unless this flag is overridden by the tt(-F) option. @@ -518,7 +524,7 @@ will be done by the completion code. Normally this is used in functions that do the matching themselves. Note that with tt(compadd) this option does not automatically turn on -menu completion if tt(AUTO_LIST), unlike the corresponding option of +menu completion if tt(AUTO_LIST) is set, unlike the corresponding option of tt(compctl) and tt(compgen) commands. ) item(tt(-O) var(array))( @@ -622,9 +628,9 @@ testing and modification is performed as if it were not given. item(tt(-q))( The word currently being completed is split in separate words at the spaces. The -resulting words are stored in the tt(words) array, and tt(PREFIX), -tt(SUFFIX), tt(QIPREFIX), and tt(QISUFFIX) are modified to reflect the -word part that is completed. +resulting words are stored in the tt(words) array, and tt(CURRENT), +tt(PREFIX), tt(SUFFIX), tt(QIPREFIX), and tt(QISUFFIX) are modified to +reflect the word part that is completed. ) enditem() diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 89f94a507..bb75d4865 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1192,7 +1192,7 @@ grouping the string as tt([d][cb][a]) and tt([a][bc][d]). Non-literal parts of the pattern must match exactly, including characters in character ranges: hence tt(LPAR()#a1)tt(RPAR()???) matches strings of length four, by applying rule 4 to an empty part of the pattern, but not -strings of length three, since all the tt(?) must match. Other characters +strings of length two, since all the tt(?) must match. Other characters which must match exactly are initial dots in filenames (unless the tt(GLOB_DOTS) option is set), and all slashes in file names, so that tt(a/bc) is two errors from tt(ab/c) (the slash cannot be transposed with @@ -1351,7 +1351,7 @@ bit. Thus, `tt(*(f70?))' gives the files for which the owner has read, write, and execute permission, and for which other group members have -no rights, independent of the permissions for other user. The pattern +no rights, independent of the permissions for other users. The pattern `tt(*(f-100))' gives all files for which the owner does not have execute permission, and `tt(*(f:gu+w,o-rx))' gives the files for which the owner and the other members of the group have at least write @@ -1455,7 +1455,7 @@ returned list. The syntax is the same as for array subscripts. var(beg) and the optional var(end) may be mathematical expressions. As in parameter subscripting they may be negative to make them count from the last match backward. E.g.: `tt(*(-OL[1,3]))' -gives a list of the names of three biggest files. +gives a list of the names of the three largest files. ) enditem() diff --git a/Doc/Zsh/mod_complist.yo b/Doc/Zsh/mod_complist.yo index 236896e43..b37252582 100644 --- a/Doc/Zsh/mod_complist.yo +++ b/Doc/Zsh/mod_complist.yo @@ -12,9 +12,9 @@ not automatically be loaded if it is not linked in: on systems with dynamic loading, `tt(zmodload complist)' is required. subsect(Parameters) -For both extensions one of the parameters tt(ZLS_COLORS) or tt(ZLS_COLOURS) -must be set, even if the value is empty (which uses all the default values -given below). These describe how matches are highlighted. The format of the +The parameters tt(ZLS_COLORS) and tt(ZLS_COLOURS) describe how matches +are highlighted. To turn on highlighting an empty value suffices, in +which case all the default values given below will be used. The format of the value of these parameters is the same as used by the GNU version of the tt(ls) command: a colon-separated list of specifications of the form `var(name)=var(value)'. The var(name) may be one of the following strings, @@ -79,7 +79,12 @@ the default values will have no visual effect. subsect(Menu selection) The tt(complist) module also offers an alternative style of selecting -matches from a list, called menu-selection. It can be invoked directly by +matches from a list, called menu-selection, which can be used if the +shell is set up to return to the last prompt after showing a +completion list (see the tt(ALWAYS_LAST_PROMPT) option in +ifzman(zmanref(zshoptions))\ +ifnzman(noderef(Options))\ +). It can be invoked directly by the widget tt(menu-select) defined by the module. Alternatively, the parameter tt(SELECTMIN) can be set to an integer, which give the minimum number of matches that must be present before menu selection is diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 265c5e184..bfd7bc201 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -205,11 +205,30 @@ tt(AUTO_CD) option set) is not a directory, and does not begin with a slash, try to expand the expression as if it were preceded by a `tt(~)' (see noderef(Filename Expansion)). ) +pindex(CHASE_DOTS) +cindex(cd, with .. in argument) +item(tt(CHASE_DOTS))( +When changing to a directory containing a path segment `tt(..)' which would +otherwise be treated as cancelling the previous segment in the path (in +other words, `tt(foo/..)' would be removed from the path, or if `tt(..)' is +the first part of the path, the last part of tt($PWD) would be deleted), +instead resolve the path to the physical directory. This option is +overridden by tt(CHASE_LINKS). + +For example, suppose tt(/foo/bar) is a link to the directory tt(/alt/rod). +Without this option set, `tt(cd /foo/bar/..)' changes to tt(/foo); with it +set, it changes to tt(/alt). The same applies if the current directory +is tt(/foo/bar) and `tt(cd ..)' is used. Note that all other symbolic +links in the path will also be resolved. +) pindex(CHASE_LINKS) cindex(links, symbolic) cindex(symbolic links) item(tt(CHASE_LINKS) (tt(-w)))( Resolve symbolic links to their true values when changing directory. +This also has the effect of tt(CHASE_DOTS), i.e. a `tt(..)' path segment +will be treated as referring to the physical parent, even if the preceeding +path segment is a symbolic link. ) pindex(CLOBBER) cindex(clobbering, of files) @@ -416,9 +435,13 @@ isn't there. pindex(HIST_EXPIRE_DUPS_FIRST) cindex(history, expiring duplicates) item(tt(HIST_EXPIRE_DUPS_FIRST))( -If the internal history needs to be trimmed to add a new line, -setting this option will cause the oldest duplicate history line to -be lost before losing a unique line from the list. +If the internal history needs to be trimmed to add the current command line, +setting this option will cause the oldest history event that has a duplicate +to be lost before losing a unique event from the list. +You should be sure to set the value of tt(HISTSIZE) to a larger number +than tt(SAVEHIST) in order to give you some room for the duplicated +events, otherwise this option will behave just like +tt(HIST_IGNORE_ALL_DUPS) once the history fills up with unique events. ) pindex(HIST_IGNORE_ALL_DUPS) cindex(history, ignoring all duplicates) diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 05bd24613..2dbff771e 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -507,7 +507,10 @@ If unset, the history is not saved. ) vindex(HISTSIZE) item(tt(HISTSIZE) <S>)( -The maximum size of the history list. +The maximum number of events stored in the internal history list. +If you use the tt(HIST_EXPIRE_DUPS_FIRST) option, setting this value +larger than the tt(SAVEHIST) size will give you the difference as a +cushion for saving duplicated history events. ) vindex(HOME) item(tt(HOME) <S>)( diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index ce1b27fec..60c003533 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -154,12 +154,9 @@ vindex(LASTWIDGET) item(tt(LASTWIDGET) (scalar))( The name of the last widget that was executed. ) -vindex(keys) -item(tt(keys) (array))( -The keys typed to invoke this widget, one element per -key. Control-keys are reported with a leading `tt(^)', as in `tt(^A)', -and meta-keys are reported with a leading `tt(M-)', as in `tt(M-a)' and -`tt(M-^A)'. +vindex(KEYS) +item(tt(KEYS) (scalar))( +The keys typed to invoke this widget, as a literal string. ) vindex(NUMERIC) item(tt(NUMERIC) (integer))( diff --git a/Etc/MACHINES b/Etc/MACHINES index fd386c7e0..038340e79 100644 --- a/Etc/MACHINES +++ b/Etc/MACHINES @@ -39,15 +39,21 @@ DEC: OSF/1 1.2, 1.3, 2.0, 3.*, DEC Unix 4.* (Alpha) remove the bogus strip and use /bin/strip instead. On Digital UNIX 4.0, compilation with gcc and with --enable-dynamic - apparently needs configuring with explicit flags: + apparently needs configuring with explicit flags when compiling + with debugging enabled: DLLD=gcc LDFLAGS='-g -rpath <path-to-.so-files>' ./configure ... FreeBSD: FreeBSD 2.2.7 [3.1.4] Should build `out-of-the-box'. -HP: HP-UX 9, 10.20 +HP: HP-UX 9, 10.20, 11.0 Should build `out-of-the-box'. + Problems with dynamic loading have been reported under 11, but + this should compile using the standard dlopen() function set + (rather than the 10.20 shl_load() function set). More details of + any difficulties would be appreciated. + IBM: AIX Should build `out-of-the-box'. On AIX 3.x (at least), --enable-zsh-mem will not work. @@ -87,9 +93,6 @@ SGI: IRIX 5.1.1.1, 5.2, 5.3, 6.2, 6.3, 6.5 full optimization (cc -O3 -OPT:Olimit=0) causes problems. Sun: SunOS 4.1.* - Dynamic loading does not work under SunOS 4.1. Sometimes, - you may need to turn it off explicitly with --disable-dynamic. - Under 4.1.3 if yellow pages is used, username completion may cause segmentation violation. This is a bug in the shared library not in zsh. Some libc.so.1.9.* has this bug (it fails in yp_all). diff --git a/Etc/NEWS b/Etc/NEWS index 9d58c9cc6..aad7ccceb 100644 --- a/Etc/NEWS +++ b/Etc/NEWS @@ -34,6 +34,7 @@ Globbing changes: - Case-insensitive and approximate globbing. - Ordering and indexing of globbing matches, e.g. *(om[1]) picks most recently modified file. + - General file mode qualifier with chmod(1)-like syntax, e.g. *(f:u+wx:) New loadable modules: - zftp, plus associated function suite, for turning your zsh session diff --git a/Functions/.cvsignore b/Functions/.cvsignore new file mode 100644 index 000000000..f3c7a7c5d --- /dev/null +++ b/Functions/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/Functions/.distfiles b/Functions/.distfiles index fe60df9b7..03aebe308 100644 --- a/Functions/.distfiles +++ b/Functions/.distfiles @@ -1,3 +1,3 @@ DISTFILES_SRC=' - .distfiles Makefile.in README.zftp + .cvsignore .distfiles Makefile.in README.zftp ' diff --git a/Functions/Zftp/zfgoto b/Functions/Zftp/zfgoto index 8d6f00a3a..bd1cdbfe5 100644 --- a/Functions/Zftp/zfgoto +++ b/Functions/Zftp/zfgoto @@ -67,7 +67,10 @@ line=${line#*@} host=${line%%:*} dir=${line#*:} -if [[ $user = ftp || $user = anonymous ]]; then +if [[ $ZFTP_USER = $user && $ZFTP_HOST = $host ]]; then + # We're already there, just change directory + zfcd ${dir:-~} +elif [[ $user = ftp || $user = anonymous ]]; then # Anonymous ftp, so we don't need password etc. zfanon $host && [[ -n $dir ]] && zfcd $dir elif [[ $zflastsession = ${host}:* && $user = $zflastuser ]]; then diff --git a/Functions/Zftp/zfinit b/Functions/Zftp/zfinit index fbe6c5979..2a5fd9b47 100644 --- a/Functions/Zftp/zfinit +++ b/Functions/Zftp/zfinit @@ -1,6 +1,6 @@ emulate -L zsh -[[ $1 = -n ]] || zmodload -ia zftp +[[ $1 = -n ]] || zmodload -e zftp || zmodload -ia zftp alias zfcd='noglob zfcd' alias zfget='noglob zfget' diff --git a/Functions/Zle/incremental-complete-word b/Functions/Zle/incremental-complete-word index 2a9c1aff2..3831ecaa6 100644 --- a/Functions/Zle/incremental-complete-word +++ b/Functions/Zle/incremental-complete-word @@ -4,32 +4,55 @@ # to a key. # This allows incremental completion of a word. After starting this -# command, a list of completion choices is shown after every character you -# type, which you can delete with ^h or DEL. RET will accept the -# completion so far. You can hit TAB to do normal completion and ^g to -# abort back to the state when you started. +# command, a list of completion choices can be shown after every character +# you type, which you can delete with ^h or DEL. RET will accept the +# completion so far. You can hit TAB to do normal completion, ^g to +# abort back to the state when you started, and ^d to list the matches. # -# Completion keys: -# incremental_prompt Prompt to show in status line during icompletion; -# the sequence `%u' is replaced by the unambiguous -# part of all matches if there is any and it is -# different from the word on the line -# incremental_stop Pattern matching keys which will cause icompletion -# to stop and the key to be re-executed -# incremental_break Pattern matching keys which will cause icompletion -# to stop and the key to be discarded -# incremental_completer Set of completers, like the `completer' key -# incremental_list If set to a non-empty string, the matches will be -# listed on every key-press +# This works best with the new function based completion system. +# +# Configuration keys: +# +# incremental_prompt +# Prompt to show in status line during icompletion. The sequence `%u' +# is replaced by the unambiguous part of all matches if there is any +# and it is different from the word on the line. A `%s' is replaced +# with `-no match-', `-no prefix-', or an empty string if there is +# no completion matching the word on the line, if the matches have +# no common prefix different from the word on the line or if there is +# such a common prefix, respectively. The sequence `%c' is replaced +# by the name of the completer function that generated the matches +# (without the leading underscore). Finally, `%n' is replaced by the +# number of matches generated and `%a' is replaced by an empty string +# if the matches are in the normal set (i.e. the one without file names +# with one of the suffixes from `fignore') and with ` -alt-' if the +# matches are in the alternate set. +# +# incremental_stop +# Pattern matching keys which will cause icompletion to stop and the +# key to be re-executed. +# +# incremental_break +# Pattern matching keys which will cause icompletion to stop and the +# key to be discarded. +# +# incremental_completer +# Set of completers, like the `completer' key for normal completion. +# +# incremental_list +# If set to a non-empty string, the matches will be listed on every +# key-press. + emulate -L zsh unsetopt autolist menucomplete automenu # doesn't work well -local key lbuf="$LBUFFER" rbuf="$RBUFFER" pmpt word lastl lastr wid twid +local key lbuf="$LBUFFER" rbuf="$RBUFFER" pmpt word +local lastl lastr wid twid num alt [[ -n "$compconfig[incremental_completer]" ]] && -set ${(s.:.)compconfig[incremental_completer]} -pmpt="${compconfig[incremental_prompt]-incremental completion...}" + set ${(s.:.)compconfig[incremental_completer]} +pmpt="${compconfig[incremental_prompt]-incremental (%c): %u%s}" if [[ -n "$compconfig[incremental_list]" ]]; then wid=list-choices @@ -40,12 +63,22 @@ fi zle $wid "$@" LBUFFER="$lbuf" RBUFFER="$rbuf" -if [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then +if (( ! _lastcomp[nmatches] )); then + word='' + state='-no match-' +elif [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then word='' + state='-no prefix-' else word="${_lastcomp[unambiguous]}" + state='' +fi +num=$_lastcomp[normal_nmatches] +if (( ! num )); then + num="${_lastcomp[nmatches]}" + alt=' -alt-' fi -zle -R "${pmpt//\\%u/$word}" +zle -R "${${${${${pmpt//\\%u/$word}//\\%s/$state}//\\%c/${_lastcomp[completer][2,-1]}}//\\%n/$num}//\\%a/$alt}" read -k key while [[ '#key' -ne '#\\r' && '#key' -ne '#\\n' && @@ -72,12 +105,24 @@ while [[ '#key' -ne '#\\r' && '#key' -ne '#\\n' && zle $twid "$@" LBUFFER="$lastl" RBUFFER="$lastr" - if [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then + if (( ! _lastcomp[nmatches] )); then word='' + state='-no match-' + elif [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then + word='' + state='-no prefix-' else word="${_lastcomp[unambiguous]}" + state='' + fi + num=$_lastcomp[normal_nmatches] + if (( ! num )); then + num="${_lastcomp[nmatches]}" + alt=' -alt-' + else + alt='' fi - zle -R "${pmpt//\\%u/$word}" + zle -R "${${${${${pmpt//\\%u/$word}//\\%s/$state}//\\%c/${_lastcomp[completer][2,-1]}}//\\%n/$num}//\\%a/$alt}" read -k key done diff --git a/INSTALL b/INSTALL index b8eaa561c..5e61f0795 100644 --- a/INSTALL +++ b/INSTALL @@ -246,9 +246,12 @@ plus those provide functions for the line editor, i.e. FUNCTIONS_INSTALL='Core/* Base/* Builtins/* User/* Commands/* Zle/*' and if the --enable-dynamic option was given, the functions in Functions/Zftp, which require the zftp module to be available (see the -zshzftpsys manual page), will be included as well. There are also some -miscellaneous functions with documentation in comments; the complete set -of functions can be installed with +zshzftpsys manual page), will be included as well. Note, however, that +some of the functions in the User subdirectory are version- and +system-specific. + +There are also some miscellaneous functions with documentation in comments; +the complete set of functions can be installed with FUNCTIONS_INSTALL='Core/* Base/* Builtins/* User/* Commands/* \ Misc/* Zftp/* Zle/*' Note you should set this by hand to include `Zftp/*' if you have zftp diff --git a/Misc/job-control-tests b/Misc/job-control-tests index 7e35fba0b..ecb9a7694 100644 --- a/Misc/job-control-tests +++ b/Misc/job-control-tests @@ -29,3 +29,48 @@ while true; do sed -e 's/foo/bar/' Src/builtin.c >/dev/null; done # ignoring the error messages from sed. # ^Z is more of a problem since you have to catch the sed. while true; do sed -e 's/foo/bar/' non-existent-file >/dev/null; done + +# Try +# ^Z +# fg +# ^Z +# fg +fn() { + local a + while read a; do :; done + less "$@" +} +cat foo | fn bar + +# Try +# ^Z +# fg +fn() { + cat builtin.c +} +fn | while read a; do :; done + +# Try +# ^Z +# fg +# q +# ^Z +# fg +# q +fn() { + less builtin.c + echo done +} +x=2; while (( x-- )); do f; done + +# Try +# ^C +# This won't work because zcat doesn't tell us that it received a signal. +# But +# ^Z +# fg +# ^C (probably a second ^C is needed: if the continued zcat is still running) +# works. +# (See also the file Etc/BUGS) +while true; do zcat foo.gz > /dev/null; done + diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index 63bca854c..738d596fa 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -2261,7 +2261,7 @@ zftp_type(char *name, char **args, int flags) fflush(stdout); return 0; } else { - nt = toupper(*str); + nt = toupper(STOUC(*str)); /* * RFC959 specifies other types, but these are the only * ones we know what to do with. @@ -2294,7 +2294,7 @@ zftp_mode(char *name, char **args, int flags) fflush(stdout); return 0; } - nt = str[0] = toupper(*str); + nt = str[0] = toupper(STOUC(*str)); if (str[1] || (nt != 'S' && nt != 'B')) { zwarnnam(name, "transfer mode %s not recognised", str, 0); return 1; @@ -2651,7 +2651,7 @@ bin_zftp(char *name, char **args, char *ops, int func) if ((prefs = getsparam("ZFTP_PREFS"))) { zfprefs = 0; for (ptr = prefs; *ptr; ptr++) { - switch (toupper(*ptr)) { + switch (toupper(STOUC(*ptr))) { case 'S': /* sendport */ zfprefs |= ZFPF_SNDP; diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 8264890df..b0fbf3ac6 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -218,6 +218,8 @@ struct cmatch { int brsl; /* ...and the suffix */ char *rems; /* when to remove the suffix */ char *remf; /* shell function to call for suffix-removal */ + int qipl; /* length of quote-prefix */ + int qisl; /* length of quote-suffix */ int rnum; /* group relative number */ int gnum; /* global number */ }; @@ -349,25 +351,25 @@ struct chdata { #define CPN_NMATCHES 0 -#define CP_NMATCHES (1 << CPN_NMATCHES) +#define CP_NMATCHES (1 << CPN_NMATCHES) #define CPN_MATCHER 1 -#define CP_MATCHER (1 << CPN_MATCHER) +#define CP_MATCHER (1 << CPN_MATCHER) #define CPN_MATCHERSTR 2 -#define CP_MATCHERSTR (1 << CPN_MATCHERSTR) +#define CP_MATCHERSTR (1 << CPN_MATCHERSTR) #define CPN_MATCHERTOT 3 -#define CP_MATCHERTOT (1 << CPN_MATCHERTOT) +#define CP_MATCHERTOT (1 << CPN_MATCHERTOT) #define CPN_CONTEXT 4 -#define CP_CONTEXT (1 << CPN_CONTEXT) +#define CP_CONTEXT (1 << CPN_CONTEXT) #define CPN_PARAMETER 5 -#define CP_PARAMETER (1 << CPN_PARAMETER) +#define CP_PARAMETER (1 << CPN_PARAMETER) #define CPN_REDIRECT 6 -#define CP_REDIRECT (1 << CPN_REDIRECT) +#define CP_REDIRECT (1 << CPN_REDIRECT) #define CPN_QUOTE 7 -#define CP_QUOTE (1 << CPN_QUOTE) +#define CP_QUOTE (1 << CPN_QUOTE) #define CPN_QUOTING 8 -#define CP_QUOTING (1 << CPN_QUOTING) +#define CP_QUOTING (1 << CPN_QUOTING) #define CPN_RESTORE 9 -#define CP_RESTORE (1 << CPN_RESTORE) +#define CP_RESTORE (1 << CPN_RESTORE) #define CPN_LIST 10 #define CP_LIST (1 << CPN_LIST) #define CPN_FORCELIST 11 @@ -398,6 +400,8 @@ struct chdata { #define CP_OLDINS (1 << CPN_OLDINS) #define CPN_VARED 24 #define CP_VARED (1 << CPN_VARED) +#define CPN_NNMATCHES 25 +#define CP_NNMATCHES (1 << CPN_NNMATCHES) -#define CP_KEYPARAMS 25 +#define CP_KEYPARAMS 26 #define CP_ALLKEYS ((unsigned int) 0xffffff) diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c index ba8bcc868..c1e2bfb57 100644 --- a/Src/Zle/comp1.c +++ b/Src/Zle/comp1.c @@ -105,6 +105,7 @@ int incompfunc; /**/ zlong compcurrent, compnmatches, + compnnmatches, compmatcher, compmatchertot, complistmax; diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export index 9b738cc78..4b6dd92fd 100644 --- a/Src/Zle/comp1.export +++ b/Src/Zle/comp1.export @@ -27,6 +27,7 @@ compmatcher compmatcherstr compmatchertot compnmatches +compnnmatches compoldlist compoldins compparameter diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 694af8429..e9ff83387 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -2210,6 +2210,7 @@ static struct compparam compkparams[] = { { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL }, { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL }, { "vared", PM_SCALAR, VAL(compvared), NULL, NULL }, + { "normal_nmatches", PM_INTEGER, VAL(compnnmatches), NULL, NULL }, { NULL, 0, NULL, NULL, NULL } }; diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 0e7152866..4bf3fec0e 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -308,8 +308,8 @@ complistmatches(Hookdef dummy, Chdata dat) Cmatch *p, m; Cexpl *e; int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0, opl = 0; - int of = isset(LISTTYPES); - int mc, ml = 0, cc, hasm = 0, cl; + int of = isset(LISTTYPES), cf; + int mc, ml = 0, cc, hasm = 0, cl = -1; struct listcols col; if (minfo.asked == 2) { @@ -318,15 +318,6 @@ complistmatches(Hookdef dummy, Chdata dat) } getcols(&col); - /* Set the cursor below the prompt. */ - if (inselect) - clearflag = 0; - trashzle(); - showinglist = listshown = 0; - - clearflag = (isset(USEZLE) && !termflags && - complastprompt && *complastprompt); - for (g = amatches; g; g = g->next) { char **pp = g->ylist; int nl = 0, l; @@ -403,6 +394,19 @@ complistmatches(Hookdef dummy, Chdata dat) nlines += 1 + ((1 + niceztrlen(m->str)) / columns); } } + cf = (isset(USEZLE) && !termflags && complastprompt && *complastprompt); + if (!nlines || (mselect >= 0 && (!cf || (nlines + nlnct - 1) >= lines))) { + showinglist = listshown = 0; + noselect = 1; + return 1; + } + /* Set the cursor below the prompt. */ + if (inselect) + clearflag = 0; + trashzle(); + showinglist = listshown = 0; + + clearflag = cf; /* Maybe we have to ask if the user wants to see the list. */ if ((!minfo.cur || !minfo.asked) && @@ -654,20 +658,20 @@ complistmatches(Hookdef dummy, Chdata dat) pnl = 1; g = g->next; } - if (clearflag) { /* Move the cursor up to the prompt, if always_last_prompt * * is set and all that... */ if ((nlines += nlnct - 1) < lines) { tcmultout(TCUP, TCMULTUP, nlines); showinglist = -1; - listshown = 1; } else clearflag = 0, putc('\n', shout); } else putc('\n', shout); + listshown = (clearflag ? 1 : -1); if (!hasm || nlines >= lines) noselect = 1; + return noselect; } @@ -676,7 +680,7 @@ typedef struct menustack *Menustack; struct menustack { Menustack prev; char *line; - int cs; + int cs, acc; struct menuinfo info; Cmgroup amatches, pmatches, lmatches; }; @@ -742,19 +746,19 @@ domenuselect(Hookdef dummy, Chdata dat) s->amatches = amatches; s->pmatches = pmatches; s->lmatches = lmatches; - menucmp = 0; + s->acc = menuacc; + menucmp = menuacc = 0; fixsuffix(); validlist = 0; pmatches = NULL; invalidatelist(); menucomplete(zlenoargs); if (dat->num < 2 || !minfo.cur || !*(minfo.cur)) { - noselect = 1; - clearlist = 1; + noselect = clearlist = listshown = 1; zrefresh(); break; } - clearlist = 1; + clearlist = listshown = 1; mselect = (*(minfo.cur))->gnum; continue; } else if (cmd == Th(z_acceptandhold) || @@ -767,6 +771,7 @@ domenuselect(Hookdef dummy, Chdata dat) s->cs = cs; memcpy(&(s->info), &minfo, sizeof(struct menuinfo)); s->amatches = s->pmatches = s->lmatches = NULL; + s->acc = menuacc; acceptlast(); do_menucmp(0); mselect = (*(minfo.cur))->gnum; @@ -782,6 +787,7 @@ domenuselect(Hookdef dummy, Chdata dat) spaceinline(l = strlen(u->line)); strncpy((char *) line, u->line, l); cs = u->cs; + menuacc = u->acc; memcpy(&minfo, &(u->info), sizeof(struct menuinfo)); p = &(minfo.cur); if (u->pmatches && pmatches != u->pmatches) { diff --git a/Src/Zle/zle.export b/Src/Zle/zle.export index 8bc049e16..f63f45eb8 100644 --- a/Src/Zle/zle.export +++ b/Src/Zle/zle.export @@ -24,6 +24,7 @@ lastambig linkkeymap listshown lmatches +menuacc menucmp menucomplete menucur diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c index dc4e27685..5ed846cd6 100644 --- a/Src/Zle/zle_params.c +++ b/Src/Zle/zle_params.c @@ -67,7 +67,7 @@ static struct zleparam { zleunsetfn, NULL }, { "LASTWIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_lwidget), zleunsetfn, NULL }, - { "keys", PM_ARRAY | PM_READONLY, NULL, FN(get_keys), + { "KEYS", PM_SCALAR | PM_READONLY, NULL, FN(get_keys), zleunsetfn, NULL }, { "NUMERIC", PM_INTEGER | PM_UNSET, FN(set_numeric), FN(get_numeric), unset_numeric, NULL }, @@ -247,29 +247,10 @@ get_lwidget(Param pm) } /**/ -static char ** +static char * get_keys(Param pm) { - char **r, **q, *p, *k, c; - - r = (char **) zhalloc((strlen(keybuf) + 1) * sizeof(char *)); - for (q = r, p = keybuf; (c = *p); q++, p++) { - k = *q = (char *) zhalloc(5); - if (c & 0x80) { - *k++ = 'M'; - *k++ = '-'; - c &= 0x7f; - } - if (c < 32 || c == 0x7f) { - *k++ = '^'; - c ^= 64; - } - *k++ = c; - *k = '\0'; - } - *q = NULL; - - return r; + return keybuf; } /**/ diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 48e1071b8..1dbffc21c 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -53,7 +53,8 @@ int nlnct; /**/ int showinglist; -/* Non-zero if a completion list was displayed. */ +/* > 0 if a completion list is displayed below the prompt, + * < 0 if a list is displayed above the prompt. */ /**/ int listshown; @@ -265,7 +266,7 @@ zrefresh(void) if (inlist) return; - if (clearlist && listshown) { + if (clearlist && listshown > 0) { if (tccan(TCCLEAREOD)) { int ovln = vln, ovcs = vcs; char *nb = nbuf[vln]; @@ -331,7 +332,8 @@ zrefresh(void) tcout(TCCLEAREOD); else cleareol = 1; /* request: clear to end of line */ - listshown = 0; + if (listshown > 0) + listshown = 0; } if (t0 > -1) olnct = t0; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 5461079cb..b678a5cad 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -106,10 +106,11 @@ static int insmnum, insgnum, insgroup, insspace; static int movetoend; -/* != 0 if we are in the middle of a menu completion */ +/* != 0 if we are in the middle of a menu completion and number of matches +* accepted with accept-and-menu-complete */ /**/ -int menucmp; +int menucmp, menuacc; /* Information about menucompletion. */ @@ -192,6 +193,10 @@ static char *qipre, *qisuf, autoq; static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl; static int noreal; +/* A parameter expansion prefix (like ${). */ + +static char *parpre; + /* This is either zero or equal to the special character the word we are * * trying to complete starts with (e.g. Tilde or Equals). */ @@ -209,9 +214,9 @@ static char *qword; static Cmgroup mgroup; -/* A match counter. */ +/* Match counters: all matches, normal matches (not alternate set). */ -static int mnum; +static int mnum, nmnum; /* The match counter when unambig_data() was called. */ @@ -540,6 +545,8 @@ reversemenucomplete(char **args) void acceptlast(void) { + menuacc++; + if (brbeg && *brbeg) { int l; @@ -553,10 +560,16 @@ acceptlast(void) brbeg[l] = ','; brbeg[l + 1] = '\0'; } else { + int l; + cs = minfo.pos + minfo.len + minfo.insc; iremovesuffix(' ', 1); - + l = cs; + cs = minfo.pos + minfo.len - (*(minfo.cur))->qisl; + foredel(l - cs); inststrlen(" ", 1, 1); + if (parpre) + inststr(parpre); minfo.insc = minfo.len = 0; minfo.pos = cs; minfo.we = 1; @@ -694,6 +707,9 @@ check_param(char *s, int set, int test) { char *p; + zsfree(parpre); + parpre = NULL; + if (!test) ispar = parq = eparq = 0; /* Try to find a `$'. */ @@ -755,6 +771,8 @@ check_param(char *s, int set, int test) /* Now make sure that the cursor is inside the name. */ if (offs <= e - s && offs >= b - s && n <= 0) { + char sav; + if (br) { p = e; while (*p == (test ? Dnull : '"')) @@ -782,6 +800,12 @@ check_param(char *s, int set, int test) else parq = eparq = 0; + /* Save the prefix. */ + sav = *b; + *b = '\0'; + untokenize(parpre = ztrdup(s)); + *b = sav; + /* And adjust wb, we, and offs again. */ offs -= b - s; wb = cs - offs; @@ -1017,7 +1041,7 @@ docomplete(int lst) int ocs = cs, ne = noerrs; noerrs = 1; - doexpansion(s, lst, olst, lincmd); + ret = doexpansion(s, lst, olst, lincmd); lastambig = 0; noerrs = ne; @@ -1043,8 +1067,8 @@ docomplete(int lst) } } ret = docompletion(s, lst, lincmd); - } else - ret = !strcmp(ol, (char *) line); + } else if (ret) + clearlist = 1; } else /* Just do completion. */ ret = docompletion(s, lst, lincmd); @@ -1064,7 +1088,7 @@ docomplete(int lst) dat.num = nmatches; dat.cur = NULL; if (runhookdef(MENUSTARTHOOK, (void *) &dat)) - menucmp = 0; + menucmp = menuacc = 0; } return ret; } @@ -1736,9 +1760,10 @@ get_comp_string(void) /* Expand the current word. */ /**/ -static void +static int doexpansion(char *s, int lst, int olst, int explincmd) { + int ret = 1; LinkList vl; char *ss; @@ -1775,7 +1800,7 @@ doexpansion(char *s, int lst, int olst, int explincmd) } if (lst == COMP_LIST_EXPAND) { /* Only the list of expansions was requested. */ - listlist(vl); + ret = listlist(vl); showinglist = 0; goto end; } @@ -1783,6 +1808,7 @@ doexpansion(char *s, int lst, int olst, int explincmd) cs = wb; foredel(we - wb); while ((ss = (char *)ugetnode(vl))) { + ret = 0; untokenize(ss); ss = quotename(ss, NULL); inststr(ss); @@ -1801,6 +1827,8 @@ doexpansion(char *s, int lst, int olst, int explincmd) end: popheap(); } LASTALLOC; + + return ret; } /* This is called from the lexer to give us word positions. */ @@ -3547,6 +3575,8 @@ add_match_data(int alt, char *str, Cline line, ai->line = join_clines(ai->line, line); mnum++; + if (!alt) + nmnum++; ai->count++; /* Allocate and fill the match structure. */ @@ -3569,6 +3599,8 @@ add_match_data(int alt, char *str, Cline line, cm->flags = flags; cm->brpl = bpl; cm->brsl = bsl; + cm->qipl = qipl; + cm->qisl = qisl; cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0')); cm->rems = cm->remf = NULL; addlinknode((alt ? fmatches : matches), cm); @@ -3896,6 +3928,7 @@ addmatches(Cadata dat, char **argv) } } compnmatches = mnum; + compnnmatches = nmnum; if (dat->exp) addexpl(); if (dat->apar) @@ -4380,10 +4413,14 @@ docompletion(char *s, int lst, int incmd) } if (comppatmatch && *comppatmatch && comppatmatch != opm) haspattern = 1; - if (!useline && uselist) + if (!useline && uselist) { /* All this and the guy only wants to see the list, sigh. */ + cs = 0; + foredel(ll); + inststr(origline); + cs = origcs; showinglist = -2; - else if (useline) { + } else if (useline) { /* We have matches. */ if (nmatches > 1) { /* There is more than one match. */ @@ -4399,9 +4436,13 @@ docompletion(char *s, int lst, int incmd) do_single(m->matches[0]); invalidatelist(); } - } else + } else { invalidatelist(); - + cs = 0; + foredel(ll); + inststr(origline); + cs = origcs; + } /* Print the explanation strings if needed. */ if (!showinglist && validlist && usemenu != 2 && nmatches != 1) { Cmgroup g = amatches; @@ -4582,22 +4623,22 @@ callcompfunc(char *s, char *fn) zsfree(compprefix); zsfree(compsuffix); if (unset(COMPLETEINWORD)) { - /* Maybe we'll have to do quoting here some time. */ - tmp = dupstring(s); + tmp = quotename(s, NULL); untokenize(tmp); compprefix = ztrdup(tmp); compsuffix = ztrdup(""); } else { char *ss, sav; - tmp = dupstring(s); - ss = tmp + offs; + ss = s + offs; sav = *ss; *ss = '\0'; + tmp = quotename(s, NULL); untokenize(tmp); compprefix = ztrdup(tmp); *ss = sav; + ss = quotename(ss, NULL); untokenize(ss); compsuffix = ztrdup(ss); } @@ -4611,6 +4652,7 @@ callcompfunc(char *s, char *fn) compqisuffix = ztrdup(qisuf ? qisuf : ""); compcurrent = (usea ? (clwpos + 1 - aadd) : 0); compnmatches = mnum; + compnnmatches = nmnum; zsfree(complist); switch (uselist) { @@ -4825,13 +4867,13 @@ makecomplist(char *s, int incmd, int lst) if (!validlist) lastambig = 0; amatches = NULL; - mnum = 0; + mnum = nmnum = 0; unambig_mnum = -1; isuf = NULL; insmnum = insgnum = 1; insgroup = oldlist = oldins = 0; begcmgroup("default", 0); - menucmp = 0; + menucmp = menuacc = 0; ccused = newlinklist(); ccstack = newlinklist(); @@ -4984,6 +5026,8 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) memcpy(tmp + sl + 1, s, noffs); tmp[(scs = cs = sl + 1 + noffs)] = 'x'; strcpy(tmp + sl + 2 + noffs, s + noffs); + if (incompfunc) + tmp = rembslash(tmp); inpush(dupstrspace(tmp), 0, NULL); line = (unsigned char *) tmp; ll = tl - 1; @@ -5013,6 +5057,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) p = NULL; if (!got && !zleparse) { DPUTS(!p, "no current word in substr"); + got = 1; cur = i; swb = wb - 1; swe = we - 1; @@ -5077,7 +5122,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) } sav = s[(i = swb - sl - 1)]; s[i] = '\0'; - qp = tricat(qipre, s, ""); + qp = tricat(qipre, (incompfunc ? rembslash(s) : s), ""); s[i] = sav; if (swe < swb) swe = swb; @@ -5085,7 +5130,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) sl = strlen(s); if (swe > sl) swe = sl, ns[swe - swb + 1] = '\0'; - qs = tricat(s + swe, qisuf, ""); + qs = tricat((incompfunc ? rembslash(s + swe) : s + swe), qisuf, ""); sl = strlen(ns); if (soffs > sl) soffs = sl; @@ -5172,7 +5217,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) compisuffix = ztrdup(""); zsfree(compqiprefix); zsfree(compqisuffix); - if (instring) { + if (ois) { compqiprefix = qp; compqisuffix = qs; } else { @@ -5188,6 +5233,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec) p = compwords[i] = (char *) getdata(n); untokenize(p); } + compcurrent = cur + 1; compwords[i] = NULL; } autoq = oaq; @@ -5242,6 +5288,7 @@ makecomplistcall(Compctl cc) inbackt = oib; autoq = oaq; compnmatches = mnum; + compnnmatches = nmnum; } LASTALLOC; } SWITCHBACKHEAPS; @@ -5317,6 +5364,7 @@ makecomplistctl(int flags) autoq = oaq; offs = ooffs; compnmatches = mnum; + compnnmatches = nmnum; zsfree(cmdstr); freearray(clwords); cmdstr = os; @@ -5979,7 +6027,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) char save = line[cs]; line[cs] = 0; - lppre = dupstring((char *) (line + wb)); + lppre = dupstring((char *) line + wb + + (qipre && *qipre ? + (strlen(qipre) - + (*qipre == '\'' || *qipre == '\"')) : 0)); line[cs] = save; if (brbeg && *brbeg) strcpy(lppre + qbrpl, lppre + qbrpl + strlen(brbeg)); @@ -5998,11 +6049,17 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) lppl = 0; } if (cs != we) { - char save = line[we]; + int end = we; + char save = line[end]; + + if (qisuf && *qisuf) { + int ql = strlen(qisuf); - line[we] = 0; + end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"'); + } + line[end] = 0; lpsuf = dupstring((char *) (line + cs)); - line[we] = save; + line[end] = save; if (brend && *brend) { char *p = lpsuf + qbrsl - (cs - wb); @@ -6640,7 +6697,8 @@ invalidatelist(void) listmatches(); if (validlist) freematches(); - lastambig = menucmp = validlist = showinglist = fromcomp = 0; + lastambig = menucmp = menuacc = validlist = showinglist = + fromcomp = listshown = 0; minfo.cur = NULL; minfo.asked = 0; compwidget = NULL; @@ -6917,6 +6975,8 @@ dupmatch(Cmatch m) r->rems = ztrdup(m->rems); r->remf = ztrdup(m->remf); r->autoq = m->autoq; + r->qipl = m->qipl; + r->qisl = m->qisl; return r; } @@ -7346,7 +7406,9 @@ instmatch(Cmatch m, int *scs) /* Ignored prefix. */ if (m->ipre) { - inststrlen(m->ipre, 1, (l = strlen(m->ipre))); + char *p = m->ipre + (menuacc ? m->qipl : 0); + + inststrlen(p, 1, (l = strlen(p))); r += l; } /* -P prefix. */ @@ -7413,7 +7475,8 @@ static int do_ambiguous(void) { int ret = 0; - menucmp = 0; + + menucmp = menuacc = 0; /* If we have to insert the first match, call do_single(). This is * * how REC_EXACT takes effect. We effectively turn the ambiguous * @@ -7501,11 +7564,13 @@ do_ambiguous(void) * if it is needed. */ if (isset(LISTBEEP)) ret = 1; - if (uselist && (usemenu != 2 || (!showinglist && !oldlist)) && + + if (uselist && (usemenu != 2 || (!listshown && !oldlist)) && ((!showinglist && (!listshown || !oldlist)) || (usemenu == 3 && !oldlist)) && (smatches >= 2 || (compforcelist && *compforcelist))) showinglist = -2; + return ret; } @@ -7649,7 +7714,7 @@ do_single(Cmatch m) } } if (!minfo.insc) - cs = minfo.pos + minfo.len; + cs = minfo.pos + minfo.len - m->qisl; } /* If completing in a brace expansion... */ if (brbeg) { @@ -7688,8 +7753,11 @@ do_single(Cmatch m) if (minfo.we && m->ripre && isset(AUTOPARAMKEYS)) makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq); - if ((menucmp && !minfo.we) || !movetoend) + if ((menucmp && !minfo.we) || !movetoend) { cs = minfo.end; + if (cs + m->qisl == lastend) + cs += minfo.insc; + } { Cmatch *om = minfo.cur; struct chdata dat; @@ -7732,6 +7800,7 @@ do_ambig_menu(void) if (usemenu != 3) { menucmp = 1; + menuacc = 0; minfo.cur = NULL; } else { if (oldlist) { @@ -7931,13 +8000,6 @@ ilistmatches(Hookdef dummy, Chdata dat) int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0; int of = isset(LISTTYPES), opl = 0; - /* Set the cursor below the prompt. */ - trashzle(); - showinglist = listshown = 0; - - clearflag = (isset(USEZLE) && !termflags && - complastprompt && *complastprompt); - for (g = amatches; g; g = g->next) { char **pp = g->ylist; int nl = 0, l; @@ -8013,6 +8075,16 @@ ilistmatches(Hookdef dummy, Chdata dat) nlines += 1 + ((1 + niceztrlen(m->str)) / columns); } } + if (!nlines) { + showinglist = listshown = 0; + return 1; + } + /* Set the cursor below the prompt. */ + trashzle(); + showinglist = listshown = 0; + + clearflag = (isset(USEZLE) && !termflags && + complastprompt && *complastprompt); /* Maybe we have to ask if the user wants to see the list. */ if ((!minfo.cur || !minfo.asked) && @@ -8152,25 +8224,25 @@ ilistmatches(Hookdef dummy, Chdata dat) pnl = 1; g = g->next; } - if (clearflag) { /* Move the cursor up to the prompt, if always_last_prompt * * is set and all that... */ if ((nlines += nlnct - 1) < lines) { tcmultout(TCUP, TCMULTUP, nlines); showinglist = -1; - listshown = 1; } else clearflag = 0, putc('\n', shout); } else putc('\n', shout); + listshown = (clearflag ? 1 : -1); + return 0; } /* This is used to print expansions. */ /**/ -void +int listlist(LinkList l) { struct cmgroup dg; @@ -8193,6 +8265,8 @@ listlist(LinkList l) validlist = vl; smatches = sm; complastprompt = oclp; + + return !dg.lcount; } /* Expand the history references. */ diff --git a/Src/builtin.c b/Src/builtin.c index 0e9baf9be..5c6b24601 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -658,6 +658,9 @@ set_pwd_env(void) } } +/* set if we are resolving links to their true paths */ +static int chasinglinks; + /* The main pwd changing function. The real work is done by other * * functions. cd_get_dest() does the initial argument processing; * * cd_do_chdir() actually changes directory, if possible; cd_new_pwd() * @@ -670,7 +673,6 @@ bin_cd(char *nam, char **argv, char *ops, int func) { LinkNode dir; struct stat st1, st2; - int chaselinks; if (isset(RESTRICTED)) { zwarnnam(nam, "restricted", NULL, 0); @@ -694,7 +696,7 @@ bin_cd(char *nam, char **argv, char *ops, int func) for (s = *argv; *++s; ops[STOUC(*s)] = 1); } brk: - chaselinks = ops['P'] || (isset(CHASELINKS) && !ops['L']); + chasinglinks = ops['P'] || (isset(CHASELINKS) && !ops['L']); PERMALLOC { pushnode(dirstack, ztrdup(pwd)); if (!(dir = cd_get_dest(nam, argv, ops, func))) { @@ -702,7 +704,7 @@ bin_cd(char *nam, char **argv, char *ops, int func) LASTALLOC_RETURN 1; } } LASTALLOC; - cd_new_pwd(func, dir, chaselinks); + cd_new_pwd(func, dir); if (stat(unmeta(pwd), &st1) < 0) { zsfree(pwd); @@ -710,7 +712,7 @@ bin_cd(char *nam, char **argv, char *ops, int func) } else if (stat(".", &st2) < 0) chdir(unmeta(pwd)); else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) { - if (chaselinks) { + if (chasinglinks) { zsfree(pwd); pwd = metafy(zgetcwd(), -1, META_DUP); } else { @@ -915,40 +917,49 @@ static char * cd_try_chdir(char *pfix, char *dest, int hard) { char *buf; + int dlen, dochaselinks = 0; /* handle directory prefix */ if (pfix && *pfix) { if (*pfix == '/') buf = tricat(pfix, "/", dest); else { - int pwl = strlen(pwd); int pfl = strlen(pfix); + dlen = strlen(pwd); - buf = zalloc(pwl + pfl + strlen(dest) + 3); + buf = zalloc(dlen + pfl + strlen(dest) + 3); strcpy(buf, pwd); - buf[pwl] = '/'; - strcpy(buf + pwl + 1, pfix); - buf[pwl + 1 + pfl] = '/'; - strcpy(buf + pwl + pfl + 2, dest); + buf[dlen] = '/'; + strcpy(buf + dlen + 1, pfix); + buf[dlen + 1 + pfl] = '/'; + strcpy(buf + dlen + pfl + 2, dest); } } else if (*dest == '/') buf = ztrdup(dest); else { - int pwl = strlen(pwd); + dlen = strlen(pwd); - buf = zalloc(pwl + strlen(dest) + 2); + buf = zalloc(dlen + strlen(dest) + 2); strcpy(buf, pwd); - buf[pwl] = '/'; - strcpy(buf + pwl + 1, dest); + buf[dlen] = '/'; + strcpy(buf + dlen + 1, dest); } - /* Normalise path. See the definition of fixdir() for what this means. */ - fixdir(buf); + /* Normalise path. See the definition of fixdir() for what this means. + * We do not do this if we are chasing links. + */ + if (!chasinglinks) + dochaselinks = fixdir(buf); + else + unmetafy(buf, &dlen); if (lchdir(buf, NULL, hard)) { - zsfree(buf); + free(buf); return NULL; } + /* the chdir succeeded, so decide if we should force links to be chased */ + if (dochaselinks) + chasinglinks = 1; return metafy(buf, -1, META_NOALLOC); } @@ -956,7 +967,7 @@ cd_try_chdir(char *pfix, char *dest, int hard) /**/ static void -cd_new_pwd(int func, LinkNode dir, int chaselinks) +cd_new_pwd(int func, LinkNode dir) { List l; char *new_pwd, *s; @@ -972,7 +983,7 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks) } else if (func == BIN_CD && unset(AUTOPUSHD)) zsfree(getlinknode(dirstack)); - if (chaselinks) { + if (chasinglinks) { s = new_pwd; new_pwd = findpwd(s); zsfree(s); @@ -1039,17 +1050,20 @@ printdirstack(void) } /* Normalise a path. Segments consisting of ., and foo/.. * - * combinations, are removed and the path is unmetafied. */ + * combinations, are removed and the path is unmetafied. + * Returns 1 if we found a ../ path which should force links to + * be chased, 0 otherwise. + */ /**/ -static void +int fixdir(char *src) { - char *dest = src; - char *d0 = dest; -#ifdef __CYGWIN__ + char *dest = src, *d0 = dest; +#ifdef __CYGWIN char *s0 = src; #endif + int ret = 0; /*** if have RFS superroot directory ***/ #ifdef HAVE_SUPERROOT @@ -1081,19 +1095,40 @@ fixdir(char *src) while (dest > d0 + 1 && dest[-1] == '/') dest--; *dest = '\0'; - return; + return ret; } if (src[0] == '.' && src[1] == '.' && - (src[2] == '\0' || src[2] == '/')) { - if (dest > d0 + 1) { - /* remove a foo/.. combination */ - for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--); - if (dest[-1] != '/') - dest--; - } - src++; - while (*++src == '/'); - } else if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) { + (src[2] == '\0' || src[2] == '/')) { + if (isset(CHASEDOTS)) { + ret = 1; + /* and treat as normal path segment */ + } else { + if (dest > d0 + 1) { + /* + * remove a foo/.. combination: + * first check foo exists, else return. + */ + struct stat st; + *dest = '\0'; + if (stat(d0, &st) < 0 || !S_ISDIR(st.st_mode)) { + char *ptrd, *ptrs; + if (dest == src) + *dest = '.'; + for (ptrs = src, ptrd = dest; *ptrs; ptrs++, ptrd++) + *ptrd = (*ptrs == Meta) ? (*++ptrs ^ 32) : *ptrs; + *ptrd = '\0'; + return 1; + } + for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--); + if (dest[-1] != '/') + dest--; + } + src++; + while (*++src == '/'); + continue; + } + } + if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) { /* skip a . section */ while (*++src == '/'); } else { @@ -3249,12 +3284,11 @@ bin_read(char *name, char **args, char *ops, int func) nchars = 1; args++; } - - firstarg = *args; - if (*args && **args == '?') - args++; - /* default result parameter */ + /* This `*args++ : *args' looks a bit weird, but it works around a bug + * in gcc-2.8.1 under DU 4.0. */ + firstarg = (*args && **args == '?' ? *args++ : *args); reply = *args ? *args++ : ops['A'] ? "reply" : "REPLY"; + if (ops['A'] && *args) { zwarnnam(name, "only one array argument allowed", NULL, 0); return 1; diff --git a/Src/exec.c b/Src/exec.c index 9c7a1ceb5..dc281675f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3144,9 +3144,24 @@ static int cancd2(char *s) { struct stat buf; - char *us = unmeta(s); + char *us, *us2 = NULL; + /* + * If CHASEDOTS and CHASELINKS are not set, we want to rationalize the + * path by removing foo/.. combinations in the logical rather than + * the physical path. If either is set, we test the physical path. + */ + if (!isset(CHASEDOTS) && !isset(CHASELINKS)) { + if (*s != '/') + us = tricat(pwd[1] ? pwd : "", "/", s); + else + us = ztrdup(s); + fixdir(us2 = us); + } else + us = unmeta(s); return !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode)); + if (us2) + free(us2); } /**/ diff --git a/Src/glob.c b/Src/glob.c index ea4980b8b..cf22ef923 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -2724,6 +2724,9 @@ charmatch(Comp c, char *x, char *y) * Here we bypass tulower() and tuupper() for speed. */ int xi = (STOUC(UNMETA(x)) & 0xff), yi = (STOUC(UNMETA(y)) & 0xff); + /* A NULL is a real null, since a \000 would be metafied. */ + if (!*x || !*y) + return 0; return xi == yi || (((c->stat & C_IGNCASE) ? ((isupper(xi) ? tolower(xi) : xi) == @@ -2926,7 +2929,10 @@ rangematch(char **patptr, int ch, int rchar) * and optional ^ have already been skipped. */ char *pat = *patptr; -#ifdef HAVE_STRCOLL + /* We don't use strcoll() for ranges, since it can have side + * effects. It's less necessary now we have [:posix:] ranges. + */ +#if 0 char l_buf[2], r_buf[2], ch_buf[2]; ch_buf[0] = ch; @@ -2944,7 +2950,7 @@ rangematch(char **patptr, int ch, int rchar) break; } else if (*pat == '-' && pat[-1] != rchar && pat[1] != Outbrack) { -#ifdef HAVE_STRCOLL +#if 0 l_buf[0] = PPAT(-1); r_buf[0] = PAT(1); if (strcoll(l_buf, ch_buf) <= 0 && diff --git a/Src/lex.c b/Src/lex.c index 33b6598b9..069f9b39b 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -241,6 +241,7 @@ lexsave(void) cmdsp = 0; inredir = 0; hdocs = NULL; + histactive = 0; ls->next = lstack; lstack = ls; diff --git a/Src/options.c b/Src/options.c index 2eb73690e..0207cd232 100644 --- a/Src/options.c +++ b/Src/options.c @@ -91,6 +91,7 @@ static struct optname optns[] = { {NULL, "braceccl", 0, BRACECCL}, {NULL, "bsdecho", OPT_EMULATE|OPT_SH, BSDECHO}, {NULL, "cdablevars", 0, CDABLEVARS}, +{NULL, "chasedots", 0, CHASEDOTS}, {NULL, "chaselinks", 0, CHASELINKS}, {NULL, "clobber", OPT_ALL, CLOBBER}, {NULL, "completealiases", 0, COMPLETEALIASES}, diff --git a/Src/parse.c b/Src/parse.c index 626ffc982..658a66660 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -72,7 +72,13 @@ struct list dummy_list; #define YYERROR { tok = LEXERR; return NULL; } #define YYERRORV { tok = LEXERR; return; } -#define COND_ERROR(X,Y) do{herrflush();zerr(X,Y,0);YYERROR}while(0) +#define COND_ERROR(X,Y) do { \ + zwarn(X,Y,0); \ + herrflush(); \ + if (noerrs != 2) \ + errflag = 1; \ + YYERROR \ +} while(0) #define make_list() allocnode(N_LIST) #define make_sublist() allocnode(N_SUBLIST) @@ -140,11 +146,13 @@ par_event(void) } if (!l) { if (errflag) { - yyerror(); + yyerror(0); return NULL; } + yyerror(1); herrflush(); - yyerror(); + if (noerrs != 2) + errflag = 1; return NULL; } else { l->right = par_event(); @@ -163,7 +171,7 @@ parse_list(void) yylex(); ret = par_list(); if (tok == LEXERR) { - yyerror(); + yyerror(0); return NULL; } return ret; @@ -1480,7 +1488,7 @@ par_cond_multi(char *a, LinkList l) /**/ static void -yyerror(void) +yyerror(int noerr) { int t0; @@ -1488,9 +1496,11 @@ yyerror(void) if (!yytext || !yytext[t0] || yytext[t0] == '\n') break; if (t0 == 20) - zerr("parse error near `%l...'", yytext, 20); + zwarn("parse error near `%l...'", yytext, 20); else if (t0) - zerr("parse error near `%l'", yytext, t0); + zwarn("parse error near `%l'", yytext, t0); else - zerr("parse error", NULL, 0); + zwarn("parse error", NULL, 0); + if (!noerr && noerrs != 2) + errflag = 1; } diff --git a/Src/utils.c b/Src/utils.c index d82f62694..f86c18b16 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -30,23 +30,12 @@ #include "zsh.mdh" #include "utils.pro" -/* Print an error */ - -/**/ -void -zwarnnam(const char *cmd, const char *fmt, const char *str, int num) -{ - int waserr; - - waserr = errflag; - zerrnam(cmd, fmt, str, num); - errflag = waserr; -} - /* name of script being sourced */ /**/ char *scriptname; + +/* Print an error */ /**/ void @@ -57,7 +46,27 @@ zerr(const char *fmt, const char *str, int num) errflag = 1; return; } + zwarn(fmt, str, num); errflag = 1; +} + +/**/ +void +zerrnam(const char *cmd, const char *fmt, const char *str, int num) +{ + if (errflag || noerrs) + return; + + zwarnnam(cmd, fmt, str, num); + errflag = 1; +} + +/**/ +void +zwarn(const char *fmt, const char *str, int num) +{ + if (errflag || noerrs) + return; trashzle(); /* * scriptname is set when sourcing scripts, so that we get the @@ -68,25 +77,29 @@ zerr(const char *fmt, const char *str, int num) nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" : scriptname ? scriptname : argzero, stderr); fputs(": ", stderr); - zerrnam(NULL, fmt, str, num); + zerrmsg(fmt, str, num); } /**/ void -zerrnam(const char *cmd, const char *fmt, const char *str, int num) +zwarnnam(const char *cmd, const char *fmt, const char *str, int num) { - if (cmd) { - if (errflag || noerrs) - return; - errflag = 1; - trashzle(); - if (unset(SHINSTDIN) || locallevel) { - nicezputs(scriptname ? scriptname : argzero, stderr); - fputs(": ", stderr); - } - nicezputs(cmd, stderr); + if (errflag || noerrs) + return; + trashzle(); + if (unset(SHINSTDIN) || locallevel) { + nicezputs(scriptname ? scriptname : argzero, stderr); fputs(": ", stderr); } + nicezputs(cmd, stderr); + fputs(": ", stderr); + zerrmsg(fmt, str, num); +} + +/**/ +void +zerrmsg(const char *fmt, const char *str, int num) +{ while (*fmt) if (*fmt == '%') { fmt++; @@ -302,7 +315,7 @@ slashsplit(char *s) /**/ static int -xsymlinks(char *s, int flag) +xsymlinks(char *s) { char **pp, **opp; char xbuf2[PATH_MAX*2], xbuf3[PATH_MAX*2]; @@ -325,15 +338,9 @@ xsymlinks(char *s, int flag) *p = '\0'; continue; } - if (unset(CHASELINKS)) { - strcat(xbuf, "/"); - strcat(xbuf, *pp); - zsfree(*pp); - continue; - } sprintf(xbuf2, "%s/%s", xbuf, *pp); t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX); - if (t0 == -1 || !flag) { + if (t0 == -1) { strcat(xbuf, "/"); strcat(xbuf, *pp); zsfree(*pp); @@ -342,9 +349,9 @@ xsymlinks(char *s, int flag) metafy(xbuf3, t0, META_NOALLOC); if (*xbuf3 == '/') { strcpy(xbuf, ""); - xsymlinks(xbuf3 + 1, flag); + xsymlinks(xbuf3 + 1); } else - xsymlinks(xbuf3, flag); + xsymlinks(xbuf3); zsfree(*pp); } } @@ -352,19 +359,19 @@ xsymlinks(char *s, int flag) return ret; } -/* expand symlinks in s, and remove other weird things */ +/* + * expand symlinks in s, and remove other weird things: + * note that this always expands symlinks. + */ /**/ char * xsymlink(char *s) { - if (unset(CHASELINKS)) - return ztrdup(s); if (*s != '/') return NULL; *xbuf = '\0'; - if (!xsymlinks(s + 1, 1)) - return ztrdup(s); + xsymlinks(s + 1); if (!*xbuf) return ztrdup("/"); return ztrdup(xbuf); @@ -374,15 +381,10 @@ xsymlink(char *s) void print_if_link(char *s) { - int chase; - if (*s == '/') { - chase = opts[CHASELINKS]; - opts[CHASELINKS] = 1; *xbuf = '\0'; - if (xsymlinks(s + 1, 1)) + if (xsymlinks(s + 1)) printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout); - opts[CHASELINKS] = chase; } } @@ -573,7 +575,8 @@ getnameddir(char *name) /* Retrieve an entry from the password table/database for this user. */ struct passwd *pw; if ((pw = getpwnam(name))) { - char *dir = xsymlink(pw->pw_dir); + char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir) + : ztrdup(pw->pw_dir); adduserdir(name, dir, ND_USERNAME, 1); str = dupstring(dir); zsfree(dir); @@ -3202,7 +3205,7 @@ getkeystring(char *s, int *len, int fromwhere, int *misc) int meta = 0, control = 0; if (fromwhere == 6) - t = tmp; + t = buf = tmp; else if (fromwhere != 4) t = buf = zhalloc(strlen(s) + 1); else { diff --git a/Src/zsh.h b/Src/zsh.h index 2070e9b3f..d2b64b9bb 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1171,6 +1171,7 @@ enum { BRACECCL, BSDECHO, CDABLEVARS, + CHASEDOTS, CHASELINKS, CLOBBER, COMPLETEALIASES, diff --git a/configure.in b/configure.in index a5dbc6413..1452d8c2d 100644 --- a/configure.in +++ b/configure.in @@ -100,7 +100,7 @@ fi]) dnl Do you want large file support, if available? undefine([lfs])dnl AC_ARG_ENABLE(lfs, -[ --enable-lfs turn on support for large files], +[ --disable-lfs turn off support for large files], [lfs="$enableval"], [lfs=yes]) dnl Pathnames for global zsh scripts @@ -183,7 +183,7 @@ AC_SUBST(zlogout)dnl dnl Do you want dynamically loaded binary modules. undefine([dynamic])dnl AC_ARG_ENABLE(dynamic, -[ --enable-dynamic allow dynamically loaded binary modules], +[ --disable-dynamic turn off dynamically loaded binary modules], [dynamic="$enableval"], [dynamic=yes]) dnl Do you want to compile as K&R C. @@ -599,7 +599,7 @@ main() { return sizeof(ino_t) < 8; } if test $lfs != no -o $zsh_cv_off_t_is_64_bit = yes \ -o $zsh_cv_ino_t_is_64_bit = yes; then AC_CACHE_CHECK(if compiler has a 64 bit type, zsh_cv_64_bit_type, - [if test $lfs != xyes -a $lfs != xno; then + [if test $lfs != yes -a $lfs != no; then zsh_64_BIT_TYPE(${lfs}, zsh_cv_64_bit_type, force) else zsh_64_BIT_TYPE(long long, zsh_cv_64_bit_type) |