From 7a40d6c258ad87d147ee5d6839e746c33ebc0ac7 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Thu, 15 Apr 1999 18:12:56 +0000 Subject: zsh-3.1.5-pws-6 --- Config/version.mk | 4 +- Doc/Makefile.in | 11 +- Doc/Zsh/builtins.yo | 20 +- Doc/Zsh/expn.yo | 108 +++++- Doc/ztexi.yo | 6 + Functions/zless | 37 ++ Functions/zls | 37 +- INSTALL | 15 +- Makefile.in | 6 +- Misc/new-completion-examples | 519 ++++++++++++++++++-------- Src/Zle/comp.h | 5 + Src/Zle/comp1.c | 8 +- Src/Zle/comp1.export | 3 +- Src/Zle/compctl.c | 67 ++-- Src/Zle/iwidgets.list | 2 +- Src/Zle/zle.h | 24 +- Src/Zle/zle_main.c | 29 +- Src/Zle/zle_params.c | 23 +- Src/Zle/zle_thingy.c | 57 +-- Src/Zle/zle_tricky.c | 847 +++++++++++++++++++++++-------------------- Src/builtin.c | 132 +++++-- Src/glob.c | 321 ++++++++++++++-- Src/init.c | 45 ++- Src/lex.c | 5 +- Src/params.c | 66 ++-- Src/subst.c | 45 ++- Src/zsh.export | 1 + Src/zsh.h | 11 +- configure.in | 21 +- patchlist.txt | 70 +++- 30 files changed, 1747 insertions(+), 798 deletions(-) create mode 100644 Functions/zless diff --git a/Config/version.mk b/Config/version.mk index 05e8483fd..29f486012 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-5 -VERSION_DATE='January 19, 1998' +VERSION=3.1.5-pws-6 +VERSION_DATE='January 28, 1998' diff --git a/Doc/Makefile.in b/Doc/Makefile.in index 1f8fa5ff9..e22f3c71d 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -112,11 +112,11 @@ version.yo: $(sdir_top)/Config/version.mk # ========== DEPENDENCIES FOR INSTALLING ========== -# install all the documentation -install: install.man install.info +# install just installs the manual pages +install: install.man -# uninstall all the documentation -uninstall: uninstall.man uninstall.info +# uninstall just unistalls the manual pages +uninstall: uninstall.man # install man pages, creating install directory if necessary install.man: $(MAN) @@ -141,6 +141,9 @@ install.info: zsh.info else :; \ fi || exit 1; \ done + if $(SHELL) -c 'install-info --version' >/dev/null 2>&1; then \ + install-info --dir-file=$(infodir)/dir $(infodir)/zsh.info; \ + else true; fi install.html: zsh_toc.html $(sdir_top)/mkinstalldirs $(htmldir) diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 82562fcbe..98110d122 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -878,7 +878,10 @@ Equivalent to tt(whence -v). findex(typeset) cindex(parameters, setting) cindex(parameters, declaring) -item(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafilrtuxm) [var(n)]] [ var(name)[tt(=)var(value)] ... ])( +xitem(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafilrtuxm) [var(n)]] [ \ +var(name)[tt(=)var(value)] ... ]) +item(tt(typeset) -T [ {tt(PLUS()|tt(-))}tt(LRUZrux) ] \ + var(SCALAR)[tt(=)var(value)] var(array))( Set or display attributes and values for shell parameters. A parameter is created for each var(name) that does not already refer @@ -891,7 +894,7 @@ ifnzman(noderef(Local Parameters))\ which case the parameter is exported em(only) when var(name) does not already appear in the environment. -For each nofill(var(name)tt(=)var(value)) assignment, the parameter +For each var(name)tt(=)var(value) assignment, the parameter var(name) set to var(value). Note that arrays currently cannot be assigned in tt(typeset) expressions; scalars and integers only. @@ -899,6 +902,19 @@ For each remaining var(name) that refers to a parameter that is set, the name and value of the parameter are printed in the form of an assignment. Nothing is printed for newly-created parameters. +If the tt(-T) option is given, exactly two (or zero) var(name) +arguments must be present. They represent a scalar and an array (in +that order) that will be tied together in the manner of tt($PATH) and +tt($path). In other words, an array present in the latter variable +appears as a scalar with the elements of the array joined by colons in +the former. Only the scalar may have an initial value. Both the +scalar and the array may otherwise be manipulated as normal. If one +is unset, the other will automatically be unset too. There is no way +of untying the variables without unsetting them, or converting the +type of one them with another tt(typeset) command; tt(+T) does not work, +assigning an array to var(SCALAR) is an error, and assigning a scalar +to var(array) sets it to be a single-element array. + If no var(name) is present, the names and values of all parameters are printed. In this case the attribute flags restrict the the display to only those parameters that have the specified attributes. Using diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 32cc27f4e..87ef13d06 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -602,6 +602,46 @@ item(tt(f))( Split the result of the expansion to lines. This is a shorthand for `tt(ps:\n:)'. ) +item(tt(t))( +Don't work on the value of the parameter, but on a string describing +the type of the parameter. This string consists of keywords separated +by hyphens (`tt(-)'). The first keyword in the string describes the +main type, it can be one of `tt(scalar)', `tt(array)', `tt(integer)', +or `tt(association)'. The other keywords describe the type in more +detail: + +startitem() +item(`tt(left)')( +for left justified parameters +) +item(`tt(right_blanks)')( +for right justified parameters with leading blanks +) +item(`tt(right_zeros)')( +for right justified parameters with leading zeros +) +item(`tt(lower)')( +for parameters whose value is converted to all lower case when it is +expanded +) +item(`tt(upper)')( +for parameters whose value is converted to all upper case when it is +expanded +) +item(`tt(readonly)')( +for readonly parameters +) +item(`tt(tag)')( +for tagged parameters +) +item(`tt(export)')( +for exported parameters +) +item(`tt(unique)')( +for arrays which keep only the first occurrence of duplicated values +) +enditem() +) enditem() The following flags are meaningful with the tt(${)...tt(#)...tt(}), @@ -1048,6 +1088,49 @@ setgid files (02000) item(tt(t))( files with the sticky bit (01000) ) +item(tt(o)var(spec))( +files with access rights matching var(spec). This var(spec) may be a +octal number optionally preceded by a `tt(=)', a `tt(PLUS())', or a +`tt(-)'. If none of these characters is given, the behavior is the +same as for `tt(=)'. The octal number decribes the mode bits to be +expected, if combined with a `tt(=)', the value given must match the +file-modes exactly, with a `tt(PLUS())', at least the bits in the +given number must be set in the file-modes, and with a `tt(-)', the +bits in the number must not be set. Giving a `tt(?)' instead of a +octal digit anywhere in the number ensures that the corresponding bits +inthe file-modes are not checked, this is only useful in combination +with `tt(=)'. + +If the qualifier `tt(o)' is followed by any other character anything +up to the next matching character (`tt([)', `tt({)', and `tt(<)' match +`tt(])', `tt(})', and `tt(>)' respectively, any other character +matches itself) is taken as a list of comma-separated +var(sub-spec)s. Each var(sub-spec) may be either a octal number as +described above or a list of any of the characters `tt(u)', `tt(g)', +`tt(o)', and `tt(a)', followed by a `tt(=)', a `tt(PLUS())', or a +`tt(-)', followed by a list of any of the characters `tt(r)', `tt(w)', +`tt(x)', `tt(s)', and `tt(t)', or a octal digit. The first list of +characters specify which acess rights are to be checked. If a `tt(u)' +is given, those for the owner of the file are used, if a `tt(g)' is +given, those of the group are checked, a `tt(o)' means to test those +of other users, and the `tt(a)' says to test all three groups. The +`tt(=)', `tt(PLUS())', and `tt(-)' again says how the modes are to be +checked and have the same meaning as described for the first form +above. The second list of characters finally says which access rights +are to be expected: `tt(r)' for read access, `tt(w)' for write access, +`tt(x)' for the right to execute the file (or to search a directory), +`tt(s)' for the setuid and setgid bits, and `tt(t)' for the sticky +bit. + +Thus, `tt(*(o70?))' 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 +`tt(*(o-100))' gives all files for which the owner does not have +execute permission, and `tt(*(o:gu+w,o-rx))' gives the files for which +the owner and the other members of the group have at least write +permission, and fo which other users don't have read or execute +permission. +) item(tt(d)var(dev))( files on the device var(dev) ) @@ -1065,8 +1148,8 @@ item(tt(u)var(id))( files owned by user ID var(id) if it is a number, if not, than the character after the `tt(u)' will be used as a separator and the string between it and the next matching separator -(`tt(LPAR())', `tt([)', `tt({)', and `tt(<)' -match `tt(RPAR())', `tt(])', `tt(})', and `tt(>)' respectively, +(`tt([)', `tt({)', and `tt(<)' +match `tt(])', `tt(})', and `tt(>)' respectively, any other character matches itself) will be taken as a user name, and the user ID of this user will be taken (e.g. `tt(u:foo:)' or `tt(u[foo])' for user `tt(foo)') @@ -1122,6 +1205,27 @@ item(tt(D))( sets the tt(GLOB_DOTS) option for the current pattern pindex(GLOB_DOTS, setting in pattern) ) +item(tt(O)var(c))( +specifies how the names of the files should be sorted. If var(c) is +tt(n) they are sorted by name (the default), if var(c) is tt(L) they +are sorted depending on the size (length) of the files, tt(l) makes +them be sorted by the number of links, and tt(a), tt(m), and tt(c) +make them be sorted by the time of the last access, modification, and +inode change respectively. Note that tt(a), tt(m), and tt(c) compare +the age to the current time, so the first name in the list is the +one of the youngest file. Also note that the modifiers tt(^) and tt(-) are +used, so `tt(*(^-OL))' gives a list of all files sorted by file size in +descending order working not on symbolic links but on the files they +point to. +) +item(tt([)var(beg)[tt(,)var(end)]tt(]))( +specifies which of the matched filenames should be included in the +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. +) enditem() More than one of these lists can be combined, separated by commas. The diff --git a/Doc/ztexi.yo b/Doc/ztexi.yo index b8e115ec0..7388c61aa 100644 --- a/Doc/ztexi.yo +++ b/Doc/ztexi.yo @@ -57,6 +57,12 @@ def(texinfo)(2)(\ NOTRANS(@iftex)NL()\ NOTRANS(@set dsq '{}')NL()\ NOTRANS(@end iftex)NL()\ + NOTRANS(@ifinfo)NL()\ + NOTRANS(@dircategory Utilities)NL()\ + NOTRANS(@direntry)NL()\ + NOTRANS( * ZSH: (zsh). The Z Shell Guide.)NL()\ + NOTRANS(@end direntry)NL()\ + NOTRANS(@end ifinfo)NL()\ ) def(texiifinfo)(1)(\ diff --git a/Functions/zless b/Functions/zless new file mode 100644 index 000000000..809ce35c7 --- /dev/null +++ b/Functions/zless @@ -0,0 +1,37 @@ +#!/usr/bin/zsh -f +# +# zsh function script to run less on various inputs, decompressing as required. +# Author: Phil Pennock. zsh-hacks@athenaeum.demon.co.uk +# Modified by Bart Schaefer. +# Thanks to zefram@fysh.org for a great deal of help in sorting this out, +# ie wrt syntax for unsetting members of arrays and eval "$(...)" when I +# asked for something better than . =(...) +# +# Use -zforce to pass through a display-formatting command +# zless -zforce 'bzip2 -dc' foo-no-dotbz2 +# zless -zforce 'od -hc' foo-binfile +# +# If you can understand all of this without reference to zshexpn(1) +# and zshparam(1) then you either have a photographic memory or you +# need to get out more. +# + +emulate -R zsh +setopt localoptions + +[[ $# -ge 1 ]] || return +local lessopts +set -A lessopts +integer i=1 loi=1 +while ((i <= $#)) +do + case $argv[i] in + -zforce) argv[i,i+2]=("=($argv[i+1] \"$argv[i+2]\")"); ((++i));; + -*) lessopts[loi++]=\"$argv[i]\"; argv[i]=(); continue;; + *.(gz|Z)) argv[i]="=(zcat \"$argv[i]\")";; + *.bz2) argv[i]="=(bzip2 -dc \"$argv[i]\")";; + *.bz) argv[i]="=(bzip -dc \"$argv[i]\")";; + esac + ((++i)) +done +eval command less $lessopts $* diff --git a/Functions/zls b/Functions/zls index da6eff856..f2299903c 100644 --- a/Functions/zls +++ b/Functions/zls @@ -6,12 +6,13 @@ zmodload -i stat || return 1 emulate -R zsh setopt localoptions -local f stat opts='' L=L mod=: dirs list +local f opts='' L=L mod=: dirs list +typeset -A stat dirs=() list=() -while getopts ailLFd f +while getopts ailLFdtuc f do opts=$opts$f if [[ $f == '?' ]] then @@ -25,24 +26,40 @@ shift OPTIND-1 [[ $opts == *F* ]] && mod=T$mod [[ $opts == *a* ]] && setopt globdots +local time=mtime tmod=m +[[ $opts == *u* ]] && time=atime tmod=a +[[ $opts == *c* ]] && time=ctime tmod=c + if ((! ARGC)) then - set * + if [[ $opts = *t* ]]; then + set *(O$tmod) + else + set * + fi opts=d$opts +elif [[ $opts = *t* && $ARGC -gt 1 ]]; then + # another glaringly obvious zsh trick: reorder the argv list + # by time, without messing up metacharacters inside + local n='$1' + for (( f = 2; f <= $ARGC; f++ )); do + n="$n|\$$f" + done + eval "argv=(($n)(O$tmod))" fi for f in $* do - stat -s$L -A stat -F "%b %e %H:%M" - $f || continue - if [[ $opts != *d* && $stat[3] == d* ]] then + stat -s$L -H stat -F "%b %e %H:%M" - $f || continue + if [[ $opts != *d* && $stat[mode] == d* ]] then dirs=( $dirs $f ) elif [[ $opts == *l* ]] then - [[ $opts == *i* ]] && print -n "${(l:7:)stat[2]} " - [[ -n $stat[14] ]] && f=( $f '->' $stat[14] ) || f=( $f($mod) ) - print -r -- "$stat[3] ${(l:3:)stat[4]} ${(r:8:)stat[5]} " \ - "${(r:8:)stat[6]} ${(l:8:)stat[8]} $stat[10] $f" + [[ $opts == *i* ]] && print -n "${(l:7:)stat[inode]} " + [[ -n $stat[link] ]] && f=( $f '->' $stat[link] ) || f=( $f($mod) ) + print -r -- "$stat[mode] ${(l:3:)stat[nlink]} ${(r:8:)stat[uid]} " \ + "${(r:8:)stat[gid]} ${(l:8:)stat[size]} $stat[$time] $f" else f=( $f($mod) ) - list=( "$list[@]" "${${(M)opts:%*i*}:+${(l:7:)stat[2]} }$f" ) + list=( "$list[@]" "${${(M)opts:%*i*}:+${(l:7:)stat[inode]} }$f" ) fi done (($#list)) && print -cr -- "$list[@]" diff --git a/INSTALL b/INSTALL index 4019a16b8..1ed6847e3 100644 --- a/INSTALL +++ b/INSTALL @@ -114,15 +114,18 @@ To install the dynamically-loadable modules, do the command: To install the zsh man page, do the command: make install.man -To install the zsh info files, do the command: - make install.info - Or alternatively, you can install all the above with the command: make install -"make install.info" will only move the info files into the info directory. -You will have to edit the topmost node of the info tree "dir" manually -in order to have the zsh info files available to your info reader. +To install the zsh info files (this must be done separately), do the +command: + make install.info + +If the programme install-info is available, "make install.info" will +insert an entry in the file "dir" in the same directory as the info +files. Otherwise you will have to edit the topmost node of the info +tree "dir" manually in order to have the zsh info files available to +your info reader. Building Zsh On Additional Architectures ---------------------------------------- diff --git a/Makefile.in b/Makefile.in index b2be5bf44..498e3bfd9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -55,9 +55,9 @@ META-FAQ: FORCE install-strip: $(MAKE) install STRIPFLAGS="-s" -# install/uninstall everything -install: install.bin install.modules install.man install.info -uninstall: uninstall.bin uninstall.modules uninstall.man uninstall.info +# install/uninstall everything (except info) +install: install.bin install.modules install.man +uninstall: uninstall.bin uninstall.modules uninstall.man # install/uninstall just the binary install.bin uninstall.bin: diff --git a/Misc/new-completion-examples b/Misc/new-completion-examples index 659679891..b98e523d6 100644 --- a/Misc/new-completion-examples +++ b/Misc/new-completion-examples @@ -19,21 +19,31 @@ typeset -A comps # With only one argument the function/variable-name __$1 is used. defcomp() { - local v + local v a='' + if [[ "$1" = -a ]] then + shift + a=yes + fi if [[ $# -eq 1 ]] then comps[$1]="__$1" + [[ -z "$a" ]] || autoload "__$1" else v="$1" shift for i; do comps[$i]="$v" done + [[ -z "$a" ]] || autoload "$v" fi } defpatcomp() { - if [[ ${+patcomps} == 1 ]] then + if [[ "$1" = -a ]] then + shift + autoload "$1" + fi + if (( $+patcomps )) then patcomps=("$patcomps[@]" "$2 $1" ) else patcomps=( "$2 $1" ) @@ -68,10 +78,10 @@ call-complete() { local var eval var\=\$\{\+$1\} - if [[ "$var" == 0 ]] then - "$@" - else + if (( var )); then eval complist \$\{${1}\[\@\]\} + else + "$@" fi } @@ -80,11 +90,21 @@ call-complete() { # arguments from the command line are gives as positional parameters. main-complete() { - emulate -R zsh + # emulate -R zsh local comp + setopt localoptions nullglob rcexpandparam globdots unsetopt markdirs globsubst shwordsplit nounset + # We first try the `compctl's. This is without first (-T) and default (-D) + # completion. If you want them add `-T' and/or `-D' to this command. + # If this produces any matches, we don't try new style completion. If you + # want to have that tried anyway, remove the `[[ -nmatches ... ]] ...' + # below. + + compcall + [[ -nmatches 0 ]] || return + # 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' @@ -164,21 +184,6 @@ do-complete() { [[ -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. @@ -190,90 +195,123 @@ __precmd() { # so you may want to modify this. pfiles() { - local nm str pa pre epre a b c s rest + local nm ostr opa pre epre a b c s rest ppres setopt localoptions nullglob rcexpandparam globdots extendedglob unsetopt markdirs globsubst shwordsplit nounset + if [[ "$1" = -W ]] then + a="$2" + if [[ "$a[1]" = '(' ]] then + ppres=( $a[2,-2]/ ) + else + eval ppres\=\( \$$a/ \) + [[ $#ppres -eq 0 ]] && ppres=( $a/ ) + fi + [[ $#ppres -eq 0 ]] && ppres=( '' ) + shift 2 + else + ppres=( '' ) + fi + + str="$PREFIX*$SUFFIX" + + if [[ -z "$a[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]] then + a=() + else + a=(-W "( $ppres )") + fi nm=$NMATCHES if [[ $# -eq 0 ]] then - complist -f + complist "$a[@]" -f elif [[ "$1" = -g ]] then - complist -g "$argv[2,-1]" + complist "$a[@]" -g "$argv[2,-1]" shift else - complist $1 + complist "$a[@]" $1 shift fi [[ -nmatches nm ]] || return - str="$PREFIX*$SUFFIX" - [[ -z "$1" ]] && 1='*' - if [[ $str[1] = \~ ]] then + + if [[ "$str[1]" = \~ ]] then pre="${str%%/*}/" eval epre\=$pre str="${str#*/}" - pa='' + opa='' + ppres=( '' ) else pre='' epre='' - if [[ $str[1] = / ]] then + if [[ "$str[1]" = / ]] then str="$str[2,-1]" - pa='/' + opa='/' + ppres=( '' ) else - pa='' + [[ "$str" = (.|..)/* ]] && ppres=( '' ) + opa='' 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%%/*}" + [[ -e "$epre$opa${str%%/*}" ]] || break + opa="$opa${str%%/*}/" + str="${str#*/}" + done + + ostr="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*." + + for ppre in "$ppres[@]"; do + str="$ostr" + pa="$opa" + while [[ "$str" = */* ]] do + rest="${str#*/}" + a="${ppre}${epre}${pa}(#l)${str%%/*}(-/)" + a=( $~a ) + if [[ $#a -eq 0 ]] then + continue 2 + 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 - return + if [[ $#c -eq 0 ]] then + continue 2 + elif [[ $#c -ne 1 ]] then + a="$ppre$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 + continue 2 + fi + a=( "$c[1]" ) fi - a=( "$c[1]" ) - fi - a="$a[1]" - pa="$pa${a##*/}/" - str="$rest" + a="$a[1]" + pa="$pa${a##*/}/" + str="$rest" + done + a="$ppre$epre$pa" + s=( $str$@ ) + s=( "${(@)s:gs.**.*.}" ) + b=( $~a(#l)$~s ) + eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \) + compadd -p "$pre$pa" -W "$ppre$epre$pa" -f ${b#$a} 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 + local nm=$NMATCHES - nm=$NMATCHES pfiles "$@" [[ $# -ne 0 && -nmatches nm ]] && pfiles @@ -289,51 +327,247 @@ __default() { defcomp __command --command-- __command=( -c ) -defcomp __math --math-- -__math=( -v ) +defcomp __vars --math-- +__vars=( -v ) defcomp __subscr --subscr-- __subscr() { + local t + + eval t\=\$\{\(t\)$COMMAND\} compalso --math-- "$@" - # ...probably other stuff + [[ $t = assoc* ]] && eval complist -k \"\(\$\{\(k\)$COMMAND\}\)\" } -# A simple pattern completion, just as an example. +# Do sub-completion for pre-command modifiers. -defpatcomp __x_options '*/X11/*' -__x_options() { - complist -J options -k '(-display -name -xrm)' +defcomp __precmd - nohup nice eval time rusage noglob nocorrect exec +__precmd() { + COMMAND="$1" + shift + (( CURRENT-- )) + if [[ CURRENT -eq 0 ]] then + CONTEXT=command + else + CONTEXT=argument + fi + compsub +} + +defcomp builtin +__builtin() { + if [[ -position 2 -1 ]] then + compsub + else + complist -eB + fi } -# A better example: completion for `find'. +defcomp command +__command() { + if [[ -position 2 -1 ]] then + compsub + else + complist -em + fi +} + +# Various completions... -defcomp find -__find() { - compsave +defcomp __jobs fg jobs +__jobs=(-j -P '%') - if [[ -mbetween -(ok|exec) \\\; ]] then +defcomp __bjobs bg +__bjobs=(-z -P '%') + +defcomp __arrays shift +__arrays=(-A) + +defcomp __which which whence where type +__which=( -caF ) + +defcomp unhash +__unhash() { + [[ -mword 1 -*d* ]] && complist -n + [[ -mword 1 -*a* ]] && complist -a + [[ -mword 1 -*f* ]] && complist -F + [[ ! -mword 1 -* ]] && complist -m +} + +defcomp hash +__hash() { + if [[ -mword 1 -*d* ]] then + if [[ -string 1 '=' ]] then + pfiles -g '*(-/)' + else + complist -n -q -S '=' + fi + elif [[ -string 1 '=' ]] then + files -g '*(*)' '*(-/)' + else + complist -m -q -S '=' + fi +} + +defcomp __funcs unfunction +__funcs=(-F) + +defcomp echotc +__echotc=(-k '(al dc dl do le up al bl cd ce cl cr dc dl do ho is le ma nd nl se so up)') + +defcomp __aliases unalias +__aliases=(-a) + +defcomp __vars getopts read unset vared + +defcomp __varseq declare export integer local readonly typeset +__varseq=(-v -S '=') + +defcomp disable +__disable() { + [[ -mcurrent -1 -*a* ]] && complist -ea + [[ -mcurrent -1 -*f* ]] && complist -eF + [[ -mcurrent -1 -*r* ]] && complist -ew + [[ ! -mcurrent -1 -* ]] && complist -eB +} + +defcomp enable +__enable() { + [[ -mcurrent -1 -*a* ]] && complist -da + [[ -mcurrent -1 -*f* ]] && complist -dF + [[ -mcurrent -1 -*r* ]] && complist -dw + [[ ! -mcurrent -1 -* ]] && complist -dB +} + +defcomp __limits limit unlimit +__limits=(-k "(${(j: :)${(f)$(limit)}%% *})") + +defcomp source +__source() { + if [[ -position 2 -1 ]] 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 + else 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 setopt +__setopt() { + local nm=$NMATCHES + + complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \ + -s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)' + [[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o +} + +defcomp unsetopt +__unsetopt() { + local nm=$NMATCHES + + complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \ + -s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)' + [[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o +} + +defcomp autoload +__autoload=(-s '${^fpath}/*(N:t)') + +defcomp bindkey +__bindkey() { + if [[ -mword 1 -*[DAN]* || -mcurrent -1 -*M ]] then + complist -s '$(bindkey -l)' + else + complist -b + fi +} + +defcomp fc +__fc() { + if [[ -mcurrent -1 -*e ]] then + complist -c + elif [[ -mcurrent -1 -[ARWI]## ]] then + files + fi +} + +defcomp sched +__sched() { + [[ -position 2 -1 ]] && compsub +} + +defcomp set +__set() { + if [[ -mcurrent -1 [-+]o ]] then + complist -o + elif [[ -current -1 -A ]] then + complist -A + fi +} + +defcomp zle +__zle() { + if [[ -word 1 -N && -position 3 ]] then + complist -F + else + complist -b + fi +} + +defcomp zmodload +__zmodload() { + if [[ -mword 1 -*(a*u|u*a)* || -mword 1 -*a* && -position 3 -1 ]] then + complist -B + elif [[ -mword 1 -*u* ]] then + complist -s '$(zmodload)' + else + complist -s '${^module_path}/*(N:t:r)' + fi +} + +defcomp trap +__trap() { + if [[ -position 1 ]] then + complist -c + else + complist -k signals + fi +} + +killfunc() { + reply=( "$(ps -x 2>/dev/null)" ) +} + +defcomp kill +__kill() { + if [[ -iprefix '-' ]] then + complist -k "($signals[1,-3])" + else + complist -P '%' -j + complist -y killfunc -s '`ps -x 2>/dev/null | tail +2 | cut -c1-5`' + fi +} + +defcomp wait +__wait() { + complist -P '%' -j + complist -y killfunc -s '`ps -x 2>/dev/null | tail +2 | cut -c1-5`' +} + +defcomp cd +__cd() { + files -W cdpath -g '*(-/)' +} + +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 __gunzip gunzip zcat __gunzip() { @@ -350,7 +584,7 @@ __xfig() { files -g '*.fig' } -defcomp __make make gmake +defcomp __make make gmake pmake __make() { complist -s "\$(awk '/^[a-zA-Z0-9][^/ ]+:/ {print \$1}' FS=: [mM]akefile)" } @@ -360,42 +594,16 @@ __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 +defcomp __dirs rmdir df du dircmp __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' @@ -406,29 +614,11 @@ __compress() { files -g '*~*.Z' } -defcomp __tex tex latex glatex slitex gslitex +defcomp __tex tex latex slitex __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)' @@ -436,11 +626,9 @@ __pdf() { defcomp tar __tar() { - local nm tf + local nm=$NMATCHES tf="$2" compsave - tf="$2" - nm=$NMATCHES if [[ ( -mword 1 *t*f* || -mword 1 *x*f* ) && -position 3 100000 ]] then complist -k "( $(tar tf $tf) )" compreset @@ -451,3 +639,36 @@ __tar() { files -g '*.(tar|TAR)' fi } + +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 +} + +# A simple pattern completion, just as an example. + +defpatcomp __x_options '*/X11/*' +__x_options() { + complist -J options -k '(-display -name -xrm)' +} diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index afd55b7f1..20e3d2cce 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -267,3 +267,8 @@ struct cline { #define CLF_MISS 4 #define CLF_DIFF 8 #define CLF_SUF 16 + +/* Flags for makecomplist*(). Things not to do. */ + +#define CFN_FIRST 1 +#define CFN_DEFAULT 2 diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c index 42bc92bb2..36588a385 100644 --- a/Src/Zle/comp1.c +++ b/Src/Zle/comp1.c @@ -43,12 +43,6 @@ Cmlist cmatcher; /* pointers to functions required by zle and defined by compctl */ -/**/ -void (*printcompctlptr) _((char *, Compctl, int, int)); - -/**/ -Compctl (*compctl_widgetptr) _((char *, char **)); - /**/ void (*makecompparamsptr) _((void)); @@ -66,6 +60,8 @@ int (*getcpatptr) _((char *, int, char *, int)); /**/ void (*makecomplistcallptr) _((Compctl)); +/**/ +void (*makecomplistctlptr) _((int)); /* Hash table for completion info for commands */ diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export index c90161740..72581173b 100644 --- a/Src/Zle/comp1.export +++ b/Src/Zle/comp1.export @@ -11,7 +11,6 @@ clwsize cmatcher compcommand compcontext -compctl_widgetptr compctltab compcurrent compiprefix @@ -28,8 +27,8 @@ incompctlfunc incompfunc instring makecomplistcallptr +makecomplistctlptr makecompparamsptr patcomps -printcompctlptr quotename rembslash diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 1913d3828..879042cf3 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1638,38 +1638,6 @@ bin_compctl(char *name, char **argv, char *ops, int func) return ret; } -/* Externally callable version of get_compctl. Used for completion widgets */ - -/**/ -static Compctl -compctl_widget(char *name, char **argv) -{ - Compctl cc = (Compctl) zcalloc(sizeof(*cc)); - cclist = 0; - showmask = 0; - - if (get_compctl(name, &argv, cc, 1, 0, 0)) { - freecompctl(cc); - return NULL; - } - - if (cclist & COMP_REMOVE) { - zwarnnam(name, "use -D to delete widget", NULL, 0); - return NULL; - } else if (cclist) { - zwarnnam(name, "special options illegal in widget", NULL, 0); - freecompctl(cc); - return NULL; - } else if (*argv) { - zwarnnam(name, "command names illegal in widget", NULL, 0); - freecompctl(cc); - return NULL; - } - cc->refc++; - - return cc; -} - /**/ static int bin_complist(char *name, char **argv, char *ops, int func) @@ -1677,7 +1645,7 @@ bin_complist(char *name, char **argv, char *ops, int func) Compctl cc; int ret = 0; - if (!incompfunc) { + if (incompfunc != 1) { zerrnam(name, "can only be called from completion function", NULL, 0); return 1; } @@ -1706,7 +1674,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) char *pre = NULL, *suf = NULL, *group = NULL; int f = 0, q = 0, m = 0, ns = 0, a = 0; - if (!incompfunc) { + if (incompfunc != 1) { zerrnam(name, "can only be called from completion function", NULL, 0); return 1; } @@ -1792,15 +1760,27 @@ bin_compadd(char *name, char **argv, char *ops, int func) } } ca_args: - if (!*argv) { - zerrnam(name, "missing completions", NULL, 0); + if (!*argv) return 1; - } + addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group, f, q, m, ns, a, argv); return 0; } +/**/ +static int +bin_compcall(char *name, char **argv, char *ops, int func) +{ + if (incompfunc != 1) { + zerrnam(name, "can only be called from completion function", NULL, 0); + return 1; + } + makecomplistctlptr((ops['T'] ? 0 : CFN_FIRST) | + (ops['D'] ? 0 : CFN_DEFAULT)); + return 0; +} + #define VAR(X) ((void *) (&(X))) static struct compparam { char *name; @@ -1823,7 +1803,7 @@ void makecompparams(void) struct compparam *cp; for (cp = compparams; cp->name; cp++) { - Param pm = createparam(cp->name, cp->type | PM_SPECIAL); + Param pm = createparam(cp->name, cp->type | PM_SPECIAL|PM_REMOVABLE); if (!pm) pm = (Param) paramtab->getnode(paramtab, cp->name); DPUTS(!pm, "param not set in makecompparams"); @@ -1856,7 +1836,7 @@ compunsetfn(Param pm, int exp) static int comp_wrapper(List list, FuncWrap w, char *name) { - if (!incompfunc) + if (incompfunc != 1) return 1; else { char *octxt, *ocmd, *opre, *osuf, *oipre; @@ -1907,7 +1887,7 @@ ignore_prefix(int l) static int comp_check(void) { - if (!incompfunc) { + if (incompfunc != 1) { zerr("condition can only be used in completion function", NULL, 0); return 0; } @@ -2119,7 +2099,8 @@ cond_nmatches(char **a, int id) 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), + BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL), + BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL), }; static struct conddef cotab[] = { @@ -2149,8 +2130,6 @@ int setup_compctl(Module m) { compctltab->printnode = printcompctlp; - printcompctlptr = printcompctl; - compctl_widgetptr = compctl_widget; makecompparamsptr = makecompparams; return 0; } @@ -2183,8 +2162,6 @@ int finish_compctl(Module m) { compctltab->printnode = NULL; - printcompctlptr = NULL; - compctl_widgetptr = NULL; makecompparamsptr = NULL; return 0; } diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index 12425d872..61ad0e24a 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -26,7 +26,7 @@ "capitalize-word", capitalizeword, 0 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP -"copy-prev-word", copyprevword, 0 +"copy-prev-word", copyprevword, ZLE_KEEPSUFFIX "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX "delete-char", deletechar, ZLE_KEEPSUFFIX "delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index f12505bd3..144bb1996 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -46,7 +46,6 @@ struct widget { union { 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 */ @@ -56,20 +55,15 @@ struct widget { }; #define WIDGET_INT (1<<0) /* widget is internally implemented */ -#define WIDGET_COMP (1<<1) /* Special completion widget */ -#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 */ +#define WIDGET_NCOMP (1<<1) /* new style 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 ZLE_KEEPSUFFIX (1<<7) /* DON'T remove added suffix */ +#define ZLE_NOTCOMMAND (1<<8) /* widget should not alter lastcmd */ +#define ZLE_ISCOMP (1<<9) /* usable for new style completion */ /* thingies */ struct thingy { diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 2c0d3655e..03549b5d0 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -45,10 +45,10 @@ int mark; /**/ int c; -/* the binding for this key */ +/* the bindings for the previous and for this key */ /**/ -Thingy bindk; +Thingy lbindk, bindk; /* insert mode/overwrite mode flag */ @@ -554,6 +554,7 @@ zleread(char *lp, char *rp, int ha) void execzlefunc(Thingy func) { + int r = 0; Widget w; if(func->flags & DISABLED) { @@ -565,14 +566,13 @@ execzlefunc(Thingy func) showmsg(msg); zsfree(msg); feep(); - } else if((w = func->widget)->flags & - (WIDGET_INT|WIDGET_COMP | WIDGET_NCOMP)) { + } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) { int wflags = w->flags; if(!(wflags & ZLE_KEEPSUFFIX)) removesuffix(); if(!(wflags & ZLE_MENUCMP) || - ((wflags & (WIDGET_COMP|WIDGET_NCOMP)) && compwidget != w)) { + ((wflags & 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. @@ -584,16 +584,14 @@ execzlefunc(Thingy func) vilinerange = 1; if(!(wflags & ZLE_LASTCOL)) lastcol = -1; - if (wflags & WIDGET_COMP) { - compwidget = w; - completespecial(); - } else if (wflags & WIDGET_NCOMP) { + if (wflags & WIDGET_NCOMP) { compwidget = w; completecall(); } else w->u.fn(); if (!(wflags & ZLE_NOTCOMMAND)) lastcmd = wflags; + r = 1; } else { List l = getshfunc(w->u.fnnam); @@ -610,14 +608,20 @@ execzlefunc(Thingy func) int osc = sfcontext; startparamscope(); - makezleparams(); + makezleparams(0); sfcontext = SFC_WIDGET; doshfunc(w->u.fnnam, l, NULL, 0, 1); sfcontext = osc; endparamscope(); lastcmd = 0; + r = 1; } } + if (r) { + unrefthingy(lbindk); + refthingy(func); + lbindk = func; + } } /* initialise command modifiers */ @@ -877,9 +881,11 @@ setup_zle(Module m) comp_strptr = comp_str; getcpatptr = getcpat; makecomplistcallptr = makecomplistcall; + makecomplistctlptr = makecomplistctl; /* initialise the thingies */ init_thingies(); + lbindk = NULL; /* miscellaneous initialisations */ stackhist = stackcs = -1; @@ -920,6 +926,8 @@ finish_zle(Module m) { int i; + unrefthingy(lbindk); + cleanup_keymaps(); deletehashtable(thingytab); @@ -944,6 +952,7 @@ finish_zle(Module m) comp_strptr = NULL; getcpatptr = NULL; makecomplistcallptr = NULL; + makecomplistctlptr = NULL; return 0; } diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c index e0c4e94ec..74f905ef4 100644 --- a/Src/Zle/zle_params.c +++ b/Src/Zle/zle_params.c @@ -61,17 +61,22 @@ static struct zleparam { zleunsetfn, NULL }, { "RBUFFER", PM_SCALAR, FN(set_rbuffer), FN(get_rbuffer), zleunsetfn, NULL }, + { "WIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_widget), + zleunsetfn, NULL }, + { "LASTWIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_lwidget), + zleunsetfn, NULL }, { NULL, 0, NULL, NULL, NULL, NULL } }; /**/ void -makezleparams(void) +makezleparams(int ro) { struct zleparam *zp; for(zp = zleparams; zp->name; zp++) { - Param pm = createparam(zp->name, zp->type | PM_SPECIAL); + Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE| + (ro ? PM_READONLY : 0))); if (!pm) pm = (Param) paramtab->getnode(paramtab, zp->name); DPUTS(!pm, "param not set in makezleparams"); @@ -197,3 +202,17 @@ get_rbuffer(Param pm) { return metafy((char *)line + cs, ll - cs, META_HEAPDUP); } + +/**/ +static char * +get_widget(Param pm) +{ + return bindk->nam; +} + +/**/ +static char * +get_lwidget(Param pm) +{ + return (lbindk ? lbindk->nam : ""); +} diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 2e21b5add..629d5a84e 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -244,9 +244,7 @@ unbindwidget(Thingy t, int override) static void freewidget(Widget w) { - if ((w->flags & WIDGET_COMP) && w->u.cc) - freecompctl(w->u.cc); - else if (w->flags & WIDGET_NCOMP) { + if (w->flags & WIDGET_NCOMP) { zsfree(w->u.comp.wid); zsfree(w->u.comp.func); } else if(!(w->flags & WIDGET_INT)) @@ -339,7 +337,7 @@ bin_zle(char *name, char **args, char *ops, int func) { 'D', bin_zle_del, 1, -1 }, { 'A', bin_zle_link, 2, 2 }, { 'N', bin_zle_new, 1, 2 }, - { 'C', bin_zle_compctl, 1, -1}, + { 'C', bin_zle_complete, 3, 3 }, { 'c', bin_zle_complete, 3, 3 }, { 0, bin_zle_call, 0, -1 }, }; @@ -392,14 +390,11 @@ scanlistwidgets(HashNode hn, int list) if(w->flags & WIDGET_INT) return; if(list) { - fputs((w->flags & WIDGET_COMP) ? "zle -C " : "zle -N ", stdout); + fputs("zle -N ", stdout); if(t->nam[0] == '-') fputs("-- ", stdout); quotedzputs(t->nam, stdout); - if (w->flags & WIDGET_COMP) { - if (printcompctlptr && w->u.cc) - printcompctlptr(NULL, w->u.cc, PRINT_LIST, 0); - } else if (w->flags & WIDGET_NCOMP) { + if (w->flags & WIDGET_NCOMP) { fputc(' ', stdout); quotedzputs(w->u.comp.wid, stdout); fputc(' ', stdout); @@ -410,11 +405,7 @@ scanlistwidgets(HashNode hn, int list) } } else { nicezputs(t->nam, stdout); - if (w->flags & WIDGET_COMP) { - fputs(" -C", stdout); - if (printcompctlptr && w->u.cc) - printcompctlptr(NULL, w->u.cc, PRINT_TYPE, 0); - } else if (w->flags & WIDGET_NCOMP) { + if (w->flags & WIDGET_NCOMP) { fputs(" -c ", stdout); nicezputs(w->u.comp.wid, stdout); fputc(' ', stdout); @@ -480,44 +471,6 @@ bin_zle_new(char *name, char **args, char *ops, char func) return 1; } -/**/ -static int -bin_zle_compctl(char *name, char **args, char *ops, char func) -{ - Compctl cc = NULL; - Widget w; - char *wname = args[0]; - - if (!compctl_widgetptr) { - zwarnnam(name, "compctl module is not loaded", NULL, 0); - return 1; - } - - args++; - - if (*args && !(cc = compctl_widgetptr(name, args))) - return 1; - - w = zalloc(sizeof(*w)); - w->flags = WIDGET_COMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX; - w->first = NULL; - w->u.cc = cc; - if(bindwidget(w, rthingy(wname))) { - freewidget(w); - zerrnam(name, "widget name `%s' is protected", wname, 0); - return 1; - } - if (ops['m']) - w->flags |= ZLE_USEMENU; - else if (ops['M']) - w->flags |= ZLE_NOMENU; - if (ops['g']) - w->flags |= ZLE_USEGLOB; - else if (ops['G']) - w->flags |= ZLE_NOGLOB; - return 0; -} - /**/ static int bin_zle_complete(char *name, char **args, char *ops, char func) diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index a958752ca..df3e11f46 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -98,10 +98,12 @@ static int menupos, menulen, menuend, menuwe, menuinsc; /* This is for completion inside a brace expansion. brbeg and brend hold * * strings that were temporarily removed from the string to complete. * - * brpl and brsl hold the offset of these strings. */ + * brpl and brsl hold the offset of these strings. * + * brpcs and brscs hold the positions of the re-inserted string in the * + * line. */ static char *brbeg = NULL, *brend = NULL; -static int brpl, brsl; +static int brpl, brsl, brpcs, brscs; /* The list of matches. fmatches contains the matches we first ignore * * because of fignore. */ @@ -261,7 +263,6 @@ usetab(void) } enum { COMP_COMPLETE, - COMP_WIDGET, COMP_LIST_COMPLETE, COMP_SPELL, COMP_EXPAND, @@ -269,18 +270,6 @@ enum { COMP_COMPLETE, COMP_LIST_EXPAND }; #define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND) -/**/ -void -completespecial(void) -{ - int flags = compwidget->flags; - usemenu = (flags & ZLE_USEMENU) ? 1 : (flags & ZLE_NOMENU) ? 0 - : isset(MENUCOMPLETE); - useglob = (flags & ZLE_USEGLOB) ? 1 : (flags & ZLE_NOGLOB) ? 0 - : isset(GLOBCOMPLETE); - docomplete(compwidget->u.cc ? COMP_WIDGET : COMP_COMPLETE); -} - /**/ void completecall(void) @@ -426,19 +415,30 @@ reversemenucomplete(void) void acceptandmenucomplete(void) { - int sl = suffixlen[' ']; - if (!menucmp) { feep(); return; } - cs = menupos + menulen + menuinsc; - if (sl) - backdel(sl); - inststrlen(" ", 1, 1); - menuinsc = menulen = 0; - menupos = cs; - menuwe = 1; + if (brbeg && *brbeg) { + int l = (brscs >= 0 ? brscs : cs) - brpcs; + + zsfree(brbeg); + brbeg = (char *) zalloc(l + 2); + memcpy(brbeg, line + brpcs, l); + brbeg[l] = ','; + brbeg[l + 1] = '\0'; + } else { + int sl = suffixlen[' ']; + + cs = menupos + menulen + menuinsc; + if (sl) + backdel(sl); + + inststrlen(" ", 1, 1); + menuinsc = menulen = 0; + menupos = cs; + menuwe = 1; + } menucomplete(); } @@ -447,6 +447,10 @@ acceptandmenucomplete(void) static int lincmd, linredir; +/* The string for the redirection operator. */ + +static char *rdstr; + /* Non-zero if the last completion done was ambiguous (used to find * * out if AUTOMENU should start). More precisely, it's nonzero after * * successfully doing any completion, unless the completion was * @@ -998,6 +1002,8 @@ get_comp_string(void) oins = ins; /* Get the next token. */ ctxtlex(); + if (inredir) + rdstr = tokstrings[tok]; if (tok == DINPAR) tokstr = NULL; @@ -1859,7 +1865,8 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out) /* Do the matching for a prefix. */ static char * -match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) +match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, + Cmatcher nm) { static unsigned char *ea; static int ealen = 0; @@ -1867,61 +1874,27 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) static int rwlen; int ll = strlen(l), lw = strlen(w), mlw; - int il = 0, iw = 0, t, stil, stiw, std, bc = brpl; - char *nw = rw, *stl = NULL, *stw; + int il = 0, iw = 0, t, bc = brpl; + char *nw = rw; Cmlist ms; - Cmatcher mp, stm; + Cmatcher mp, lm = NULL; Cline lr = NULL; - *nlp = NULL; + if (nlp) { + *nlp = NULL; - if (ll > ealen) { - /* This is the `in'/`out' string for pattern matching. */ - if (ealen) - zfree(ea, ealen); - ea = (unsigned char *) zalloc(ealen = ll + 20); + if (ll > ealen) { + /* This is the `in'/`out' string for pattern matching. */ + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = ll + 20); + } } while (ll && lw) { - if (*l == *w) { - /* Same character, take it. */ - - if (stl) { - /* But first check, if we were collecting characters * - * for a `*'. */ - int sl = iw - stiw; - - nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0); - - addtocline(nlp, &lr, stl, stm->llen, - stw, sl, stm, (std ? CLF_SUF : 0)); - - stl = NULL; - - if (bc <= 0 && bplp) { - *bplp = nw - rw; - bplp = NULL; - } - } - nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0); - - addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0); - - l++; - w++; - il++; - iw++; - ll--; - lw--; - bc--; - - if (bc <= 0 && bplp) { - *bplp = nw - rw; - bplp = NULL; - } - continue; - } for (ms = mstack; ms; ms = ms->next) { for (mp = ms->matcher; mp; mp = mp->next) { + if (nm == mp || lm == mp) + continue; t = 1; /* Try to match the prefix, if any. */ if (mp->flags & CMF_LEFT) { @@ -1947,22 +1920,42 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) else t = 0; } - if (t && !stl) { - /* We simply keep the current position * - * and start collecting characters until * - * another matcher matches. */ - std = (mp->flags & CMF_LEFT); - stl = l; - stil = il; - stw = w; - stiw = iw; - stm = mp; - t = 0; - l += mp->llen; - il += mp->llen; - ll -= mp->llen; - - break; + if (t) { + int i = 0, j = iw, k = lw; + int jj = il + mp->llen, kk = ll - mp->llen; + char *p = l + mp->llen, *q = w; + + for (; k; i++, j++, k--, q++) { + if (match_pfx(p, q, NULL, NULL, + NULL, NULL, mp)) + break; + } + if (k) { + if (nlp) { + nw = addtoword(&rw, &rwlen, nw, mp, + l, w, i, 0); + addtocline(nlp, &lr, l, mp->llen, + w, i, mp, + ((mp->flags & CMF_LEFT) ? + CLF_SUF : 0)); + } + w = q; + iw = j; + lw = k; + l = p; + il = jj; + ll = kk; + bc -= i; + + if (bc <= 0 && bplp) { + *bplp = nw - rw; + bplp = NULL; + } + lm = mp; + break; + } + else + t = 0; } else t = 0; @@ -1988,26 +1981,10 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) if (t) { /* If it matched, build a new chunk on the Cline list * * and add the string to the built match. */ - if (stl) { - int sl = iw - stiw; - - nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0); - - addtocline(nlp, &lr, - stl, stm->llen, stw, sl, stm, - (std ? CLF_SUF : 0)); - - stl = NULL; - - if (bc <= 0 && bplp) { - *bplp = nw - rw; - bplp = NULL; - } + if (nlp) { + nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0); + addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0); } - nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0); - - addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0); - l += mp->llen; w += mlw; ll -= mp->llen; @@ -2023,62 +2000,84 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) break; } } - if (mp) + if (mp) { + if (mp != lm) + lm = NULL; break; + } } - if (!stl && !t) { - if (*nlp) { + if (t) + continue; + if (*l == *w) { + /* Same character, take it. */ + if (nlp) { + nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0); + addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0); + } + l++; + w++; + il++; + iw++; + ll--; + lw--; + bc--; + + if (bc <= 0 && bplp) { + *bplp = nw - rw; + bplp = NULL; + } + lm = NULL; + } else { + if (nlp && *nlp) { lr->next = freecl; freecl = *nlp; } return NULL; } - if (stl) { - /* We are collecting characters, just skip over. */ - w++; - lw--; - iw++; - } } - *lp = iw; + if (lp) + *lp = iw; if (lw) { - /* There is a unmatched portion in the word, keep it. */ - if (rlp) { - w = dupstring(w); - addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID); - - *rlp = lr; - } else { - addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END); + if (nlp) { + /* There is a unmatched portion in the word, keep it. */ + if (rlp) { + w = dupstring(w); + addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID); - nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0); + *rlp = lr; + } else { + addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END); + nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0); + } } - } - else if (rlp) { - if (lr) { + } else if (rlp) { + if (nlp && lr) { lr->next = freecl; freecl = *nlp; } return NULL; } - if (nw) + if (nlp && nw) *nw = '\0'; if (ll) { - if (*nlp) { + if (nlp && *nlp) { lr->next = freecl; freecl = *nlp; } - return 0; + return NULL; } - /* Finally, return the built match string. */ - return dupstring(rw); + if (nlp) + /* Finally, return the built match string. */ + return dupstring(rw); + + return ((char *) 1); } /* Do the matching for a suffix. */ static char * -match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) +match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) { static unsigned char *ea; static int ealen = 0; @@ -2086,60 +2085,29 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) static int rwlen; int ll = strlen(l), lw = strlen(w), mlw; - int il = 0, iw = 0, t, stil, stiw, std, bc = brsl; - char *nw = rw, *stl = NULL, *stw; + int il = 0, iw = 0, t, bc = brsl; + char *nw = rw; Cmlist ms; - Cmatcher mp, stm; + Cmatcher mp, lm = NULL; Cline lr = NULL; l += ll; w += lw; - *nlp = NULL; + if (nlp) { + *nlp = NULL; - if (ll > ealen) { - if (ealen) - zfree(ea, ealen); - ea = (unsigned char *) zalloc(ealen = ll + 20); + if (ll > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = ll + 20); + } } while (ll && lw) { - if (l[-1] == w[-1]) { - if (stl) { - int sl = iw - stiw; - - stl -= stm->llen; - stw -= sl; - nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1); - - addtocline(nlp, &lr, stl, stm->llen, - stw, sl, stm, (std ? CLF_SUF : 0)); - - stl = NULL; - - if (bc <= 0 && bslp) { - *bslp = nw - rw; - bslp = NULL; - } - } - nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1); - - addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0); - - l--; - w--; - il++; - iw++; - ll--; - lw--; - bc--; - if (bc <= 0 && bslp) { - *bslp = nw - rw; - bslp = NULL; - } - continue; - } for (ms = mstack; ms; ms = ms->next) { for (mp = ms->matcher; mp; mp = mp->next) { + if (nm == mp || lm == mp) + continue; t = 1; if (mp->flags & CMF_RIGHT) { if (il < mp->ralen || iw < mp->ralen) @@ -2164,22 +2132,43 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) else t = 0; } - if (t && !stl) { - std = (mp->flags & CMF_LEFT); - stl = l; - stil = il; - stw = w; - stiw = iw; - stm = mp; - t = 0; - l -= mp->llen; - il += mp->llen; - ll -= mp->llen; - - break; + if (t) { + int i = 0, j = iw, k = lw; + int jj = il + mp->llen, kk = ll - mp->llen; + char *p = l - mp->llen, *q = w; + + for (; k; i++, j++, k--, q--) + if (match_sfx(p, q, NULL, NULL, + NULL, mp)) + break; + if (k) { + if (nlp) { + nw = addtoword(&rw, &rwlen, nw, mp, + l - mp->llen, w - i, + i, 1); + addtocline(nlp, &lr, l - mp->llen, + mp->llen, w - i, i, mp, + ((mp->flags & CMF_LEFT) ? + CLF_SUF : 0)); + } + w = q; + iw = j; + lw = k; + l = p; + il = jj; + ll = kk; + bc -= i; + + if (bc <= 0 && bslp) { + *bslp = nw - rw; + bslp = NULL; + } + lm = mp; + break; + } + else + t = 0; } - else - t = 0; } } else { t = pattern_match(mp->line, l - mp->llen, NULL, ea) && @@ -2199,30 +2188,11 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) t = 0; } if (t) { - if (stl) { - int sl = iw - stiw; - - stl -= stm->llen; - stw -= sl; - - nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1); - - addtocline(nlp, &lr, - stl, stm->llen, stw, sl, stm, - (std ? CLF_SUF : 0)); - - stl = NULL; - - if (bc <= 0 && bslp) { - *bslp = nw - rw; - bslp = NULL; - } + if (nlp) { + nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1); + addtocline(nlp, &lr, l - mp->llen, mp->llen, + w - mlw, mlw, mp, 0); } - nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1); - - addtocline(nlp, &lr, l - mp->llen, mp->llen, - w - mlw, mlw, mp, 0); - l -= mp->llen; w -= mlw; ll -= mp->llen; @@ -2237,34 +2207,55 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) break; } } - if (mp) + if (mp) { + if (mp != lm) + lm = NULL; break; + } } - if (!stl && !t) { - if (*nlp) { + if (t) + continue; + if (l[-1] == w[-1]) { + if (nlp) { + nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1); + addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0); + } + l--; + w--; + il++; + iw++; + ll--; + lw--; + bc--; + if (bc <= 0 && bslp) { + *bslp = nw - rw; + bslp = NULL; + } + lm = NULL; + } else { + if (nlp && *nlp) { lr->next = freecl; freecl = *nlp; } return NULL; } - if (stl) { - w--; - lw--; - iw++; - } } - *lp = iw; - if (nw) + if (lp) + *lp = iw; + if (nlp && nw) *nw = '\0'; if (ll) { - if (*nlp) { + if (nlp && *nlp) { lr->next = freecl; freecl = *nlp; } - return 0; + return NULL; } - return dupstring(rw); + if (nlp) + return dupstring(rw); + + return ((char *) 1); } /* Check if the word `w' matches. */ @@ -2283,8 +2274,8 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl int sl; Cline sli, last; - if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) { - if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) { + if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl, NULL))) { + if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl, NULL))) { int pml, sml; last->llen -= sl; @@ -2305,7 +2296,7 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl } else return NULL; - } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl))) + } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl, NULL))) return NULL; if (lppre && *lppre) { @@ -2373,6 +2364,7 @@ instmatch(Cmatch m) if (brbeg && *brbeg) { cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0); l = strlen(brbeg); + brpcs = cs; inststrlen(brbeg, 1, l); r += l; ocs += l; @@ -2385,12 +2377,13 @@ instmatch(Cmatch m) if (brend && *brend) { a = cs; cs -= m->brsl; - ocs = cs; + ocs = brscs = cs; l = strlen(brend); inststrlen(brend, 1, l); r += l; cs = a + l; - } + } else + brscs = -1; if (m->suf) { inststrlen(m->suf, 1, (l = strlen(m->suf))); r += l; @@ -2509,6 +2502,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, if (!ai->firstm) ai->firstm = cm; } + compnmatches = mnum; } LASTALLOC; } SWITCHBACKHEAPS; } @@ -3180,6 +3174,108 @@ docompletion(char *s, int lst, int incmd) } LASTALLOC; } +/* This calls the given function for new style completion. */ + +/**/ +static void +callcompfunc(char *s, char *fn) +{ + List list; + int lv = lastval; + + if ((list = getshfunc(fn)) != &dummy_list) { + LinkList args = newlinklist(); + char **p, *tmp; + int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext; + + addlinknode(args, fn); + + 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"; + if (rdstr) + compcommand = rdstr; + } 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(); + makezleparams(1); + sfcontext = SFC_CWIDGET; + NEWHEAPS(compheap) { + doshfunc(fn, list, args, 0, 1); + } OLDHEAPS; + sfcontext = osc; + endparamscope(); + lastcmd = 0; + incompfunc = icf; + } + lastval = lv; +} + /* The beginning and end of a word range to be used by -l. */ static int brange, erange; @@ -3232,98 +3328,10 @@ makecomplist(char *s, int incmd, int lst) ccused = newlinklist(); ccstack = newlinklist(); - 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); + if (compfunc) + callcompfunc(s, compfunc); + else + makecomplistglobal(s, incmd, lst, 0); endcmgroup(NULL); @@ -3368,11 +3376,11 @@ ctokenize(char *p) if (*p == '\\') bslash = 1; else { - if (*p == '$') { + if (*p == '$' || *p == '=') { if (bslash) p[-1] = Bnull; else - *p = String; + *p = (*p == '$' ? String : Equals); } bslash = 0; } @@ -3431,22 +3439,75 @@ makecomplistcall(Compctl cc) } SWITCHBACKHEAPS; } +/* A simple counter to avoid endless recursion between old and new style * + * completion. */ + +static int cdepth = 0; + +#define MAX_CDEPTH 16 + +/**/ +void +makecomplistctl(int flags) +{ + if (cdepth == MAX_CDEPTH) + return; + + cdepth++; + SWITCHHEAPS(compheap) { + HEAPALLOC { + int ooffs = offs, lip, lp; + char *str = comp_str(&lip, &lp), *t; + char *os = cmdstr, **ow = clwords, **p, **q; + int on = clwnum, op = clwpos; + + clwnum = arrlen(pparams) + 1; + clwpos = compcurrent - 1; + cmdstr = ztrdup(compcommand); + clwords = (char **) zalloc((clwnum + 1) * sizeof(char *)); + clwords[0] = ztrdup(cmdstr); + for (p = pparams, q = clwords + 1; *p; p++, q++) { + t = dupstring(*p); + ctokenize(t); + remnulargs(t); + *q = ztrdup(t); + } + *q = NULL; + offs = lip + lp; + incompfunc = 2; + makecomplistglobal(str, + (!clwpos && !strcmp(compcontext, "command")), + COMP_COMPLETE, flags); + incompfunc = 1; + offs = ooffs; + compnmatches = mnum; + zsfree(cmdstr); + freearray(clwords); + cmdstr = os; + clwords = ow; + clwnum = on; + clwpos = op; + } LASTALLOC; + } SWITCHBACKHEAPS; + cdepth--; +} + /* This function gets the compctls for the given command line and * * adds all completions for them. */ /**/ static void -makecomplistglobal(char *os, int incmd, int lst) +makecomplistglobal(char *os, int incmd, int lst, int flags) { Compctl cc; char *s; - if (lst == COMP_WIDGET) { - cc = compwidget->u.cc; - } else if (inwhat == IN_ENV) + ccont = CC_CCCONT; + + if (inwhat == IN_ENV) { /* Default completion for parameter values. */ cc = &cc_default; - else if (inwhat == IN_MATH) { + } else if (inwhat == IN_MATH) { /* Parameter names inside mathematical expression. */ cc_dummy.mask = CC_PARAMS; cc = &cc_dummy; @@ -3469,16 +3530,17 @@ makecomplistglobal(char *os, int incmd, int lst) cc = &cc_default; else { /* Otherwise get the matches for the command. */ - makecomplistcmd(os, incmd); + makecomplistcmd(os, incmd, flags); cc = NULL; } if (cc) { /* First, use the -T compctl. */ - makecomplistcc(&cc_first, os, incmd); - - if (!(ccont & CC_CCCONT)) - return; + if (!(flags & CFN_FIRST)) { + makecomplistcc(&cc_first, os, incmd); + if (!(ccont & CC_CCCONT)) + return; + } makecomplistcc(cc, os, incmd); } } @@ -3487,18 +3549,19 @@ makecomplistglobal(char *os, int incmd, int lst) /**/ static void -makecomplistcmd(char *os, int incmd) +makecomplistcmd(char *os, int incmd, int flags) { Compctl cc; Compctlp ccp; char *s; /* First, use the -T compctl. */ - makecomplistcc(&cc_first, os, incmd); - - if (!(ccont & CC_CCCONT)) - return; + if (!(flags & CFN_FIRST)) { + makecomplistcc(&cc_first, os, incmd); + if (!(ccont & CC_CCCONT)) + return; + } /* Then search the pattern compctls, with the command name and the * * full pathname of the command. */ makecomplistpc(os, incmd); @@ -3526,9 +3589,11 @@ makecomplistcmd(char *os, int incmd) (cc = ccp->cc)) || ((s = dupstring(cmdstr)) && remlpaths(&s) && (ccp = (Compctlp) compctltab->getnode(compctltab, s)) && - (cc = ccp->cc))))) + (cc = ccp->cc))))) { + if (flags & CFN_DEFAULT) + return; cc = &cc_default; - + } makecomplistcc(cc, os, incmd); } @@ -3817,12 +3882,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT)); - if (!incompfunc && findnode(ccstack, cc)) + if (incompfunc != 1 && findnode(ccstack, cc)) return; addlinknode(ccstack, cc); - if (!incompfunc && allccs) { + if (incompfunc != 1 && allccs) { if (findnode(allccs, cc)) { uremnode(ccstack, firstnode(ccstack)); return; @@ -4430,45 +4495,50 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) /* Add user names. */ maketildelist(); if (cc->func) { - /* This handles the compctl -K flag. */ - List list; - char **r; - int lv = lastval; - - /* Get the function. */ - if ((list = getshfunc(cc->func)) != &dummy_list) { - /* We have it, so build a argument list. */ - LinkList args = newlinklist(); - int osc = sfcontext; - - addlinknode(args, cc->func); - - if (delit) { - p = dupstrpfx(os, ooffs); - untokenize(p); - addlinknode(args, p); - p = dupstring(os + ooffs); - untokenize(p); - addlinknode(args, p); - } else { - addlinknode(args, lpre); - addlinknode(args, lsuf); + if (cc->func[0] == ' ') + /* Temporary hack for access to new style completione. */ + callcompfunc(os, cc->func + 1); + else { + /* This handles the compctl -K flag. */ + List list; + char **r; + int lv = lastval; + + /* Get the function. */ + if ((list = getshfunc(cc->func)) != &dummy_list) { + /* We have it, so build a argument list. */ + LinkList args = newlinklist(); + int osc = sfcontext; + + addlinknode(args, cc->func); + + if (delit) { + p = dupstrpfx(os, ooffs); + untokenize(p); + addlinknode(args, p); + p = dupstring(os + ooffs); + untokenize(p); + addlinknode(args, p); + } else { + addlinknode(args, lpre); + addlinknode(args, lsuf); + } + + /* This flag allows us to use read -l and -c. */ + if (incompfunc != 1) + incompctlfunc = 1; + sfcontext = SFC_COMPLETE; + /* Call the function. */ + doshfunc(cc->func, list, args, 0, 1); + sfcontext = osc; + incompctlfunc = 0; + /* And get the result from the reply parameter. */ + if ((r = get_user_var("reply"))) + while (*r) + addmatch(*r++, NULL); } - - /* This flag allows us to use read -l and -c. */ - if (!incompfunc) - incompctlfunc = 1; - sfcontext = SFC_COMPLETE; - /* Call the function. */ - doshfunc(cc->func, list, args, 0, 1); - sfcontext = osc; - incompctlfunc = 0; - /* And get the result from the reply parameter. */ - if ((r = get_user_var("reply"))) - while (*r) - addmatch(*r++, NULL); + lastval = lv; } - lastval = lv; } if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) { /* Get job names. */ @@ -4625,7 +4695,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) } /* No harm in allowing read -l and -c here, too */ - if (!incompfunc) + if (incompfunc != 1) incompctlfunc = 1; sfcontext = SFC_COMPLETE; doshfunc(cc->ylist, list, args, 0, 1); @@ -4692,7 +4762,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) clwords += brange; } /* Produce the matches. */ - makecomplistcmd(s, incmd); + makecomplistcmd(s, incmd, CFN_FIRST); /* And restore the things we changed. */ clwords = ow; @@ -5021,9 +5091,10 @@ permmatches(void) &nn, &nl); g->mcount = nn; g->lcount = nn - nl; - if (g->ylist) + if (g->ylist) { g->lcount = arrlen(g->ylist); - + smatches = 2; + } g->expls = (Cexpl *) makearray(g->lexpls, 0, &(g->ecount), NULL); g->ccount = 0; diff --git a/Src/builtin.c b/Src/builtin.c index f6941286d..ea1ac8ab9 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -50,7 +50,7 @@ static struct builtin builtins[] = BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL), BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL), BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), - BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafilrtux", NULL), + BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtux", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL), BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), @@ -60,7 +60,7 @@ static struct builtin builtins[] = BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL), BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL), BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL), - BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRUZafilrtu", "x"), + BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafilrtu", "x"), BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL), BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL), BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL), @@ -78,7 +78,7 @@ static struct builtin builtins[] = BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL), BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), - BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZailrtu", NULL), + BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZailrtu", NULL), BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), @@ -93,7 +93,7 @@ static struct builtin builtins[] = BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL), BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL), BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL), - BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafiltux", "r"), + BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafiltux", "r"), BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"), BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL), BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL), @@ -107,7 +107,7 @@ static struct builtin builtins[] = BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL), BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"), - BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafilrtuxm", NULL), + BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtuxm", NULL), BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL), BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"), BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"), @@ -1468,11 +1468,11 @@ getasg(char *s) /* function to set a single parameter */ /**/ -int +Param typeset_single(char *cname, char *pname, Param pm, int func, - int on, int off, int roff, char *value) + int on, int off, int roff, char *value, Param altpm) { - int usepm, tc; + int usepm, tc, keeplocal = 0; /* use the existing pm? */ usepm = pm && !(pm->flags & PM_UNSET); @@ -1490,24 +1490,24 @@ typeset_single(char *cname, char *pname, Param pm, int func, locallevel != pm->level && func != BIN_EXPORT) usepm = 0; - /* attempting a type conversion? */ + /* attempting a type conversion, or making a tied colonarray? */ if ((tc = usepm && (((off & pm->flags) | (on & ~pm->flags)) & - (PM_INTEGER|PM_HASHED|PM_ARRAY)))) + (PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED)))) usepm = 0; if (tc && (pm->flags & PM_SPECIAL)) { zerrnam(cname, "%s: can't change type of a special parameter", pname, 0); - return 1; + return NULL; } if (usepm) { if (!on && !roff && !value) { paramtab->printnode((HashNode)pm, 0); - return 0; + return pm; } if ((pm->flags & PM_RESTRICTED && isset(RESTRICTED))) { zerrnam(cname, "%s: restricted", pname, 0); - return 1; + return pm; } if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) && !(pm->flags & PM_READONLY & ~off)) @@ -1530,9 +1530,9 @@ typeset_single(char *cname, char *pname, Param pm, int func, setsparam(pname, ztrdup(value)); } else if (value) { zwarnnam(cname, "can't assign new value for array %s", pname, 0); - return 1; + return NULL; } - return 0; + return pm; } /* @@ -1542,10 +1542,15 @@ typeset_single(char *cname, char *pname, Param pm, int func, * 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; - } + /* Maintain existing readonly/exported status... */ + on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->flags; + /* ...but turn off existing readonly so we can delete it */ + pm->flags &= ~PM_READONLY; + /* + * If we're just changing the type, we should keep the + * variable at the current level of localness. + */ + keeplocal = pm->level; /* * Try to carry over a value, but not when changing from, * to, or between non-scalar types. @@ -1563,17 +1568,33 @@ typeset_single(char *cname, char *pname, Param pm, int func, pm = createparam(pname, on & ~PM_READONLY); DPUTS(!pm, "BUG: parameter not created"); pm->ct = auxlen; - if (func != BIN_EXPORT) + + if (altpm && PM_TYPE(pm->flags) == PM_SCALAR) { + /* + * It seems safer to set this here than in createparam(), + * to make sure we only ever use the colonarr functions + * when u.data is correctly set. + */ + pm->sets.cfn = colonarrsetfn; + pm->gets.cfn = colonarrgetfn; + pm->u.data = &altpm->u.arr; + } + + if (keeplocal) + pm->level = keeplocal; + else 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; + /* the only safe thing to do here seems to be unset the param */ + unsetparam_pm(pm, 0, 1); + return NULL; } - return 0; + return pm; } /* declare, export, integer, local, readonly, typeset */ @@ -1585,7 +1606,7 @@ bin_typeset(char *name, char **argv, char *ops, int func) Param pm; Asgment asg; Comp com; - char *optstr = "aiALRZlurtxU"; + char *optstr = "aiALRZlurtxUT"; int on = 0, off = 0, roff, bit = PM_ARRAY; int i; int returnval = 0, printflags = 0; @@ -1619,6 +1640,9 @@ bin_typeset(char *name, char **argv, char *ops, int func) off |= PM_UPPER; if (on & PM_HASHED) off |= PM_ARRAY; + if (on & PM_TIED) + off |= PM_INTEGER | PM_ARRAY | PM_HASHED; + on &= ~off; /* Given no arguments, list whatever the options specify. */ @@ -1631,6 +1655,58 @@ bin_typeset(char *name, char **argv, char *ops, int func) return 0; } + if (on & PM_TIED) { + Param apm; + char *name1; + + if (ops['m']) { + zwarnnam(name, "incompatible options for -T", NULL, 0); + return 1; + } + on &= ~off; + if (!argv[1] || argv[2]) { + zwarnnam(name, "-T requires names of scalar and array", NULL, 0); + return 1; + } + + /* + * Create the tied array; this is normal except that + * it has the PM_TIED flag set. Do it first because + * we need the address. + */ + if (!(asg = getasg(argv[1]))) + return 1; + name1 = ztrdup(asg->name); + if (!(apm=typeset_single(name, asg->name, + (Param)paramtab->getnode(paramtab, + asg->name), + func, on | PM_ARRAY, off, roff, + asg->value, NULL))) + return 1; + + /* + * Create the tied colonarray. We make it as a normal scalar + * and fix up the oddities later. + */ + if (!(asg = getasg(argv[0])) || + !(pm=typeset_single(name, asg->name, + (Param)paramtab->getnode(paramtab, + asg->name), + func, on, off, roff, asg->value, apm))) { + unsetparam_pm(apm, 1, 1); + return 1; + } + + pm->ename = name1; + apm->ename = ztrdup(asg->name); + + return 0; + } + if (off & PM_TIED) { + zerrnam(name, "use unset to remove tied variables", NULL, 0); + return 1; + } + /* With the -m option, treat arguments as glob patterns */ if (ops['m']) { MUSTUSEHEAP("typeset -m"); @@ -1664,8 +1740,8 @@ bin_typeset(char *name, char **argv, char *ops, int func) } for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) { pm = (Param) getdata(pmnode); - if (typeset_single(name, pm->nam, pm, func, on, off, roff, - asg->value)) + if (!typeset_single(name, pm->nam, pm, func, on, off, roff, + asg->value, NULL)) returnval = 1; } } @@ -1680,9 +1756,9 @@ bin_typeset(char *name, char **argv, char *ops, int func) returnval = 1; continue; } - if (typeset_single(name, asg->name, - (Param)paramtab->getnode(paramtab, asg->name), - func, on, off, roff, asg->value)) + if (!typeset_single(name, asg->name, + (Param)paramtab->getnode(paramtab, asg->name), + func, on, off, roff, asg->value, NULL)) returnval = 1; } return returnval; diff --git a/Src/glob.c b/Src/glob.c index 6536e0f06..7a3839576 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -31,7 +31,42 @@ #include "glob.pro" /* flag for CSHNULLGLOB */ - + +typedef struct gmatch *Gmatch; + +struct gmatch { + char *name; + long size; + long atime; + long mtime; + long ctime; + long links; + long _size; + long _atime; + long _mtime; + long _ctime; + long _links; +}; + +#define GS_NAME 1 +#define GS_SIZE 2 +#define GS_ATIME 4 +#define GS_MTIME 8 +#define GS_CTIME 16 +#define GS_LINKS 32 + +#define GS_SHIFT 5 +#define GS__SIZE (GS_SIZE << GS_SHIFT) +#define GS__ATIME (GS_ATIME << GS_SHIFT) +#define GS__MTIME (GS_MTIME << GS_SHIFT) +#define GS__CTIME (GS_CTIME << GS_SHIFT) +#define GS__LINKS (GS_LINKS << GS_SHIFT) + +#define GS_DESC 2048 + +#define GS_NORMAL (GS_SIZE | GS_ATIME | GS_MTIME | GS_CTIME | GS_LINKS) +#define GS_LINKED (GS_NORMAL << GS_SHIFT) + /**/ int badcshglob; @@ -42,8 +77,8 @@ static int matchct; /* number of matches found */ static char *pathbuf; /* pathname buffer */ static int pathbufsz; /* size of pathbuf */ static int pathbufcwd; /* where did we chdir()'ed */ -static char **matchbuf; /* array of matches */ -static char **matchptr; /* &matchbuf[matchct] */ +static Gmatch matchbuf; /* array of matches */ +static Gmatch matchptr; /* &matchbuf[matchct] */ static char *colonmod; /* colon modifiers in qualifier list */ typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figure. */ @@ -81,6 +116,7 @@ static struct qual *quals; static int qualct, qualorct; static int range, amc, units; static int gf_nullglob, gf_markdirs, gf_noglobdots, gf_listtypes, gf_follow; +static int gf_sorts, gf_nsorts, gf_sortlist[11]; /* Prefix, suffix for doing zle trickery */ @@ -213,6 +249,7 @@ insert(char *s, int checked) if (!statted && statfullpath(s, &buf, 1)) return; + statted = 1; qo = quals; for (qn = qo; qn && qn->func;) { range = qn->range; @@ -237,19 +274,44 @@ insert(char *s, int checked) } qn = qn->next; } - } else if (!checked && statfullpath(s, NULL, 1)) - return; - + } else if (!checked) { + if (statfullpath(s, NULL, 1)) + return; + statted = 1; + } news = dyncat(pathbuf, news); if (colonmod) { /* Handle the remainder of the qualifer: e.g. (:r:s/foo/bar/). */ s = colonmod; modify(&news, &s); } - *matchptr++ = news; + if (!statted && (gf_sorts & GS_NORMAL)) { + statfullpath(s, &buf, 1); + statted = 1; + } + if (statted != 2 && (gf_sorts & GS_LINKED)) { + if (statted) { + if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0)) + memcpy(&buf2, &buf, sizeof(buf)); + } else if (statfullpath(s, &buf2, 0)) + statfullpath(s, &buf2, 1); + } + matchptr->name = news; + matchptr->size = buf.st_size; + matchptr->atime = buf.st_atime; + matchptr->mtime = buf.st_mtime; + matchptr->ctime = buf.st_ctime; + matchptr->links = buf.st_nlink; + matchptr->_size = buf2.st_size; + matchptr->_atime = buf2.st_atime; + matchptr->_mtime = buf2.st_mtime; + matchptr->_ctime = buf2.st_ctime; + matchptr->_links = buf2.st_nlink; + matchptr++; + if (++matchct == matchsz) { - matchbuf = (char **)realloc((char *)matchbuf, - sizeof(char **) * (matchsz *= 2)); + matchbuf = (Gmatch )realloc((char *)matchbuf, + sizeof(struct gmatch) * (matchsz *= 2)); matchptr = matchbuf + matchct; } @@ -944,21 +1006,144 @@ qgetnum(char **s) return v; } -/* get octal number after qualifier */ +/* get mode spec after qualifier */ /**/ static long -qgetoctnum(char **s) +qgetmodespec(char **s) { - long v = 0; + long yes = 0, no = 0, val, mask, t; + char *p = *s, c, how, end; - if (!idigit(**s)) { - zerr("octal number expected", NULL, 0); - return 0; + if ((c = *p) == '=' || c == Equals || c == '+' || c == '-' || + c == '?' || c == Quest || (c >= '0' && c <= '7')) { + end = 0; + c = 0; + } else { + end = (c == '<' ? '>' : + (c == '[' ? ']' : + (c == '{' ? '}' : + (c == Inang ? Outang : + (c == Inbrack ? Outbrack : + (c == Inbrace ? Outbrace : c)))))); + p++; } - while (**s >= '0' && **s <= '7') - v = v * 010 + *(*s)++ - '0'; - return v; + do { + mask = 0; + while (((c = *p) == 'u' || c == 'g' || c == 'o' || c == 'a') && end) { + switch (c) { + case 'o': mask |= 01007; break; + case 'g': mask |= 02070; break; + case 'u': mask |= 04700; break; + case 'a': mask |= 07777; break; + } + p++; + } + how = ((c == '+' || c == '-') ? c : '='); + if (c == '+' || c == '-' || c == '=' || c == Equals) + p++; + val = 0; + if (mask) { + while ((c = *p++) != ',' && c != end) { + switch (c) { + case 'x': val |= 00111; break; + case 'w': val |= 00222; break; + case 'r': val |= 00444; break; + case 's': val |= 06000; break; + case 't': val |= 01000; break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + t = ((long) c - '0'); + val |= t | (t << 3) | (t << 6); + break; + default: + zerr("invalid mode specification", NULL, 0); + return 0; + } + } + if (how == '=' || how == '+') { + yes |= val & mask; + val = ~val; + } + if (how == '=' || how == '-') + no |= val & mask; + } else { + t = 07777; + while ((c = *p) == '?' || c == Quest || + (c >= '0' && c <= '7')) { + if (c == '?' || c == Quest) { + t = (t << 3) | 7; + val <<= 3; + } else { + t <<= 3; + val = (val << 3) | ((long) c - '0'); + } + p++; + } + if (end && c != end && c != ',') { + zerr("invalid mode specification", NULL, 0); + return 0; + } + if (how == '=') { + yes = (yes & ~t) | val; + no = (no & ~t) | (~val & ~t); + } else if (how == '+') + yes |= val; + else + no |= val; + } + } while (end && c != end); + + *s = p; + return ((yes & 07777) | ((no & 07777) << 12)); +} + +static int +gmatchcmp(Gmatch a, Gmatch b) +{ + int i, *s; + long r; + + for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) { + switch (*s & ~GS_DESC) { + case GS_NAME: + r = notstrcmp(&a->name, &b->name); + break; + case GS_SIZE: + r = b->size - a->size; + break; + case GS_ATIME: + r = a->atime - b->atime; + break; + case GS_MTIME: + r = a->mtime - b->mtime; + break; + case GS_CTIME: + r = a->ctime - b->ctime; + break; + case GS_LINKS: + r = b->links - a->links; + break; + case GS__SIZE: + r = b->_size - a->_size; + break; + case GS__ATIME: + r = a->_atime - b->_atime; + break; + case GS__MTIME: + r = a->_mtime - b->_mtime; + break; + case GS__CTIME: + r = a->_ctime - b->_ctime; + break; + case GS__LINKS: + r = b->_links - a->_links; + break; + } + if (r) + return (int) ((*s & GS_DESC) ? -r : r); + } + return 0; } /* Main entry point to the globbing code for filename globbing. * @@ -976,7 +1161,8 @@ glob(LinkList list, LinkNode np) Complist q; /* pattern after parsing */ char *ostr = (char *)getdata(np); /* the pattern before the parser */ /* chops it up */ - + int first = 0, last = -1; /* index of first/last match to */ + /* return */ MUSTUSEHEAP("glob"); if (unset(GLOBOPT) || !haswilds(ostr)) { untokenize(ostr); @@ -994,6 +1180,7 @@ glob(LinkList list, LinkNode np) gf_markdirs = isset(MARKDIRS); gf_listtypes = gf_follow = 0; gf_noglobdots = unset(GLOBDOTS); + gf_sorts = gf_nsorts = 0; /* Check for qualifiers */ if (isset(BAREGLOBQUAL) && str[sl - 1] == Outpar) { @@ -1246,10 +1433,9 @@ glob(LinkList list, LinkNode np) } break; case 'o': - /* Match octal mode of file exactly. * - * Currently undocumented. */ - func = qualeqflags; - data = qgetoctnum(&s); + /* Match modes with chmod-spec. */ + func = qualmodeflags; + data = qgetmodespec(&s); break; case 'M': /* Mark directories with a / */ @@ -1315,6 +1501,51 @@ glob(LinkList list, LinkNode np) data = qgetnum(&s); break; + case 'O': + { + int t; + + switch (*s) { + case 'n': t = GS_NAME; break; + case 'L': t = GS_SIZE; break; + case 'l': t = GS_LINKS; break; + case 'a': t = GS_ATIME; break; + case 'm': t = GS_MTIME; break; + case 'c': t = GS_CTIME; break; + default: + zerr("unknown sort specifier", NULL, 0); + return; + } + if ((sense & 2) && t != GS_NAME) + t <<= GS_SHIFT; + if (gf_sorts & t) { + zerr("doubled sort specifier", NULL, 0); + return; + } + gf_sorts |= t; + gf_sortlist[gf_nsorts++] = t | + ((sense & 1) ? GS_DESC : 0); + s++; + break; + } + case '[': + case Inbrack: + { + char *os = --s; + struct value v; + + v.isarr = SCANPM_WANTVALS; + v.pm = NULL; + v.b = -1; + v.inv = 0; + if (getindex(&s, &v) || s == os) { + zerr("invalid subscript", NULL, 0); + return; + } + first = v.a; + last = v.b; + break; + } default: zerr("unknown file attribute", NULL, 0); return; @@ -1353,10 +1584,14 @@ glob(LinkList list, LinkNode np) zerr("bad pattern: %s", ostr, 0); return; } - + if (!gf_nsorts) { + gf_sortlist[0] = gf_sorts = GS_NAME; + gf_nsorts = 1; + } /* Initialise receptacle for matched files, * * expanded by insert() where necessary. */ - matchptr = matchbuf = (char **)zalloc((matchsz = 16) * sizeof(char *)); + matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) * + sizeof(struct gmatch)); matchct = 0; /* The actual processing takes place here: matches go into * @@ -1375,18 +1610,32 @@ glob(LinkList list, LinkNode np) return; } else { /* treat as an ordinary string */ - untokenize(*matchptr++ = dupstring(ostr)); + untokenize(matchptr->name = dupstring(ostr)); + matchptr++; matchct = 1; } } /* Sort arguments in to lexical (and possibly numeric) order. * * This is reversed to facilitate insertion into the list. */ - qsort((void *) & matchbuf[0], matchct, sizeof(char *), - (int (*) _((const void *, const void *)))notstrcmp); - - matchptr = matchbuf; - while (matchct--) /* insert matches in the arg list */ - insertlinknode(list, node, *matchptr++); + qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch), + (int (*) _((const void *, const void *)))gmatchcmp); + + if (first < 0) + first += matchct; + if (last < 0) + last += matchct; + if (first < 0) + first = 0; + if (last >= matchct) + last = matchct - 1; + if (first <= last) { + matchptr = matchbuf + matchct - 1 - last; + last -= first; + while (last-- >= 0) { /* insert matches in the arg list */ + insertlinknode(list, node, matchptr->name); + matchptr++; + } + } free(matchbuf); } @@ -2918,13 +3167,15 @@ qualflags(struct stat *buf, long mod) return mode_to_octal(buf->st_mode) & mod; } -/* mode matches number supplied exactly */ +/* mode matches specification */ /**/ static int -qualeqflags(struct stat *buf, long mod) +qualmodeflags(struct stat *buf, long mod) { - return mode_to_octal(buf->st_mode) == mod; + long v = mode_to_octal(buf->st_mode), y = mod & 07777, n = mod >> 12; + + return ((v & y) == y && !(v & n)); } /* regular executable file? */ diff --git a/Src/init.c b/Src/init.c index 0c874eead..5e0a550dd 100644 --- a/Src/init.c +++ b/Src/init.c @@ -300,18 +300,61 @@ init_io(void) /* Make sure the tty is opened read/write. */ if (isatty(0)) { +#ifdef TIOCNXCL + /* + * See if the terminal claims to be busy. If so, and fd 0 + * is a terminal, try and set non-exclusive use for that. + * This is something to do with Solaris over-cleverness. + */ + int tmpfd; + if ((tmpfd = open("/dev/tty", O_RDWR | O_NOCTTY)) < 0) { + if (errno == EBUSY) + ioctl(0, TIOCNXCL, 0); + } else + close(tmpfd); +#endif zsfree(ttystrname); if ((ttystrname = ztrdup(ttyname(0)))) SHTTY = movefd(open(ttystrname, O_RDWR | O_NOCTTY)); + /* + * xterm, rxvt and probably all terminal emulators except + * dtterm on Solaris 2.6 & 7 have a bug. Applications are + * unable to open /dev/tty or /dev/pts/ + * because something in Sun's STREAMS modules doesn't like + * it. The open() call fails with EBUSY which is not even + * listed as a possibility in the open(2) man page. So we'll + * try to outsmart The Company. -- + * + * Presumably there's no harm trying this on any OS, given that + * isatty(0) worked but opening the tty didn't. Possibly we won't + * get the tty read/write, but it's the best we can do -- pws + * + * Try both stdin and stdout before trying /dev/tty. -- Bart + */ +#if defined(HAVE_FCNTL_H) && defined(F_GETFL) +#define rdwrtty(fd) ((fcntl(fd, F_GETFL) & O_RDWR) == O_RDWR) +#else +#define rdwrtty(fd) 1 +#endif + if (SHTTY == -1 && rdwrtty(0)) { + SHTTY = movefd(dup(0)); + } + } + if (SHTTY == -1 && isatty(1) && rdwrtty(1) && + (SHTTY = movefd(dup(1))) != -1) { + zsfree(ttystrname); + ttystrname = ztrdup(ttyname(1)); } if (SHTTY == -1 && (SHTTY = movefd(open("/dev/tty", O_RDWR | O_NOCTTY))) != -1) { zsfree(ttystrname); - ttystrname = ztrdup("/dev/tty"); + ttystrname = ztrdup(ttyname(SHTTY)); } if (SHTTY == -1) { zsfree(ttystrname); ttystrname = ztrdup(""); + } else if (!ttystrname) { + ttystrname = ztrdup("/dev/tty"); } /* We will only use zle if shell is interactive, * diff --git a/Src/lex.c b/Src/lex.c index b08dfed5b..7371243a7 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -109,7 +109,8 @@ int parend; /* text of puctuation tokens */ -static char *tokstrings[WHILE + 1] = { +/**/ +char *tokstrings[WHILE + 1] = { NULL, /* NULLTOK 0 */ ";", /* SEPER */ "\\n", /* NEWLIN */ @@ -120,7 +121,7 @@ static char *tokstrings[WHILE + 1] = { ")", /* OUTPAR */ "||", /* DBAR */ "&&", /* DAMPER */ - ")", /* OUTANG 10 */ + ">", /* OUTANG 10 */ ">|", /* OUTANGBANG */ ">>", /* DOUTANG */ ">>|", /* DOUTANGBANG */ diff --git a/Src/params.c b/Src/params.c index 3dc7e1a1b..f57413a2e 100644 --- a/Src/params.c +++ b/Src/params.c @@ -679,7 +679,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) Comp c; /* first parse any subscription flags */ - if (*s == '(' || *s == Inpar) { + if (v->pm && (*s == '(' || *s == Inpar)) { int escapes = 0; int waste; for (s++; *s != ')' && *s != Outpar && s != *str; s++) { @@ -765,7 +765,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) } else if (rev) { v->isarr |= SCANPM_WANTVALS; } - if (!down && PM_TYPE(v->pm->flags) == PM_HASHED) + if (!down && v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) v->isarr &= ~SCANPM_MATCHMANY; *inv = ind; } @@ -785,7 +785,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) singsub(&s); if (!rev) { - if (PM_TYPE(v->pm->flags) == PM_HASHED) { + if (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) { HashTable ht = v->pm->gets.hfn(v->pm); if (!ht) { ht = newparamtable(17, v->pm->nam); @@ -1371,7 +1371,7 @@ setarrvalue(Value v, char **val) freearray(val); return; } - if (PM_TYPE(v->pm->flags) & ~(PM_ARRAY|PM_HASHED)) { + if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED))) { freearray(val); zerr("attempt to assign array value to non-array", NULL, 0); return; @@ -1501,7 +1501,7 @@ setsparam(char *s, char *val) if (!(v = getvalue(&s, 1))) createparam(t, PM_SCALAR); else if (PM_TYPE(v->pm->flags) == PM_ARRAY && - !(v->pm->flags & PM_SPECIAL) && unset(KSHARRAYS)) { + !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && unset(KSHARRAYS)) { unsetparam(t); createparam(t, PM_SCALAR); v = NULL; @@ -1545,7 +1545,7 @@ setaparam(char *s, char **val) if (!(v = getvalue(&s, 1))) createparam(t, PM_ARRAY); else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) && - !(v->pm->flags & PM_SPECIAL)) { + !(v->pm->flags & (PM_SPECIAL|PM_TIED))) { int uniq = v->pm->flags & PM_UNIQUE; unsetparam(t); createparam(t, PM_ARRAY | uniq); @@ -1662,26 +1662,36 @@ unsetparam_pm(Param pm, int altflag, int exp) unsetparam_pm(altpm, 1, exp); } - /* If this was a local variable, we need to keep the old * - * struct so that it is resurrected at the right level. * - * This is partly because when an array/scalar value is set * - * and the parameter used to be the other sort, unsetparam() * - * is called. Beyond that, there is an ambiguity: should * - * foo() { local bar; unset bar; } make the global bar * - * available or not? The following makes the answer "no". */ - if ((locallevel && locallevel >= pm->level) || (pm->flags & PM_SPECIAL)) + /* + * If this was a local variable, we need to keep the old + * struct so that it is resurrected at the right level. + * This is partly because when an array/scalar value is set + * and the parameter used to be the other sort, unsetparam() + * is called. Beyond that, there is an ambiguity: should + * foo() { local bar; unset bar; } make the global bar + * available or not? The following makes the answer "no". + * + * Some specials, such as those used in zle, still need removing + * from the parameter table; they have the PM_REMOVABLE flag. + */ + if ((locallevel && locallevel >= pm->level) || + (pm->flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL) return; - paramtab->removenode(paramtab, pm->nam); /* remove parameter node from table */ + /* remove parameter node from table */ + paramtab->removenode(paramtab, pm->nam); if (pm->old) { oldpm = pm->old; paramtab->addnode(paramtab, oldpm->nam, oldpm); - if ((PM_TYPE(oldpm->flags) == PM_SCALAR) && oldpm->sets.cfn == strsetfn) + if ((PM_TYPE(oldpm->flags) == PM_SCALAR) && + oldpm->sets.cfn == strsetfn) adduserdir(oldpm->nam, oldpm->u.str, 0, 0); } - paramtab->freenode((HashNode) pm); /* free parameter node */ + /* Even removable specials shouldn't be deleted. */ + if (!(pm->flags & PM_SPECIAL)) + paramtab->freenode((HashNode) pm); /* free parameter node */ } /* Standard function to unset a parameter. This is mostly delegated to * @@ -1759,6 +1769,9 @@ arrsetfn(Param pm, char **x) if (pm->flags & PM_UNIQUE) uniqarray(x); pm->u.arr = x; + /* Arrays tied to colon-arrays may need to fix the environment */ + if (pm->ename && x) + arrfixenv(pm->ename, x); } /* Function to get value of an association parameter */ @@ -1950,7 +1963,8 @@ arrvarsetfn(Param pm, char **x) char * colonarrgetfn(Param pm) { - return zjoin(*(char ***)pm->u.data, ':'); + char ***dptr = (char ***)pm->u.data; + return *dptr ? zjoin(*dptr, ':') : ""; } /**/ @@ -1959,8 +1973,15 @@ colonarrsetfn(Param pm, char *x) { char ***dptr = (char ***)pm->u.data; - freearray(*dptr); - *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) : mkarray(NULL); + /* + * If this is tied to a parameter (rather than internal) array, + * the array itself may be NULL. Otherwise, we have to make + * sure it doesn't ever get null. + */ + if (*dptr) + freearray(*dptr); + *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) : + (pm->flags & PM_TIED) ? NULL : mkarray(NULL); if (pm->ename) arrfixenv(pm->nam, *dptr); zsfree(x); @@ -2399,7 +2420,7 @@ arrfixenv(char *s, char **t) MUSTUSEHEAP("arrfixenv"); if (t == path) cmdnamtab->emptytable(cmdnamtab); - u = zjoin(t, ':'); + u = t ? zjoin(t, ':') : ""; len_s = strlen(s); pm = (Param) paramtab->getnode(paramtab, s); for (ep = environ; *ep; ep++) @@ -2602,6 +2623,9 @@ freeparamnode(HashNode hn) if (delunset) pm->unsetfn(pm, 1); zsfree(pm->nam); + /* If this variable was tied by the user, ename was ztrdup'd */ + if (pm->flags & PM_TIED) + zsfree(pm->ename); zfree(pm, sizeof(struct param)); } diff --git a/Src/subst.c b/Src/subst.c index 5211ee0ee..b77dbd4a4 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -702,6 +702,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) int whichlen = 0; int chkset = 0; int vunset = 0; + int wantt = 0; int spbreak = isset(SHWORDSPLIT) && !ssub && !qt; char *val = NULL, **aval = NULL; unsigned int fwidth = 0; @@ -902,6 +903,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) hvals = SCANPM_WANTVALS; break; + case 't': + wantt = 1; + break; + default: flagerr: zerr("error in flags", NULL, 0); @@ -978,9 +983,47 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) *s = sav; v = (Value) NULL; } else { - if (!(v = fetchvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1, + if (!(v = fetchvalue(&s, (wantt ? -1 : + ((unset(KSHARRAYS) || inbrace) ? 1 : -1)), hkeys|hvals))) vunset = 1; + + if (wantt) { + if (v) { + int f = v->pm->flags; + + switch (PM_TYPE(f)) { + case PM_SCALAR: val = "scalar"; break; + case PM_ARRAY: val = "array"; break; + case PM_INTEGER: val = "integer"; break; + case PM_HASHED: val = "association"; break; + } + val = dupstring(val); + if (f & PM_LEFT) + val = dyncat(val, "-left"); + if (f & PM_RIGHT_B) + val = dyncat(val, "-right_blanks"); + if (f & PM_RIGHT_Z) + val = dyncat(val, "-right_zeros"); + if (f & PM_LOWER) + val = dyncat(val, "-lower"); + if (f & PM_UPPER) + val = dyncat(val, "-upper"); + if (f & PM_READONLY) + val = dyncat(val, "-readonly"); + if (f & PM_TAGGED) + val = dyncat(val, "-tag"); + if (f & PM_EXPORTED) + val = dyncat(val, "-export"); + if (f & PM_UNIQUE) + val = dyncat(val, "-unique"); + vunset = 0; + } else + val = dupstring(""); + + v = NULL; + isarr = 0; + } } while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) { if (!v) { diff --git a/Src/zsh.export b/Src/zsh.export index c85bfbad4..32c0e7d3c 100644 --- a/Src/zsh.export +++ b/Src/zsh.export @@ -214,6 +214,7 @@ tgoto tok tokenize tokstr +tokstrings tputs trashzleptr tricat diff --git a/Src/zsh.h b/Src/zsh.h index dabcd90c8..e23f9c895 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -780,6 +780,7 @@ struct shfunc { #define SFC_HOOK 2 /* one of the special functions */ #define SFC_WIDGET 3 /* user defined widget */ #define SFC_COMPLETE 4 /* called from completion code */ +#define SFC_CWIDGET 5 /* new style completion widget */ /* node in list of function call wrappers */ @@ -916,10 +917,12 @@ struct param { #define PM_TAGGED (1<<9) /* tagged */ #define PM_EXPORTED (1<<10) /* exported */ #define PM_UNIQUE (1<<11) /* remove duplicates */ -#define PM_SPECIAL (1<<12) /* special builtin parameter */ -#define PM_DONTIMPORT (1<<13) /* do not import this variable */ -#define PM_RESTRICTED (1<<14) /* cannot be changed in restricted mode */ -#define PM_UNSET (1<<15) /* has null value */ +#define PM_TIED (1<<12) /* array tied to colon-path or v.v. */ +#define PM_SPECIAL (1<<13) /* special builtin parameter */ +#define PM_DONTIMPORT (1<<14) /* do not import this variable */ +#define PM_RESTRICTED (1<<15) /* cannot be changed in restricted mode */ +#define PM_UNSET (1<<16) /* has null value */ +#define PM_REMOVABLE (1<<17) /* special can be removed from paramtab */ /* Flags for extracting elements of arrays and associative arrays */ #define SCANPM_WANTVALS (1<<0) diff --git a/configure.in b/configure.in index 1b2692ea1..975e02e49 100644 --- a/configure.in +++ b/configure.in @@ -1008,9 +1008,24 @@ char *argv[]; aix*) DLLDFLAGS="${DLLDFLAGS=-G -bexpall -lc}" ;; solaris*|sysv4*|esix*) DLLDFLAGS="${DLLDFLAGS=-G}" ;; esac - case "$host_os" in - hpux*) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" ;; - linux*) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-rdynamic}" ;; + case "$host" in + *-hpux*) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" ;; + *-linux*) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-rdynamic}" ;; + mips-sni-sysv4) + # + # unfortunately, we have different compilers + # that need different flags + # + sni_cc_version=`$CC -V 2>&1 | head -1` + case "$sni_cc_version" in + CDS* ) + EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-Blargedynsym}" + ;; + * ) + EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-LD-Blargedynsym}" + ;; + esac + ;; esac AC_CACHE_CHECK(if your dlsym() needs a leading underscore, zsh_cv_func_dlsym_needs_underscore, diff --git a/patchlist.txt b/patchlist.txt index 6ec3ec351..7a2760329 100644 --- a/patchlist.txt +++ b/patchlist.txt @@ -1,5 +1,5 @@ This version of zsh is based on 3.1.5 and includes the following -patches. (The version number built into the shell has not been changed.) +patches. Old stuff: @@ -111,7 +111,7 @@ My special parameter unset fix, 4662 Third edition I've taken the plunge and changed $ZSH_VERSION, the current one is now -3.1.5.pws-1 . It seemed rational to have something incremental at the +3.1.5.pws-3 . It seemed rational to have something incremental at the end for testing, so I abandoned using the date. 4482 (cdmatch2)and 4641 (${assoc[0]}) now applied; 4641 was supposed @@ -261,8 +261,70 @@ 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. +4946, 4949, 4950, 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. + + pws-6 + +Sven: fix for completion after redirection, 4957 + +Bart: add-on, 4965 + +Andrej: configure patch for Reliant UNIX et al., 5021 (as resubmitted) + +Sven: compctl list with a single string, 4974 + +Sven: compctl -M matches with *'s, 4975, 5007 + +Sven: compadd and new-completion-examples, 4976 + +Sven: funky new glob modifiers: change sort order, select +item from list, 4979; make time order work like ls -t, 4987 + +Sven: fix completion whitespace for copy-previous-word, 4981 + +Sven: fix for new-style completion after redirection, 4986, 4988 + +New mirror site ftp://ftp.win.ne.jp/pub/shell/zsh/ in META-FAQ (not +posted) + +Andrej: when installing info files, insert zsh.info into dir, 5016 + +Sven: ${(t)param} flag, 5022, 5045; no unset behaviour, 5078 + +Phil: zless, 5032, simplified by Bart, 5037, also added a `setopt +localoptions' after spending an hour wondering why nothing worked any +more. + +Me: `make install' does not do `make install.info', 5047 + +Sven: compcall tries old-style completion from new-style function, +compctl -K ' func' handles newstyle completion, 5059; avoid recursion, +5065; my dynamic fix-up, 5085 + +Sven: inserting completion inside brace expansion, 5060 + +Sven: extra completion context, 5092 + +Me: typeset -T MYPATH mypath, 5094, plus fix for MYPATH=(foo), +mypath=foo (and also existing PATH=(foo) bug), 5120 + +Sven: doc fix for glob qualifiers, 5102 + +Drazen Kacar, modified by me: workaround for terminal bug on Solaris, +5103; modified by Bart, 5113 + +Sven: zle and widget information via variables in new completion +functions, 5104 + +Me: remove old zle -C, zle -C now does new completion, 5105 + +Sven: glob qualifier o for modes, 5107 + +Me: fix for unsetting special zle variables, 5111 + +Drazen Kacar, modified by me: unlock terminal device on Solaris, 5118 +(5117 was wrong) -- cgit 1.4.1