diff options
author | Tanaka Akira <akr@users.sourceforge.net> | 1999-04-15 18:16:27 +0000 |
---|---|---|
committer | Tanaka Akira <akr@users.sourceforge.net> | 1999-04-15 18:16:27 +0000 |
commit | b4a5b9db8b528f9c9b6a9cbb00db381c95659380 (patch) | |
tree | 17bf8f93d47de0af7495ec6d11361aef1ca62f9b | |
parent | ba4f5e80ec9d7e145718e79fed6e57a852c86c12 (diff) | |
download | zsh-b4a5b9db8b528f9c9b6a9cbb00db381c95659380.tar.gz zsh-b4a5b9db8b528f9c9b6a9cbb00db381c95659380.tar.xz zsh-b4a5b9db8b528f9c9b6a9cbb00db381c95659380.zip |
zsh-3.1.5-pws-9 zsh-3.1.5-pws-9
99 files changed, 3672 insertions, 478 deletions
diff --git a/Config/version.mk b/Config/version.mk index 675dede6e..3e36faf5c 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-8 -VERSION_DATE='February 13, 1999' +VERSION=3.1.5-pws-9 +VERSION_DATE='February 18, 1999' diff --git a/Doc/Makefile.in b/Doc/Makefile.in index e22f3c71d..7bc5d7ada 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -38,7 +38,7 @@ TEXI2HTML = texi2html -expandinfo -split_chapter .SUFFIXES: .yo .1 # man pages to install -MAN = zsh.1 zshbuiltins.1 zshcompctl.1 \ +MAN = zsh.1 zshbuiltins.1 zshcompctl.1 zshcompwid.1 \ zshexpn.1 zshmisc.1 zshmodules.1 \ zshoptions.1 zshparam.1 zshzle.1 zshall.1 @@ -48,7 +48,7 @@ YODLSRC = zmacros.yo zman.yo ztexi.yo Zsh/arith.yo Zsh/builtins.yo \ Zsh/compat.yo Zsh/compctl.yo Zsh/cond.yo Zsh/exec.yo Zsh/expn.yo \ Zsh/filelist.yo Zsh/files.yo Zsh/func.yo Zsh/grammar.yo Zsh/guide.yo \ Zsh/index.yo Zsh/intro.yo Zsh/invoke.yo Zsh/jobs.yo Zsh/metafaq.yo \ -Zsh/modules.yo Zsh/mod_cap.yo \ +Zsh/modules.yo Zsh/mod_cap.yo Zsh/compwid.yo \ Zsh/mod_clone.yo Zsh/mod_comp1.yo Zsh/mod_compctl.yo Zsh/mod_deltochar.yo \ Zsh/mod_example.yo Zsh/mod_files.yo Zsh/mod_stat.yo \ Zsh/mod_zle.yo Zsh/options.yo \ diff --git a/Doc/Zsh/.distfiles b/Doc/Zsh/.distfiles index 42517f507..c4a6d8080 100644 --- a/Doc/Zsh/.distfiles +++ b/Doc/Zsh/.distfiles @@ -1,9 +1,9 @@ DISTFILES_SRC=' .distfiles - arith.yo builtins.yo compat.yo compctl.yo cond.yo exec.yo expn.yo - filelist.yo files.yo func.yo grammar.yo guide.yo index.yo intro.yo + arith.yo builtins.yo compat.yo compctl.yo compwid.yo cond.yo exec.yo + expn.yo filelist.yo files.yo func.yo grammar.yo guide.yo index.yo intro.yo invoke.yo jobs.yo metafaq.yo mod_cap.yo mod_clone.yo mod_comp1.yo mod_compctl.yo mod_deltochar.yo mod_example.yo mod_files.yo mod_sched.yo - mod_stat.yo mod_zle.yo modules.yo options.yo params.yo prompt.yo - redirect.yo restricted.yo seealso.yo zle.yo + mod_stat.yo mod_zftp.yo mod_zle.yo modules.yo options.yo params.yo + prompt.yo redirect.yo restricted.yo seealso.yo zle.yo ' diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo index 6a7f19023..124f9e604 100644 --- a/Doc/Zsh/compctl.yo +++ b/Doc/Zsh/compctl.yo @@ -1,4 +1,4 @@ -texinode(Programmable Completion)(Zsh Modules)(Zsh Line Editor)(Top) +texinode(Programmable Completion)(Completion Widgets)(Zsh Line Editor)(Top) chapter(Programmable Completion) cindex(completion, programmable) cindex(completion, controlling) @@ -89,7 +89,7 @@ to the user database is too slow and/or it contains too many users (so that completion after `tt(~)' is too slow to be usable), you can use indent( -tt(compctl -T -x 'C[0,*/*]' -f - 's[~]' -k friends -S/ -tn) +tt(compctl -T -x 's[~] C[0,[^/]#]' -k friends -S/ -tn) ) to complete the strings in the array tt(friends) after a `tt(~)'. diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo new file mode 100644 index 000000000..2cb12e2c2 --- /dev/null +++ b/Doc/Zsh/compwid.yo @@ -0,0 +1,428 @@ +texinode(Completion Widgets)(Zsh Modules)(Programmable Completion)(Top) +chapter(Completion Widgets) +cindex(completion, widgets) +cindex(completion, programmable) +cindex(completion, controlling) +sect(Description) +Completion widgets are defined using the tt(-C) option to the tt(zle) +builtin command provided by the tt(zle) module (see +ifzman(zmanref(zshzle))\ +ifnzman(noderef(The zle Module))\ +). For example, the invocation: + +indent(nofill( +tt(zle -C complete expand-or-complete completer))) + +defines a widget named tt(complete). If this widget is bound to a key +using the tt(bindkey) builtin command defined in the tt(zle) module +(see +ifzman(zmanref(zshzle))\ +ifnzman(noderef(Zsh Line Editor))\ +) typing that key will make the completion code call the shell +function tt(completer). This function is responsible for generating +the possible matches using the builtins described below. Once the +function returns, the completion code takes over control again and +treats the matches the way the builtin widget tt(expand-or-complete) +would do it. For this second argument, the name of any of the builtin +widgets that handle completions can be given, i.e. it may be any of +tt(complete-word), tt(expand-or-complete), +tt(expand-or-complete-prefix), tt(menu-complete), +tt(menu-expand-or-complete), tt(reverse-menu-complete), +tt(list-choices), or tt(delete-char-or-list). + +startmenu() +menu(Special Parameters) +menu(Builtin Commands) +menu(Condition Codes) +menu(Examples) +endmenu() + +texinode(Special Parameters)(Builtin Commands)()(Completion Widgets) +sect(Special Parameters) + +Inside completion widgets some parameters have special meaning. They +will be used inside the widget function and other shell functions +called from it. Outside of these function they are not special to the +shell in any way. + +The parameters are used to give information about the internal state +from the completion code to the completion widget and can be set to +give information to the completion code from the completion +widget. Some of the builtin commands and the condition codes use or +change the current values of these parameters. While the completion +widget is active, these parameters are reseton each function exit to +the values they had when the function was entered. + +startitem() +item(tt(argv))( +The positional parameters are set to the arguments on the command line +when the widget function is invoked from the completion code. +) +item(tt(CURRENT))( +This is the number of the current word, i.e. the word the cursor is +currently on in the tt(argv) array. +) +item(tt(CONTEXT))( +This will be set by the completion code to the overall context +completion is attempted in. Possible values are: + +startitem() +item(tt(command))( +when completing in a command position, e.g. in the first word on the +command line +) +item(tt(argument))( +when completing an argument for a command +) +item(tt(redirect))( +when completing after a redirection operator; in this case the +positional parameters contain not only the arguments but also the +command name itself as the first element +) +item(tt(condition))( +when completing inside a `tt([[)...tt(]])' conditional expressing; in +this case the positional parameters are set to the words inside the +conditional expressions +) +item(tt(math))( +when completing in a mathematical environment such as a +`tt(LPAR()LPAR())...tt(RPAR()RPAR())' construct +) +item(tt(value))( +when completing the value of a parameter assignment; in case of an +array value the positional parameters are set to the words in +parentheses +) +item(tt(subscript))( +when completing inside a parameter expansion subscript +) +enditem() +) +item(tt(COMMAND))( +In most cases this is set to name of the command for which completion +is tried. When completing after a redirection operator it contains the +string forming that operator. Also, when completing in the value of a +parameter assignment or in a parameter subscript it is set to the name +of the parameter. +) +item(tt(PREFIX))( +This should be set to that part of the current word that should be +taken as the string every possible match has to begin with. Initially +this will be set to the part of the current word from the beginning of +the word up to the position of the cursor. When +) +item(tt(IPREFIX))( +When a part of the current word should not be considered part of the +matches, this part should be taken from the tt(PREFIX) parameter and +appended to this parameter. This will initially be set to the empty +string when called from the completion code. +) +item(tt(SUFFIX))( +This should be set to that part of the current word that should be +taken as the string every possible match has to end with. The +completion code sets this to the part of the current word from the +cursor position to the end. +) +item(tt(NMATCHES))( +This is always set to the number of matches generated and accepted by +the completion code so far. +) +item(tt(MATCHER))( +When completion is used with a global match specification (i.e. a +tt(compctl) with only a tt(-M) option), this parameter is set to the +number of the specification string which is currently used. +) +enditem() + +texinode(Builtin Commands)(Condition Codes)(Special Parameters)(Completion Widgets) +sect(Builtin Commands) +startitem() +findex(complist) +item(tt(complist) var(flags ...))( + +Generate matches according to the given var(flags) which can be any of +the option flags supported by the tt(compctl) builtin command (see +ifzman(zmanref(zshcompctl))\ +ifnzman(noderef(Programmable Completion))\ +) except for the tt(-t) and tt(-l) flags. Also, when using the tt(-K) +flag, the function given as argument to it can not access the command +line with the tt(read) builtin command. + +The matches will be generated in the same way as if the completion code +generated them directly from a tt(compctl)-definition with the same +flags. The completion code will consider only those matches as +possible completions that match the prefix and suffix from the special +parameters desribed above. These strings will be compared with the +generated matches using the normal matching rules and any matching +specifications given with the tt(-M) flag to tt(complist) and the +global matching specifications given to the tt(compctl) builtin +command. +) +xitem(tt(compadd) [ tt(-qQfnUam) ] [ tt(-F) var(array) ]) +xitem([ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ]) +xitem([ tt(-p) var(hidden-prefix) ] [ tt(-s) var(hidden-suffix) ]) +xitem([ tt(-i) var(ignored-prefix) ] [ tt(-W) var(file-prefix) ]) +xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ]) +xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ]) +item([ tt(-M) var(match-spec) ] [ tt(--) ] [ var(words) ... ])( + +This builtin command can be used to add matches and directly control +all the information the completion code stores with each possible +match. + +The supported flags are: + +startitem() +item(tt(-P) var(prefix))( +The same as for tt(compctl) and tt(complist), it gives a string that +should be inserted before the given words when they are completed. The +string given is not considered to be part of the match. +) +item(tt(-S) var(suffix))( +Like tt(-P) but gives a string that has to be inserted after the match. +) +item(tt(-p) var(hidden-prefix))( +This gives a string that should be +... +) +item(tt(-s) var(hidden-suffix))( +... +) +item(tt(-i) var(ignored-prefix))( +... +) +item(tt(-J) var(name))( +As for tt(compctl) and tt(complist) this gives the name of the group +of matches the words should be stored in. +) +item(tt(-V) var(name))( +Like tt(-J) but naming a unsorted group. +) +item(tt(-q))( +This flag has the same meaning as for tt(compctl) and tt(complist), +too. It makes the suffix given with tt(-S) be automatically removed if +the next character typed is a blank or does not insert anything or if +the suffix consists of only one character and the next character typed +is the same character. +) +item(tt(-r) var(remove-chars))( +This makes the suffix given with tt(-S) be automatically removed if +the next character typed inserts one of the characters given in the +var(remove-chars). This string is parsed as a characters class with +the usual backslash-sequences understood, e.g. using `tt(-r "a-z\t")' +removes the suffix if the next character typed inserts one of the +lower case letters or a TAB, and `tt(-r "^0-9")' removes the suffix if +the next character typed inserts anything but a digit. One extra +backslash sequence is understood in this string: `tt(\-)' stands for +all characters that insert nothing. Thus `tt(-S "=" -q)' is the same +as `tt(-S "=" -r "= \t\n\-")'. +) +item(tt(-R) var(remove-func))( +For the cases where one wants to remove suffix and the tt(-r) option +does not give enough control, this option can be used. It stores the +name of the shell function var(remove-func) in the matches. If one of +the matches is finally accepted and the tt(-S)-suffix inserted, this +function will be called after the next character typed. It gets the +length of the suffix as its argument and can use the special +parameters available in zle widgets (see +ifzman(zmanref(zshzle))\ +ifnzman(noderef(Zsh Line Editor))\ +) to analyse and modify the command line. +) +item(tt(-f))( +If this flag is given, the matches build are marked as being the names +of files. They need not be actual filenames, though. But if they are +and the option tt(LIST_TYPES) is set, the characters describing the +types of the files in the completion lists will be shown. This also +makes a slash automatically be added when the name of a directory is +completed. +) +item(tt(-W) var(file-prefix))( +This option has the same meaning as for the tt(compctl) and +tt(complist) builtin commands. Here, however, only one string may be +given, not an array. This string is used as a pathname that will be +prepended to the given words and the prefix given with the tt(-p) +option to perform the file-tests when showing completion +listings. Hence it is only useful if combined with the tt(-f) flag, +since the tests will only be performed if that flag is given. +) +item(tt(-a))( +When used by tt(compctl) or tt(complist) the completion code normally +builds two sets of matches: the normal one where words with one of the +suffixes in the array parameter tt(fignore) are not considered +possible matches, and the alternate set where the words excluded +from the first set are stored. Normally only the matches in the first +set are used. But if this set is empty, the words from the alternate +set are used. + +The tt(compadd) builtin does not use tt(fignore) parameter and +normally stores all words in the first set. With the tt(-a)-flag +given, however, they are all stored in the alternate set unless this +flag is overridden by the tt(-F) option. +) +item(tt(-F) var(array))( +This can be used to give an array containing suffixes like the +tt(fignore) parameter. Words with one of these suffixes are stored in +the alternate set of matches and words without one of these suffixes +are stored in the normal set. + +The var(array) may be the name of an array parameter or a list of +literal suffixes enclosed in parentheses as in `tt(-F "(.o .h)")'. If +the name of an array is given, the elements of the array are taken as +the suffixes. +) +item(tt(-Q))( +As for tt(compctl) and tt(complist) this flag instructs the completion +code not to quote any metacharacters in the words when inserting them +in the command line. +) +item(tt(-m))( +Normally the matches added by tt(compadd) will not be compared with +what is already on the line. If this flag is given, this comparison is +performed as usual and the match specifications given with the tt(-M) +option to tt(compadd) and the global match specifications defined with +tt(compctl) will be used. This means that probably not all the word +given will be stored as matches since some of them may not match the +string on the line. +) +item(tt(-M) var(match-spec))( +This option allows one to give local match specifications with the +same meaning and format as for the tt(compctl) and tt(complist) +builtin commands. Note that they will only be used if the tt(-m) is +given, too. +) +item(tt(-n))( +Words added with tt(compadd) with this flag will be used as possible +matches as usual but they not appear in the completion listing. +) +item(tt(-U))( +If this flag is given to one of the calls to tt(compadd) and the +option tt(AUTO_MENU) is set, the completion code will immediatly +switch to menucompletion. +) +item(tt(-), tt(--))( +This flag ends the list of flags and options. All arguments after it +will be taken as the words to use as matches even if they begin with +hyphens. +) +enditem() +) +item(tt(compcall) [ tt(-TD) ])( + +This allows one to use completion definitions given with the +tt(compctl) builtin from within completion widgets. It makes +completion code complete the current word according to the +tt(compctl)s defined. Normally only tt(compctl)s given for specific +commands are used. To make the code use the completion flags given to +the tt(-T) option of tt(compctl), one can give the tt(-T) flag to +tt(compctl). Likewise, the tt(-D) flag to tt(compcall) makes the +default completion flags given to tt(compctl) with the tt(-D) option +be used. +) +enditem() + +texinode(Condition Codes)(Examples)(Builtin Commands)(Completion Widgets) +sect(Condition Codes) + +Inside completion widgets not only the builtin commands described +above can be used, but also some additional condition codes. These +work on the special parameters and can be used to easily build +completion functions that generate different matches depending on the +strings on the line. + +The following condition codes are made available inside completion +widgets: + +startitem() +item(tt(-prefix) var(string))( +true if the content of tt(PREFIX) starts with var(string) +) +item(tt(-iprefix) var(string))( +like tt(-prefix), but the var(string) is removed from tt(PREFIX) and +added to tt(IPREFIX) +) +item(tt(-position) var(beg) [ var(end) ])( +true if tt(CURRENT) is equal to var(beg) or, if var(end) is given, +equal to or greater than var(beg) and equal to or less than var(end); +both of var(beg) and var(end) may be arithmetic expressions, if they +are less than zero the number of words in tt(argv) are added to them +before comparing them to tt(CURRENT); thus, tt(-1) is the last word, +tt(-2) is the word before that and so on +) +item(tt(-word) var(index) var(string))( +true if the word number var(index) in tt(argv) is equal to +var(string); again, var(index) may be negative, counting backwards +) +item(tt(-mword) var(index) var(pattern))( +like tt(-word) but using pattern matching +) +item(tt(-current) var(offset) var(string))( +like tt(-word) but var(offset) is relative to the value of +tt(CURRENT) +) +item(tt(-mcurrent) var(offset) var(pattern))( +like tt(-current) but using pattern matching +) +item(tt(-string) [ var(number) ] var(string))( +true if the current word contains var(string); anything up to the last +occurrence of this string will be ingnored by removing it from +tt(PREFIX) and adding it to tt(IPREFIX); if var(number) is given, +anything up to the var(number)'th occurrence of the var(string) will +be ignored; again, var(nmuber) may be any arithmetic expression and +negative values count backward +) +item(tt(-class) [ var(number) ] var(class))( +like tt(-string) but the var(class) is used as a character class so +that anything up to and including the last or the var(number)'th +occurrence of any character from the string var(class) is ignored +) +item(tt(-words) var(min) [ var(max) ])( +true if the number of words is equal to var(min); if var(max) is +given, it is true if the number of words is equal to or greater than +var(min) and equal to or less than var(max) +) +item(tt(-after) var(string))( +true if the cursor is after a word that is equal to var(string) +) +item(tt(-mafter) var(pattern))( +like tt(-after) but using pattern matching +) +item(tt(-between) var(string1) var(string2))( +true if the cursor is after a word that is equal to var(string1), if +there is also a word that is equal to va(string2), this is true only +if the cursor is before it +) +item(tt(-mbetween) var(pattern1) var(pattern2))( +like tt(-between) but using pattern matching +) +item(tt(-nmatches) var(number))( +true if the the value of tt(NMATCHES) is equal to var(number) +) +item(tt(-matcher) var(number))( +true if the value of tt(MATCHER) is equal to var(number) +) +enditem() + +texinode(Examples)()(Condition Codes)(Completion Widgets) +sect(Examples) + +The first step is to define the widget: + +indent(nofill( +tt(zle -C complete complete-word complete-history))) + +Then the widget can be bound to a key using the tt(bindkey) builtin +command: + +indent(nofill( +tt(bindkey '^X\t' complete))) + +After that the shell function tt(complete-history) will be invoked +after typing control-X and TAB. The function should then generte the +matches, e.g.: + +indent(nofill( +tt(complete-history LPAR()RPAR() { complist -H 0 '' }))) + +In this the function will complete words from the history matching the +current word. diff --git a/Doc/Zsh/guide.yo b/Doc/Zsh/guide.yo index 0fb954e26..e0005e339 100644 --- a/Doc/Zsh/guide.yo +++ b/Doc/Zsh/guide.yo @@ -29,6 +29,7 @@ menu(Options) menu(Shell Builtin Commands) menu(Zsh Line Editor) menu(Programmable Completion) +menu(Completion Widgets) menu(Zsh Modules) --- Indices --- @@ -103,6 +104,13 @@ menu(Alternative Completion) menu(Extended Completion) menu(Example) +Completion Widgets + +menu(Special Parameters) +menu(Builtin Commands) +menu(Condition Codes) +menu(Examples) + Zsh Modules menu(The cap Module) diff --git a/Doc/Zsh/intro.yo b/Doc/Zsh/intro.yo index ebb678f70..f619f82fe 100644 --- a/Doc/Zsh/intro.yo +++ b/Doc/Zsh/intro.yo @@ -21,6 +21,7 @@ list(em(zshoptions) Zsh options) list(em(zshbuiltins) Zsh built-in functions) list(em(zshzle) Zsh command line editing) list(em(zshcompctl) Zsh completion control) +list(em(zshcompwid) Zsh completion widgets) list(em(zshmodules) Zsh loadable modules) ifzshone(\ list(em(zshall) Meta-man page containing all of the above) diff --git a/Doc/Zsh/metafaq.yo b/Doc/Zsh/metafaq.yo index cad82ee65..1d1636942 100644 --- a/Doc/Zsh/metafaq.yo +++ b/Doc/Zsh/metafaq.yo @@ -11,7 +11,7 @@ sect(Author) cindex(author) Zsh was originally written by Paul Falstad tt(<pf@zsh.org>). Zsh is now maintained by the members of the zsh-workers mailing -list tt(<zsh-workers@math.gatech.edu>). The development is currently +list tt(<zsh-workers@sunsite.auc.dk>). The development is currently coordinated by Andrew Main (Zefram) tt(<zefram@zsh.org>). The coordinator can be contacted at tt(<coordinator@zsh.org>), but matters relating to the code should generally go to the mailing list. diff --git a/Doc/Zsh/mod_compctl.yo b/Doc/Zsh/mod_compctl.yo index b7adf8fa6..cfd615b2d 100644 --- a/Doc/Zsh/mod_compctl.yo +++ b/Doc/Zsh/mod_compctl.yo @@ -1,7 +1,12 @@ texinode(The compctl Module)(The deltochar Module)(The comp1 Module)(Zsh Modules) sect(The compctl Module) -The tt(compctl) module makes available one builtin command, tt(compctl), -which is the standard way to control completions for ZLE. See +The tt(compctl) module makes available several builtin commands. tt(compctl), +is the standard way to control completions for ZLE. See ifzman(zmanref(zshcompctl))\ ifnzman(noderef(Programmable Completion))\ . +The other builtin commands can be used in user-defined completion widgets, +see +ifzman(zmanref(zshcompwid))\ +ifnzman(noderef(Completion Widgets))\ +. diff --git a/Doc/Zsh/mod_zftp.yo b/Doc/Zsh/mod_zftp.yo index c08130bf5..395bf26de 100644 --- a/Doc/Zsh/mod_zftp.yo +++ b/Doc/Zsh/mod_zftp.yo @@ -439,4 +439,8 @@ until the next call to tt(zftp). Other status changes in subshells will not be reflected by changes to the variables (but should be otherwise harmless). +On some operatings systems, the control connection is not valid after a +fork(), so that operations in subshells or on the left hand side of a +pipeline are not possible. + enditem() diff --git a/Doc/Zsh/mod_zle.yo b/Doc/Zsh/mod_zle.yo index 86d145636..ad88c94c7 100644 --- a/Doc/Zsh/mod_zle.yo +++ b/Doc/Zsh/mod_zle.yo @@ -165,7 +165,7 @@ xitem(tt(zle) tt(-l) [ tt(-L) ]) xitem(tt(zle) tt(-D) var(widget) ...) xitem(tt(zle) tt(-A) var(old-widget) var(new-widget)) xitem(tt(zle) tt(-N) var(widget) [ var(function) ]) -xitem(tt(zle) tt(-C) [ tt(-mMgG) ] var(widget) [ var(compctl-options) ]) +xitem(tt(zle) tt(-C) var(widget) var(completion-widget) var(function)) item(tt(zle) var(widget))( The tt(zle) builtin performs a number of different actions concerning ZLE. Which operation it performs depends on its options: @@ -193,22 +193,15 @@ widget is invoked from within the editor, the specified shell var(function) is called. If no function name is specified, it defaults to the same name as the widget. ) -item(tt(-C) [ tt(-mMgG) ] var(widget) [ var(compctl-options) ])( -Create a user-defined widget which will perform completion according -to var(compctl-options). These are passed directly to the -tt(compctl) command, see -ifzman(zmanref(zshcompctl))\ -ifnzman(noderef(Programmable Completion))\ -; no command names or special options (tt(-LDCT)) may be used. If the -var(compctl-options) are missing the widget will have normal -completion behaviour as modified by the tt(zle) options. - -There are four additional tt(zle) options, which must precede the -widget name: tt(-m) and tt(-M) force the widget to use or not to use -menu completion, respectively, while tt(-g) and tt(-G) likewise force -the widget to use or not to use glob completion. The defaults are to -use the current settings of tt(MENU_COMPLETE) and tt(GLOB_COMPLETE) -as with normal completion. +item(tt(-C) var(widget) var(completion-widget) var(function))( +Create a user-defined completion widget names var(widget). The +completion widget will behave like the built-in completion-widget +whose name is given as var(completion-widget). To generate the +completions, the shell function var(function) will be called. +For further information, see +ifzman(zmanref(zshcompwid))\ +ifnzman(noderef(Completion Widgets))\ +. ) item(var(widget))( Invoke the specified widget. This can only be done when ZLE is diff --git a/Doc/Zsh/modules.yo b/Doc/Zsh/modules.yo index 300553845..97c420eed 100644 --- a/Doc/Zsh/modules.yo +++ b/Doc/Zsh/modules.yo @@ -1,4 +1,4 @@ -texinode(Zsh Modules)()(Programmable Completion)(Top) +texinode(Zsh Modules)()(Completion Widgets)(Top) chapter(Zsh Modules) cindex(modules) sect(Description) @@ -19,7 +19,8 @@ item(tt(comp1))( Base of the completion system. Used by the tt(compctl) and tt(zle) modules. ) item(tt(compctl))( -The tt(compctl) builtin for controlling completion. +The tt(compctl) builtin for controlling completion and the builtins for +completion widgets. ) item(tt(deltochar))( A ZLE function duplicating EMACS' tt(zap-to-char). diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index b9455f578..b59fa624a 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -544,12 +544,14 @@ List jobs in the long format by default. ) pindex(MAGIC_EQUAL_SUBST) item(tt(MAGIC_EQUAL_SUBST))( -All unquoted arguments of the form `var(identifier)tt(=)var(expression)' +All unquoted arguments of the form `var(anything)tt(=)var(expression)' appearing after the command name have filename expansion (that is, where var(expression) has a leading `tt(~)' or `tt(=)') performed on var(expression) as if it were a parameter assignment. The argument is not otherwise treated specially; it is passed to the command as a single -argument, and not used as an actual parameter assignment. +argument, and not used as an actual parameter assignment. For example, in +tt(echo foo=~/bar:~/rod), both occurrences of tt(~) would be replaced. +Note that this happens anyway with tt(typeset) and similar statements. ) pindex(MAIL_WARNING) cindex(mail, warning of reading) diff --git a/Doc/Zsh/seealso.yo b/Doc/Zsh/seealso.yo index 8289e6628..5231e8828 100644 --- a/Doc/Zsh/seealso.yo +++ b/Doc/Zsh/seealso.yo @@ -10,6 +10,7 @@ ifzshone(\ , zmanref(zshbuiltins), zmanref(zshcompctl), +zmanref(zshcompwid), zmanref(zshexpn), zmanref(zshmisc), zmanref(zshmodules), diff --git a/Doc/zsh.yo b/Doc/zsh.yo index a97f023e7..e6cef6a32 100644 --- a/Doc/zsh.yo +++ b/Doc/zsh.yo @@ -64,6 +64,7 @@ ifnzman(includefile(Zsh/options.yo)) ifnzman(includefile(Zsh/builtins.yo)) ifnzman(includefile(Zsh/zle.yo)) ifnzman(includefile(Zsh/compctl.yo)) +ifnzman(includefile(Zsh/compwid.yo)) ifnzman(includefile(Zsh/modules.yo)) ifzshall(\ def(source)(1)(NOTRANS(.so )man1/ARG1NOTRANS(.)1)\ @@ -75,6 +76,7 @@ source(zshoptions) source(zshbuiltins) source(zshzle) source(zshcompctl) +source(zshcompwid) source(zshmodules) manpage(ZSHALL)(1)(date())(zsh version()) )\ diff --git a/Doc/zshcompwid.yo b/Doc/zshcompwid.yo new file mode 100644 index 000000000..d876e422d --- /dev/null +++ b/Doc/zshcompwid.yo @@ -0,0 +1,3 @@ +manpage(ZSHCOMPWID)(1)(date())(zsh version()) +manpagename(zshcompwid)(zsh completion widgets) +includefile(Zsh/compwid.yo) diff --git a/Etc/MACHINES b/Etc/MACHINES index b9d3290c0..27e21f330 100644 --- a/Etc/MACHINES +++ b/Etc/MACHINES @@ -76,9 +76,12 @@ Reliant: SINIX linking; configure now detects this and will disable dynamic linking even if you requested it. -SGI: IRIX 5.1.1.1, 5.2, 5.3, 6.2, 6.3 +SGI: IRIX 5.1.1.1, 5.2, 5.3, 6.2, 6.3, 6.5 Should build `out-of-the-box'. + On 6.5.2, zsh malloc routines are reported not to work; also + full optimization (cc -O3 -OPT:Olimit=0) causes problems. + Sun: SunOS 4.1.* Under 4.1.3 if yellow pages is used, username completion may cause segmentation violation. This is a bug in the shared library not diff --git a/Functions/Completion/_aliases b/Functions/Completion/_aliases new file mode 100644 index 000000000..1038a726e --- /dev/null +++ b/Functions/Completion/_aliases @@ -0,0 +1,3 @@ +#defcomp unalias + +complist -a diff --git a/Functions/Completion/_arrays b/Functions/Completion/_arrays new file mode 100644 index 000000000..cbeac7118 --- /dev/null +++ b/Functions/Completion/_arrays @@ -0,0 +1,3 @@ +#defcomp shift + +complist -A diff --git a/Functions/Completion/_autoload b/Functions/Completion/_autoload new file mode 100644 index 000000000..4f506baeb --- /dev/null +++ b/Functions/Completion/_autoload @@ -0,0 +1,3 @@ +#defcomp autoload + +complist -s '${^fpath}/*(N:t)' diff --git a/Functions/Completion/_bg_jobs b/Functions/Completion/_bg_jobs new file mode 100644 index 000000000..511bb8308 --- /dev/null +++ b/Functions/Completion/_bg_jobs @@ -0,0 +1,3 @@ +#defcomp bg + +complist -z -P '%' diff --git a/Functions/Completion/_bindkey b/Functions/Completion/_bindkey new file mode 100644 index 000000000..8eddeb2a8 --- /dev/null +++ b/Functions/Completion/_bindkey @@ -0,0 +1,7 @@ +#defcomp bindkey + +if [[ -mword 1 -*[DAN]* || -mcurrent -1 -*M ]]; then + complist -s '$(bindkey -l)' +else + complist -b +fi diff --git a/Functions/Completion/_builtin b/Functions/Completion/_builtin new file mode 100644 index 000000000..d2b11226b --- /dev/null +++ b/Functions/Completion/_builtin @@ -0,0 +1,7 @@ +#defcomp builtin + +if [[ -position 2 -1 ]]; then + compsub +else + complist -eB +fi diff --git a/Functions/Completion/_cd b/Functions/Completion/_cd new file mode 100644 index 000000000..f3ce67ec7 --- /dev/null +++ b/Functions/Completion/_cd @@ -0,0 +1,3 @@ +#defcomp cd + +_files -W cdpath -g '*(-/)' diff --git a/Functions/Completion/_command b/Functions/Completion/_command new file mode 100644 index 000000000..b2812de25 --- /dev/null +++ b/Functions/Completion/_command @@ -0,0 +1,7 @@ +#defcomp command + +if [[ -position 2 -1 ]]; then + _normal "$@" +else + complist -em +fi diff --git a/Functions/Completion/_command_names b/Functions/Completion/_command_names new file mode 100644 index 000000000..d3b8a109a --- /dev/null +++ b/Functions/Completion/_command_names @@ -0,0 +1,3 @@ +#defcomp -command- + +complist -c diff --git a/Functions/Completion/_compress b/Functions/Completion/_compress new file mode 100644 index 000000000..860aeb5b0 --- /dev/null +++ b/Functions/Completion/_compress @@ -0,0 +1,3 @@ +#defcomp compress + +_files -g '*~*.Z' diff --git a/Functions/Completion/_condition b/Functions/Completion/_condition new file mode 100644 index 000000000..3e45e1b8f --- /dev/null +++ b/Functions/Completion/_condition @@ -0,0 +1,10 @@ +#defcomp -condition- + +if [[ -current -1 -o ]]; then + complist -o -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' +elif [[ -current -1 -nt || -current -1 -ot || -current -1 -ef ]]; then + _files +else + _files + complist -v +fi diff --git a/Functions/Completion/_configure b/Functions/Completion/_configure new file mode 100644 index 000000000..de8d5fba5 --- /dev/null +++ b/Functions/Completion/_configure @@ -0,0 +1,12 @@ +#defcomp configure + +if [[ $PREFIX = *=* ]]; then + # Complete filenames after e.g. --prefix= + IPREFIX=${PREFIX%%=*}= + PREFIX=${PREFIX#*=} + complist -f +else + # Generate a list of options from configure --help + complist -s '$($COMMAND --help | + sed -n -e '\''s/^ *\(--[-a-z0-9]*\)[ =,].*$/\1/p'\'')' +fi diff --git a/Functions/Completion/_dd b/Functions/Completion/_dd new file mode 100644 index 000000000..2458541ea --- /dev/null +++ b/Functions/Completion/_dd @@ -0,0 +1,13 @@ +#defcomp dd + +if [[ -iprefix conv= ]]; then + # If there's a comma present, ignore up to the last one. The + # test alone will have that effect. + [[ -string , ]] + complist -S, -q \ + -k '(ascii ebcdic ibm block unblock lcase ucase swab noerror sync)' +elif [[ -iprefix 'if=' || -iprefix 'of=' ]]; then + _files +else + complist -S '=' -k '(if of ibs obs bs cbs skip files seek count conv)' +fi diff --git a/Functions/Completion/_default b/Functions/Completion/_default new file mode 100644 index 000000000..8bcf14f6a --- /dev/null +++ b/Functions/Completion/_default @@ -0,0 +1,13 @@ +#defcomp -default- + +# We first try the `compctl's. This is without first (-T) and default (-D) +# completion. If you want them add `-T' and/or `-D' to this command. +# If there is a `compctl' for the command we are working on, we return +# immediatly. If you want to use new style completion anyway, remove the +# `|| return'. Also, you may want to use new style completion if the +# `compctl' didn't produce any matches. In that case remove the `|| return' +# and at the line `[[ -nmatches 0 ]] || return' after `compcall'. + +compcall || return + +_files diff --git a/Functions/Completion/_dirs b/Functions/Completion/_dirs new file mode 100644 index 000000000..c79080d58 --- /dev/null +++ b/Functions/Completion/_dirs @@ -0,0 +1,3 @@ +#defcomp rmdir df du dircmp + +_files -/ '*(-/)' diff --git a/Functions/Completion/_disable b/Functions/Completion/_disable new file mode 100644 index 000000000..063b65a7d --- /dev/null +++ b/Functions/Completion/_disable @@ -0,0 +1,6 @@ +#defcomp disable + +[[ -mcurrent -1 -*a* ]] && complist -ea +[[ -mcurrent -1 -*f* ]] && complist -eF +[[ -mcurrent -1 -*r* ]] && complist -ew +[[ ! -mcurrent -1 -* ]] && complist -eB diff --git a/Functions/Completion/_dvi b/Functions/Completion/_dvi new file mode 100644 index 000000000..bb2fc293e --- /dev/null +++ b/Functions/Completion/_dvi @@ -0,0 +1,3 @@ +#defcomp xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype + +_files -g '*.(dvi|DVI)' diff --git a/Functions/Completion/_echotc b/Functions/Completion/_echotc new file mode 100644 index 000000000..85ebb97ce --- /dev/null +++ b/Functions/Completion/_echotc @@ -0,0 +1,3 @@ +#defcomp echotc + +complist -k '(al dc dl do le up al bl cd ce cl cr dc dl do ho is le ma nd nl se so up)' diff --git a/Functions/Completion/_enable b/Functions/Completion/_enable new file mode 100644 index 000000000..22ff53ee7 --- /dev/null +++ b/Functions/Completion/_enable @@ -0,0 +1,6 @@ +#defcomp enable + +[[ -mcurrent -1 -*a* ]] && complist -da +[[ -mcurrent -1 -*f* ]] && complist -dF +[[ -mcurrent -1 -*r* ]] && complist -dw +[[ ! -mcurrent -1 -* ]] && complist -dB diff --git a/Functions/Completion/_fc b/Functions/Completion/_fc new file mode 100644 index 000000000..f0d2c03fd --- /dev/null +++ b/Functions/Completion/_fc @@ -0,0 +1,7 @@ +#defcomp fc + +if [[ -mcurrent -1 -*e ]]; then + complist -c +elif [[ -mcurrent -1 -[ARWI]## ]]; then + _files +fi diff --git a/Functions/Completion/_files b/Functions/Completion/_files new file mode 100644 index 000000000..ab3006e39 --- /dev/null +++ b/Functions/Completion/_files @@ -0,0 +1,10 @@ +#autoload + +# Utility function for completing files of a given type or any file. +# In many cases you will want to call this one instead of _path_files(). + +local nm=$NMATCHES + +_path_files "$@" + +[[ $# -ne 0 && -nmatches nm ]] && _path_files diff --git a/Functions/Completion/_find b/Functions/Completion/_find new file mode 100644 index 000000000..ca4f79908 --- /dev/null +++ b/Functions/Completion/_find @@ -0,0 +1,21 @@ +#defcomp find + +if [[ -mbetween -(ok|exec) \\\; ]]; then + _normal "$@" +elif [[ -iprefix - ]]; then + complist -s 'daystart {max,min,}depth follow noleaf version xdev \ + {a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \ + {i,}{l,}name {no,}{user,group} path perm regex size true uid used \ + exec {f,}print{f,0,} ok prune ls' +elif [[ -position 1 ]]; then + complist -g '. ..' + _files -g '(-/)' +elif [[ -mcurrent -1 -((a|c|)newer|fprint(|0|f)) ]]; then + _files +elif [[ -current -1 -fstype ]]; then + complist -k '(ufs 4.2 4.3 nfs tmp mfs S51K S52K)' +elif [[ -current -1 -group ]]; then + complist -k groups +elif [[ -current -1 -user ]]; then + complist -u +fi diff --git a/Functions/Completion/_functions b/Functions/Completion/_functions new file mode 100644 index 000000000..8a352ea08 --- /dev/null +++ b/Functions/Completion/_functions @@ -0,0 +1,3 @@ +#defcomp unfunction + +complist -F diff --git a/Functions/Completion/_gunzip b/Functions/Completion/_gunzip new file mode 100644 index 000000000..35a27e774 --- /dev/null +++ b/Functions/Completion/_gunzip @@ -0,0 +1,3 @@ +#defcomp gunzip zcat + +_files -g '*.[gG][z]' diff --git a/Functions/Completion/_gzip b/Functions/Completion/_gzip new file mode 100644 index 000000000..3cda1e4ed --- /dev/null +++ b/Functions/Completion/_gzip @@ -0,0 +1,3 @@ +#defcomp gzip + +_files -g '*~*.[gG][zZ]' diff --git a/Functions/Completion/_hash b/Functions/Completion/_hash new file mode 100644 index 000000000..a6109563d --- /dev/null +++ b/Functions/Completion/_hash @@ -0,0 +1,13 @@ +#defcomp hash + +if [[ -mword 1 -*d* ]]; then + if [[ -string 1 '=' ]]; then + _path_files -g '*(-/)' + else + complist -n -q -S '=' + fi +elif [[ -string 1 '=' ]]; then + _files -g '*(*)' '*(-/)' +else + complist -m -q -S '=' +fi diff --git a/Functions/Completion/_hosts b/Functions/Completion/_hosts new file mode 100644 index 000000000..3acc327ac --- /dev/null +++ b/Functions/Completion/_hosts @@ -0,0 +1,3 @@ +#defcomp ftp ncftp ping rwho rup xping traceroute nslookup + +complist -k hosts diff --git a/Functions/Completion/_jobs b/Functions/Completion/_jobs new file mode 100644 index 000000000..018883c61 --- /dev/null +++ b/Functions/Completion/_jobs @@ -0,0 +1,3 @@ +#defcomp fg jobs + +complist -j -P '%' diff --git a/Functions/Completion/_kill b/Functions/Completion/_kill new file mode 100644 index 000000000..bf0e1d3f8 --- /dev/null +++ b/Functions/Completion/_kill @@ -0,0 +1,8 @@ +#defcomp kill + +if [[ -iprefix '-' ]]; then + complist -k "($signals[1,-3])" +else + complist -P '%' -j + complist -y _kill_helper -s '`ps 2>/dev/null | tail +2 | cut -c1-5`' +fi diff --git a/Functions/Completion/_kill_helper b/Functions/Completion/_kill_helper new file mode 100644 index 000000000..d3e9aaf4a --- /dev/null +++ b/Functions/Completion/_kill_helper @@ -0,0 +1,3 @@ +#autoload + +reply=( "$(ps 2>/dev/null)" ) diff --git a/Functions/Completion/_limits b/Functions/Completion/_limits new file mode 100644 index 000000000..35ccbe07e --- /dev/null +++ b/Functions/Completion/_limits @@ -0,0 +1,3 @@ +#defcomp limit unlimit + +complist -k "(${(j: :)${(f)$(limit)}%% *})" diff --git a/Functions/Completion/_main_complete b/Functions/Completion/_main_complete new file mode 100644 index 000000000..003a01785 --- /dev/null +++ b/Functions/Completion/_main_complete @@ -0,0 +1,48 @@ +#autoload + +# The main loop of the completion code. This is what is called when +# completion is attempted from the command line. +# The completion code gives us the special variables and the arguments +# from the command line are given as positional parameters. + +local comp name + +setopt localoptions nullglob rcexpandparam globdots +unsetopt markdirs globsubst shwordsplit nounset + +# An entry for `-first-' is the replacement for `compctl -T' +# Completion functions may set `COMPSKIP' to any value to make the +# main loops stop calling other completion functions. + +comp="$comps[-first-]" +if [[ ! -z "$comp" ]]; then + "$comp" "$@" + if (( $+COMPSKIP )); then + unset COMPSKIP + return + fi +fi + +# For arguments we use the `_normal function. + +if [[ $CONTEXT == argument || $CONTEXT == command ]]; then + _normal "$@" +else + # Let's see if we have a special completion definition for the other + # possible contexts. + + comp='' + + case $CONTEXT in + redirect) comp="$comps[-redirect-]";; + math) comp="$comps[-math-]";; + subscript) comp="$comps[-subscript-]";; + value) comp="$comps[-value-]";; + condition) comp="$comps[-condition-]";; + esac + + # If not, we use default completion, if any. + + [[ -z "$comp" ]] && comp="$comps[-default-]" + [[ -z "$comp" ]] || "$comp" "$@" +fi diff --git a/Functions/Completion/_make b/Functions/Completion/_make new file mode 100644 index 000000000..d576b0308 --- /dev/null +++ b/Functions/Completion/_make @@ -0,0 +1,3 @@ +#defcomp make gmake pmake + +complist -s "\$(awk '/^[a-zA-Z0-9][^/ ]+:/ {print \$1}' FS=: [mM]akefile)" diff --git a/Functions/Completion/_man b/Functions/Completion/_man new file mode 100644 index 000000000..8204fba0b --- /dev/null +++ b/Functions/Completion/_man @@ -0,0 +1,11 @@ +#defcomp man +setopt localoptions rcexpandparam + +local rep +if [[ $2 = (<->*|ln) ]]; then + rep=( $manpath/(man|cat)$2/$PREFIX*$SUFFIX.<->*(N:t:r) ) +else + rep=( $manpath/(man|cat)*/$PREFIX*$SUFFIX.<->*(N:t:r) ) +fi + +(( $#rep )) && compadd -m $rep diff --git a/Functions/Completion/_mh b/Functions/Completion/_mh new file mode 100644 index 000000000..67ce49fd2 --- /dev/null +++ b/Functions/Completion/_mh @@ -0,0 +1,70 @@ +#defcomp folder comp inc mark refile repl scan show next prev rmm pick whom mhn mhpath mhpatch + +# Completion for all possible MH commands. +# Alter the following two to your own mh directory and the directory +# where standard mh library files live. (It works anyway, but this +# will save a little time.) +local mymhdir=~/Mail +local mhlib=/usr/lib/mh + +# To be on the safe side, check this exists and if not, get it anyway. +[[ -d $mymhdir ]] || mymhdir=$(mhpath +) + +if [[ -iprefix - ]]; then + # get list of options, which MH commands can generate themselves + # awk is just too icky to use for this, sorry. send me one if + # you come up with it. + compadd -m $($COMMAND -help | perl -ne 'if (/^\s*-\(?(\S+)/) { + $n = $1; + $n =~ s/\)//g; + print $n =~ s/^\[([a-z]+)\]// ? "$n\n$1$n\n" : "$n\n"; + }') + return +elif [[ -iprefix '+' || -iprefix '@' || -current -1 -draftfolder ]]; then + # Complete folder names. + local mhpath + if [[ $IPREFIX != '@' ]]; then + [[ $IPREFIX = '+' ]] || IPREFIX=+ + mhpath=$mymhdir + else + mhpath=$(mhpath) + fi + + # painless, or what? + complist -W mhpath -/ +elif [[ -mcurrent -1 -(editor|(whatnow|rmm|show|more)proc) ]]; then + complist -c +elif [[ -current -1 -file ]]; then + complist -f +elif [[ -mcurrent -1 -(form|audit|filter) ]]; then + # Need some MH template file, which may be in our own MH directory + # or with the standard library. + local mhfpath + # This is the only place we need mhlib, so leave the test till here. + [[ -d $mhlib ]] || { mhlib=$(mhparam mhlproc); mhlib=$mhlib:h; } + mhfpath=($mymhdir $mhlib) + + complist -W mhfpath -g '*(.)' +elif [[ -mcurrent -1 -(no|)cc ]]; then + compadd -m all to cc me +elif [[ -mcurrent -1 -[rw]cache ]]; then + compadd -m public private never ask +else + # Generate sequences. + local foldnam folddir f + for f in $argv; do + [[ $f = [@+]* ]] && foldnam=$f + done + if [[ $foldnam = '+'* ]]; then + folddir=$mymhdir/${foldnam#+} + elif [[ $foldnam = '@'* ]]; then + folddir=$(mhpath)/${foldnam#@} + else + folddir=$(mhpath) + # leaving foldnam empty works here + fi + + complist -s '$(mark $foldnam | awk -F: '\''{ print $1 }'\'')' + compadd -m reply next cur prev first last all unseen + complist -W folddir -g '<->' +fi diff --git a/Functions/Completion/_most_recent_file b/Functions/Completion/_most_recent_file new file mode 100644 index 000000000..69900e6ff --- /dev/null +++ b/Functions/Completion/_most_recent_file @@ -0,0 +1,3 @@ +#defkeycomp expand-or-complete \C-xm + +complist -g '*(om[1])' diff --git a/Functions/Completion/_normal b/Functions/Completion/_normal new file mode 100644 index 000000000..22cc1decf --- /dev/null +++ b/Functions/Completion/_normal @@ -0,0 +1,56 @@ +#autoload + +local comp cmd1 cmd2 pat val name + +# Completing in command position? If not we set up `cmd1' and `cmd2' as +# two strings we have search in the completion definition arrays (e.g. +# a path and the last path name component). + +if [[ $CONTEXT == command ]]; then + comp="$comps[-command-]" + [[ -z "$comp" ]] || "$comp" "$@" + return +elif [[ "$COMMAND[1]" == '=' ]]; then + eval cmd1\=$COMMAND + cmd2="$COMMAND[2,-1]" +elif [[ "$COMMAND" == */* ]]; then + cmd1="$COMMAND" + cmd2="${COMMAND:t}" +else + cmd1="$COMMAND" + eval cmd2=$(whence -p $COMMAND) +fi + +# See if there are any matching pattern completions. + +if (( $#patcomps )); then + for i in "$patcomps[@]"; do + pat="${i% *}" + val="${i#* }" + if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]]; then + "$val" "$@" + if (( $+COMPSKIP )); then + unset COMPSKIP + return + fi + fi + done +fi + +# Now look up the two names in the normal completion array. + +name="$cmd1" +comp="$comps[$cmd1]" + +if [[ -z "$comp" ]]; then + name="$cmd2" + comp="$comps[$cmd2]" +fi + +# And generate the matches, probably using default completion. + +if [[ -z "$comp" ]]; then + name=-default- + comp="$comps[-default-]" +fi +[[ -z "$comp" ]] || "$comp" "$@" diff --git a/Functions/Completion/_path_files b/Functions/Completion/_path_files new file mode 100644 index 000000000..7db364b7d --- /dev/null +++ b/Functions/Completion/_path_files @@ -0,0 +1,272 @@ +#autoload + +# Utility function for in-path completion. +# First argument should be an complist-option (e.g. -f, -/, -g). The other +# arguments should be glob patterns, one per argument. +# +# E.g.: _path_files -g '*.tex' '*.texi' +# +# This is intended as a replacement for `complist -f', `complist -/', and +# `complist -g ...' (but don't use it with other options). +# +# You may also give the `-W <spec>' option as with `compctl' and `complist', +# but only as the first argument. +# +# This function also accepts an optional `-F <string>' option as its first +# argument or just after the `-W <spec>'. This can be used to define file +# name extension (a la `fignore'). Files with such an extension will not +# be considered possible completions. +# +# This function behaves as if you have a matcher definition like: +# compctl -M 'r:|[-.,_/]=* r:|=* m:{a-z}={A-Z} m:-=_ m:.=,' \ +# 'm:{a-z}={A-Z} l:|=* r:|=*' +# so you may want to modify this. + +local nm prepaths str linepath realpath donepath patstr prepath testpath rest +local tmp1 collect tmp2 suffixes i ignore + +setopt localoptions nullglob rcexpandparam globdots extendedglob +unsetopt markdirs globsubst shwordsplit nounset + +# Get the optional `-W' option and its argument. +if [[ "$1" = -W ]]; then + tmp1="$2" + if [[ "$tmp1[1]" = '(' ]]; then + prepaths=( $tmp1[2,-2]/ ) + else + prepaths=( ${(P)${tmp1}} ) + [[ $#prepaths -eq 0 ]] && prepaths=( $tmp1/ ) + fi + [[ $#prepaths -eq 0 ]] && prepaths=( '' ) + shift 2 +else + prepaths=( '' ) +fi + +# Get the optional `-F' option and its argument. +if [[ "$1" = -F ]]; then + ignore=(-F "$2") + shift 2 +else + ignore='' +fi + +# str holds the whole string from the command line with a `*' between +# the prefix and the suffix. + +str="${PREFIX:q}*${SUFFIX:q}" + +# We will first try normal completion called with `complist', but only if we +# weren't given a `-F' option. + +if [[ -z "$ignore" ]]; then + # First build an array containing the `-W' option, if there is any and we + # want to use it. We don't want to use it if the string from the command line + # is a absolute path or relative to the current directory. + + if [[ -z "$tmp1[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]]; then + tmp1=() + else + tmp1=(-W "( $prepaths )") + fi + + # Now call complist. + + nm=$NMATCHES + if [[ $# -eq 0 ]]; then + complist "$tmp1[@]" -f + elif [[ "$1" = -g ]]; then + complist "$tmp1[@]" -g "$argv[2,-1]" + shift + else + complist "$tmp1[@]" $1 + shift + fi + + # If this generated any matches, we don't wnat to do in-path completion. + + [[ -nmatches nm ]] || return + + # No `-F' option, so we want to use `fignore'. + + ignore=(-F fignore) +fi + +# If we weren't given any file patterns as arguments, we trick ourselves +# into believing that we were given the pattern `*'. This is just to simplify +# the following code. + +[[ -z "$1" ]] && 1='*' + +# Now let's have a closer look at the string to complete. + +if [[ "$str[1]" = \~ ]]; then + # It begins with `~', so remember anything before the first slash to be able + # to report it to the completion code. Also get an expanded version of it + # (in `realpath'), so that we can generate the matches. Then remove that + # prefix from the string to complete, set `donepath' to build the correct + # paths and make sure that the loop below is run only once with an empty + # prefix path by setting `prepaths'. + + linepath="${str%%/*}/" + eval realpath\=path + str="${str#*/}" + donepath='' + prepaths=( '' ) +else + # If the string does not start with a `~' we don't remove a prefix from the + # string. + + liniepath='' + realpath='' + + if [[ "$str[1]" = / ]]; then + # If it is a absolut path name, we remove the first slash and put it in + # `donepath' meaning that we treat it as the path that was already handled. + # Also, we don't use the paths from `-W'. + + str="$str[2,-1]" + donepath='/' + prepaths=( '' ) + else + # The common case, we just use the string as it is, unless it begins with + # `./' or `../' in which case we don't use the paths from `-W'. + + [[ "$str" = (.|..)/* ]] && prepaths=( '' ) + donepath='' + fi +fi + +# First we skip over all pathname components in `str' which really exist in +# the file-system, so that `/usr/lib/l<TAB>' doesn't offer you `lib' and +# `lib5'. Pathname components skipped this way are taken from `str' and added +# to `donepath'. + +while [[ "$str" = */* ]] do + [[ -e "$realpath$donepath${str%%/*}" ]] || break + donepath="$donepath${str%%/*}/" + str="${str#*/}" +done + +# Now build the glob pattern. As noted above, this function behaves as if +# a global matcher with two matching specifications are given. + +if [[ -matcher 1 ]]; then + patstr="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*." +else + patstr="${str%/*}/*${str##*/}*" + patstr="$patstr:gs./.*/.:gs.**.*." +fi + +# Finally, generate the matches. First we loop over all the paths from `-W'. +# Note that in this loop `str' is used as a modifyable version of `patstr' +# and `testpath' is a modifyable version of `donepath'. + +for prepath in "$prepaths[@]"; do + str="$patstr" + testpath="$donepath" + + # The second loop tests the components of the path in `str' to get the + # possible matches. + + while [[ "$str" = */* ]] do + # `rest' is the pathname after the first slash that is left. In `tmp1' + # we get the globbing matches for the pathname component currently + # handled. + + rest="${str#*/}" + tmp1="${prepath}${realpath}${testpath}(#l)${str%%/*}(-/)" + tmp1=( $~tmp1 ) + + if [[ $#tmp1 -eq 0 ]]; then + # If this didn't produce any matches, we don't need to test this path + # any further, so continue with the next `-W' path, if any. + + continue 2 + elif [[ $#tmp1 -gt 1 ]]; then + # If it produced more than one match, we want to remove those which + # don't have possible following pathname components matching the + # rest of the string we are completing. (The case with only one + # match is handled below.) + # In `collect' we will collect those of the produced pathnames that + # have a matching possible path-suffix. In `suffixes' we build an + # array containing strings build from the rest of the string to + # complete and the glob patterns we were given as arguments. + + collect=() + suffixes=( $rest$@ ) + suffixes=( "${(@)suffixes:gs.**.*.}" ) + + # In the loop the prefixes from the `tmp1' array produced above and + # the suffixes we just built are used to produce possible matches + # via globbing. + + for i in $tmp1; do + tmp2=( $~i/(#l)$~suffixes ) + [[ $#tmp2 -ne 0 ]] && collect=( $collect $i ) + done + + # If this test showed that none of the matches from the glob in `tmp1' + # has a possible sub-path matching what's on the line, we give up and + # continue with the next `-W' path. + + if [[ $#collect -eq 0 ]]; then + continue 2 + elif [[ $#collect -ne 1 ]]; then + # If we have more than one possible match, this means that the + # pathname component currently handled is ambiguous, so we give + # it to the completion code. + # First we build the full path prefix in `tmp1'. + + tmp1="$prepath$realpath$testpath" + + # Now produce all matching pathnames in `collect'. + + collect=( $~collect/(#l)$~suffixes ) + + # And then remove the common path prefix from all these matches. + + collect=( ${collect#$tmp1} ) + + # Finally, we add all these matches with the common (unexpanded) + # pathprefix (the `-p' option), the path-prefix (the `-W' option) + # to allow the completion code to test file type, and the path- + # suffix (the `-s' option). We also tell the completion code that + # these are file names and that `fignore' should be used as usual + # (the `-f' and `-F' options). + + for i in $collect; do + compadd -p "$linepath$testpath" -W "$tmp1" -s "/${i#*/}" -f "$ignore[@]" - "${i%%/*}" + done + + # We have just finished handling all the matches from above, so we + # can continue with the next `-W' path. + + continue 2 + fi + # We reach this point if only one of the path prefixes in `tmp1' + # has a existing path-suffix matching the string from the line. + # In this case we accept this match and continue with the next + # path-name component. + + tmp1=( "$collect[1]" ) + fi + # This is also reached if the first globbing produced only one match + # in this case we just continue with the next pathname component, too. + + tmp1="$tmp1[1]" + testpath="$testpath${tmp1##*/}/" + str="$rest" + done + + # We are here if all pathname components except the last one (which is still + # not tested) are unambiguous. So we add matches with the full path prefix, + # no path suffix, the `-W' we are currently handling, all the matches we + # can produce in this directory, if any. + + tmp1="$prepath$realpath$testpath" + suffixes=( $str$@ ) + suffixes=( "${(@)suffixes:gs.**.*.}" ) + tmp2=( $~tmp1(#l)$~suffixes ) + compadd -p "$linepath$testpath" -W "$prepath$realpath$testpath" -f "$ignore[@]" - ${tmp2#$tmp1} +done diff --git a/Functions/Completion/_pdf b/Functions/Completion/_pdf new file mode 100644 index 000000000..7d7756c3e --- /dev/null +++ b/Functions/Completion/_pdf @@ -0,0 +1,3 @@ +function acroread + +_files -g '*.(pdf|PDF)' diff --git a/Functions/Completion/_precommand b/Functions/Completion/_precommand new file mode 100644 index 000000000..2cf661147 --- /dev/null +++ b/Functions/Completion/_precommand @@ -0,0 +1,5 @@ +#defcomp - nohup nice eval time rusage noglob nocorrect exec + +[[ -position 1 -1 ]] + +_normal "$@" diff --git a/Functions/Completion/_ps b/Functions/Completion/_ps new file mode 100644 index 000000000..6bc0643b2 --- /dev/null +++ b/Functions/Completion/_ps @@ -0,0 +1,3 @@ +#defcomp gs ghostview gview psnup psselect pswrap pstops pstruct lpr + +_files -g '*([pP][sS]|eps)' diff --git a/Functions/Completion/_rcs b/Functions/Completion/_rcs new file mode 100644 index 000000000..537db6278 --- /dev/null +++ b/Functions/Completion/_rcs @@ -0,0 +1,9 @@ +#defcomp co ci rcs + +[[ $COMMAND = ci || $COMMAND = rcs ]] && _files + +if [[ $NMATCHES -eq 0 && -d RCS && $COMMAND != ci ]]; then + local rep + rep=(RCS/$PREFIX*$SUFFIX,v(:t:s/\,v//)) + (( $#rep )) && compadd -m $rep +fi diff --git a/Functions/Completion/_rlogin b/Functions/Completion/_rlogin new file mode 100644 index 000000000..e36554f23 --- /dev/null +++ b/Functions/Completion/_rlogin @@ -0,0 +1,9 @@ +#defcomp rlogin rsh ssh + +if [[ -position 1 ]]; then + complist -k hosts +elif [[ -position 2 ]]; then + complist -k '(-l)' +else + complist -u +fi diff --git a/Functions/Completion/_sched b/Functions/Completion/_sched new file mode 100644 index 000000000..ee785879b --- /dev/null +++ b/Functions/Completion/_sched @@ -0,0 +1,3 @@ +#defcomp sched + +[[ -position 2 -1 ]] && compsub diff --git a/Functions/Completion/_set b/Functions/Completion/_set new file mode 100644 index 000000000..e3069f134 --- /dev/null +++ b/Functions/Completion/_set @@ -0,0 +1,7 @@ +#defcomp set + +if [[ -mcurrent -1 [-+]o ]]; then + complist -o +elif [[ -current -1 -A ]]; then + complist -A +fi diff --git a/Functions/Completion/_setopt b/Functions/Completion/_setopt new file mode 100644 index 000000000..4abb3ccee --- /dev/null +++ b/Functions/Completion/_setopt @@ -0,0 +1,7 @@ +#defcomp setopt + +local nm=$NMATCHES + +complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \ + -s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)' +[[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o diff --git a/Functions/Completion/_source b/Functions/Completion/_source new file mode 100644 index 000000000..94e3fbeb1 --- /dev/null +++ b/Functions/Completion/_source @@ -0,0 +1,7 @@ +#defcomp source + +if [[ -position 2 -1 ]]; then + compsub +else + _files +fi diff --git a/Functions/Completion/_strip b/Functions/Completion/_strip new file mode 100644 index 000000000..6962ac455 --- /dev/null +++ b/Functions/Completion/_strip @@ -0,0 +1,2 @@ +#defcomp strip +_files -g '*(*)' diff --git a/Functions/Completion/_stty b/Functions/Completion/_stty new file mode 100644 index 000000000..6b54b5007 --- /dev/null +++ b/Functions/Completion/_stty @@ -0,0 +1,16 @@ +#defcomp stty + +if [[ -mcurrent -1 \ + (*erase|discard|status|dsusp|intr|kill|lnext|quit|reprint|start|s*p) ]] +then + compadd -m -Q '^-' '^h' '^?' '^c' '^u' +else + [[ -string '-' || -string '+' ]] + compadd -m rows columns intr quit erase kill eof eol \ + eol2 start stop susp dsusp reprint discard werase lnext \ + parenb parodd cs8 cstopb hupcl cread clocal parext \ + ignbrk brkint ignpar parmrk inpck istrip inlcr igncr icrnl iuclc \ + ixon ixany ixoff imaxbel isig icanon xcase echo echoe echok \ + echonl noflsh tostop echoctl echoprt echoke flusho pending iexten \ + opost olcuc onlcr ocrnl onocr onlret ofill ofdel +fi diff --git a/Functions/Completion/_subscript b/Functions/Completion/_subscript new file mode 100644 index 000000000..2b827a117 --- /dev/null +++ b/Functions/Completion/_subscript @@ -0,0 +1,4 @@ +#defcomp -subscript- + +_compalso -math- "$@" +[[ ${(Pt)${COMMAND}} = assoc* ]] && complist -k "( ${(kP)${COMMAND}} )" diff --git a/Functions/Completion/_tar b/Functions/Completion/_tar new file mode 100644 index 000000000..91767e44d --- /dev/null +++ b/Functions/Completion/_tar @@ -0,0 +1,11 @@ +#defcomp tar + +local nm=$NMATCHES tf="$2" + +if [[ ( -mword 1 *t*f* || -mword 1 *x*f* ) && -position 3 100000 ]]; then + complist -k "( $(tar tf $tf) )" +elif [[ -mword 1 *c*f* && -position 3 100000 ]]; then + _files +elif [[ -mcurrent -1 *f* && -position 2 ]]; then + _files -g '*.(tar|TAR)' +fi diff --git a/Functions/Completion/_tex b/Functions/Completion/_tex new file mode 100644 index 000000000..1f645e2a2 --- /dev/null +++ b/Functions/Completion/_tex @@ -0,0 +1,3 @@ +#defcomp tex latex slitex + +_files -g '*.(tex|TEX|texinfo|texi)' diff --git a/Functions/Completion/_trap b/Functions/Completion/_trap new file mode 100644 index 000000000..59e81c589 --- /dev/null +++ b/Functions/Completion/_trap @@ -0,0 +1,7 @@ +#defcomp trap + +if [[ -position 1 ]]; then + complist -c +else + complist -k signals +fi diff --git a/Functions/Completion/_uncompress b/Functions/Completion/_uncompress new file mode 100644 index 000000000..e25805d50 --- /dev/null +++ b/Functions/Completion/_uncompress @@ -0,0 +1,3 @@ +#defcomp uncompress zmore + +_files -g '*.Z' diff --git a/Functions/Completion/_unhash b/Functions/Completion/_unhash new file mode 100644 index 000000000..fe40c25a2 --- /dev/null +++ b/Functions/Completion/_unhash @@ -0,0 +1,6 @@ +#defcomp unhash + +[[ -mword 1 -*d* ]] && complist -n +[[ -mword 1 -*a* ]] && complist -a +[[ -mword 1 -*f* ]] && complist -F +[[ ! -mword 1 -* ]] && complist -m diff --git a/Functions/Completion/_unsetopt b/Functions/Completion/_unsetopt new file mode 100644 index 000000000..90d642b51 --- /dev/null +++ b/Functions/Completion/_unsetopt @@ -0,0 +1,7 @@ +#defcomp unsetopt + +local nm=$NMATCHES + +complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \ + -s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)' +[[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o diff --git a/Functions/Completion/_vars b/Functions/Completion/_vars new file mode 100644 index 000000000..7153b6f38 --- /dev/null +++ b/Functions/Completion/_vars @@ -0,0 +1,3 @@ +#defcomp -math- getopts read unset vared + +complist -v diff --git a/Functions/Completion/_vars_eq b/Functions/Completion/_vars_eq new file mode 100644 index 000000000..fcbb0148c --- /dev/null +++ b/Functions/Completion/_vars_eq @@ -0,0 +1,3 @@ +#defcomp declare export integer local readonly typeset + +complist -v -q -S '=' diff --git a/Functions/Completion/_wait b/Functions/Completion/_wait new file mode 100644 index 000000000..34bc53ee3 --- /dev/null +++ b/Functions/Completion/_wait @@ -0,0 +1,4 @@ +#defcomp wait + +complist -P '%' -j +complist -y _kill_helper -s '`ps 2>/dev/null | tail +2 | cut -c1-5`' diff --git a/Functions/Completion/_which b/Functions/Completion/_which new file mode 100644 index 000000000..324256e3d --- /dev/null +++ b/Functions/Completion/_which @@ -0,0 +1,3 @@ +#defcomp which whence where type + +complist -caF diff --git a/Functions/Completion/_x_options b/Functions/Completion/_x_options new file mode 100644 index 000000000..cc469286d --- /dev/null +++ b/Functions/Completion/_x_options @@ -0,0 +1,5 @@ +#defpatcomp */X11/* + +# A simple pattern completion, just as an example. + +complist -J options -k '(-display -name -xrm)' diff --git a/Functions/Completion/_xfig b/Functions/Completion/_xfig new file mode 100644 index 000000000..fcd2bba9f --- /dev/null +++ b/Functions/Completion/_xfig @@ -0,0 +1,3 @@ +#defcomp xfig + +_files -g '*.fig' diff --git a/Functions/Completion/_zftp b/Functions/Completion/_zftp new file mode 100644 index 000000000..4ff707cc6 --- /dev/null +++ b/Functions/Completion/_zftp @@ -0,0 +1,50 @@ +#defpatcomp zf* + +# Don't try any more completion after this. +COMPSKIP=1 + +# Completion for zftp builtin and zf* functions. The functions +# zfcd_match and zfget_match (used for old-style completion) +# need to be installed for remote file and directory completion to work. + +local subcom + +if [[ $COMMAND = zftp ]]; then + if [[ $CURRENT -eq 1 ]]; then + compadd -m open params user login type ascii binary mode put \ + putat get getat append appendat ls dir local remote mkdir rmdir + return + fi + subcom=$1 +else + subcom=$COMMAND +fi + +case $subcom in + *(cd|ls|dir)) + # complete remote directories; we could be smarter about hiding prefixes + zfcd_match $PREFIX $SUFFIX + (( $#reply )) && compadd -m -S/ -q $reply + ;; + + *(get(|at)|gcp|delete|remote)) + # complete remote files + zfget_match $PREFIX $SUFFIX + (( $#reply )) && compadd -F fignore -m $reply + ;; + + *(put(|at)|pcp)) + # complete local files + _files + ;; + + *(open|anon|params)) + # complete hosts: should do cleverer stuff with user names + complist -k hosts + ;; + + *) + # dunno... try ordinary completion after all. + unset COMPSKIP + ;; +esac diff --git a/Functions/Completion/_zle b/Functions/Completion/_zle new file mode 100644 index 000000000..bb1102e74 --- /dev/null +++ b/Functions/Completion/_zle @@ -0,0 +1,7 @@ +#defcomp zle + +if [[ -word 1 -N && -position 3 ]]; then + complist -F +else + complist -b +fi diff --git a/Functions/Completion/_zmodload b/Functions/Completion/_zmodload new file mode 100644 index 000000000..112acb57c --- /dev/null +++ b/Functions/Completion/_zmodload @@ -0,0 +1,9 @@ +#defcomp zmodload + +if [[ -mword 1 -*(a*u|u*a)* || -mword 1 -*a* && -position 3 -1 ]]; then + complist -B +elif [[ -mword 1 -*u* ]]; then + complist -s '$(zmodload)' +else + complist -s '${^module_path}/*(N:t:r)' +fi diff --git a/Functions/Completion/dump b/Functions/Completion/dump new file mode 100644 index 000000000..ad5547bea --- /dev/null +++ b/Functions/Completion/dump @@ -0,0 +1,89 @@ +# This is a file to be sourced to dump the definitions for new-style +# completion defined by 'init' in the same directory. For best results, +# it should be run immediately after init, before any of the completions +# have been autoloaded. The output should be directed into the "init.dump" +# in the same directory as init. If you rename init, just stick .dump onto +# the end of whatever you have called it and put it in the same directory. +# +# You will need to update the dump every time you add a new completion. +# To do this, simply remove the .dump file, start a new shell, and +# create the .dump file as before. +# +# It relies on KSH_ARRAYS not being set. + +# Print the number of files used for completion. This is used in init +# to see if auto-dump should re-dump the dump-file. + +_d_file=${COMPDUMP-${0:h}/init.dump} + +_d_files=( ${^~fpath}/_*~*~ ) + +print "#files: $#_d_files" > $_d_file + +unset _d_files + +# First dump the arrays comps and patcomps. The quoting hieroglyphyics +# ensure that a single quote inside a variable is itself correctly quoted. + +print "comps=(" >> $_d_file +for _d_f in ${(k)comps}; do + print -r - "'${_d_f//\'/'\\''}'" "'${comps[$_d_f]//\'/'\\''}'" +done >> $_d_file +print ")" >> $_d_file + +if (( $#patcomps )); then + print "\npatcomps=(" >> $_d_file + for _d_f in "$patcomps[@]"; do + print -r - "'${_d_f//\'/'\\''}'" + done >> $_d_file + print ")" >> $_d_file +fi + +print >> $_d_file + +# Now dump the key bindings. We dump all bindings for zle widgets +# whose names start with a underscore. +# We need both the zle -C's and the bindkey's to recreate. + +_d_bks=() +zle -lL | + while read -rA _d_line; do + if [[ ${_d_line[5]} = _* ]]; then + print -r - ${_d_line} + _d_bks=($_d_bks ${_d_line[3]}) + fi + done >> $_d_file +bindkey | + while read -rA _d_line; do + if [[ ${_d_line[2]} = (${(j.|.)~_d_bks}) ]]; then + print -r "bindkey '${_d_line[1][2,-2]}' ${_d_line[2]}" + fi + done >> $_d_file + +print >> $_d_file + + +# Autoloads: whence -w produces "_d_foo: function", so look for +# all functions beginning with `_'. + +_d_als=($(whence -wm '_*' | +while read -rA _d_line; do + [[ ${_d_line[2]} = function ]] && print -r - ${_d_line[1]%:} +done)) + +# print them out: about six to a line looks neat + +while (( $#_d_als )); do + print -n autoload + for (( _i = 0; _i < 6; _i++ )); do + if (( $#_d_als )); then + print -n " $_d_als[1]" + shift _d_als + fi + done + print +done >> $_d_file + +print >> $_d_file + +unset _d_line _d_zle _d_bks _d_als _d_f _f_file diff --git a/Functions/Completion/init b/Functions/Completion/init index a40c5f61b..35e7f6a3c 100644 --- a/Functions/Completion/init +++ b/Functions/Completion/init @@ -3,34 +3,22 @@ # directory that will automatically be made autoloaded (see the end of this # file). # The names of the files that will be considered for autoloading have to -# start with two underscores (like `__setopt). +# start with a underscores (like `_setopt). # The first line of these files will be read and has to say what should be # done with its contents: # -# `#function <names ...>' +# `#defcomp <names ...>' # if the first line looks like this, the file is # autoloaded as a function and that function will # be called to generate the matches when completing # for one of the commands whose <name> is given # -# `#array <names ...>' -# with a first line like this, the filename is taken as -# the name of an array; when trying to generate matches -# for the command <name>, the file will be sourced and -# should define this array, the builtin `complist' will -# then be called with the elements of this array as its -# arguments; this is intended for simple definitions -# for which you don't need a shell function -# -# `#pattern-function <pattern>' +# `#defpatcomp <pattern>' # this defines a function that should be called to generate # matches for commands whose name matches <pattern>; note # that only one pattern may be given # -# `#pattern-array <pattern>' -# like `#pattern-function' but defining an array -# -# `#key-function <style> [ <key-sequence> ... ] +# `#defkeycomp <style> [ <key-sequence> ... ] # this is used to bind special completions to all the given # <key-sequence>(s). The <style> is the name of one of the built-in # completion widgets (complete-word, delete-char-or-list, @@ -41,45 +29,47 @@ # rather than by the context. The widget has the same name as # the autoload file and can be bound using bindkey in the normal way. # -# `#key-array <style> [ <key-sequence> ... ] -# like `#key-function', but defining an array instead -# -# `#helper' +# `#autoload' # this is for helper functions that are not used to # generate matches, but should automatically be loaded # when they are called # # Note that no white space is allowed between the `#' and the rest of # the string. +# +# See the file `dump' for how to speed up initialiation. +# If we got the `-d'-flag, we will automatically dump the new state (at +# the end). -# An associative array for completions definitions. The keys of the entries -# are the names of the command, the values are names of functions or variables -# that are to be used to generate the matches. -# Pattern completions will be stored in an normal array named `patcomps'. -# Completion definitions bound directly to keys are stored in an assoc array -# named `keycomps'. +if [[ "$1" = -d ]]; then + _i_autodump=1 +else + _i_autodump=0 +fi + +# The associative array containing the definitions for the commands. +# Definitions for patterns will be stored in the normal array `patcomps'. typeset -A comps -typeset -A keycomps # This may be used to define completion handlers. The first argument is the -# name of the function or variable containing the definition, the other -# arguments are the command names for which this definition should be used. -# With only one argument the function/variable-name __$1 is used. +# name of the function containing the definition, the other arguments are the +# command names for which this definition should be used. +# With only one argument the function/variable-name _$1 is used. # If given the `-a' option, the function is defined as being autoloaded. defcomp() { - local name autol='' + local name if [[ "$1" = -a ]]; then shift autol=yes fi if [[ $# -eq 1 ]]; then - comps[$1]="__$1" - [[ -z "$autol" ]] || autoload "__$1" + comps[$1]="_$1" + [[ -z "$autol" ]] || autoload "_$1" else name="$1" shift @@ -90,10 +80,9 @@ defcomp() { fi } - # Almost like `defcomp', but this always gets two arguments: the name of a -# variable or function describing what should be completed and the pattern -# that will be compared to the command names for which completion is attempted. +# function describing what should be completed and the pattern that will be +# compared to the command names for which completion is attempted. defpatcomp() { if [[ "$1" = -a ]]; then @@ -101,13 +90,12 @@ defpatcomp() { autoload "$1" fi if (( $+patcomps )) then - patcomps=("$patcomps[@]" "$2 $1" ) + patcomps=("$patcomps[@]" "$2 $1") else - patcomps=( "$2 $1" ) + patcomps=("$2 $1") fi } - # This is used to define completion handlers directly bound to keys. The # first argument is as for `defcomp', giving the handler. The second # argument is the name of one of the built-in completion widgets. Any @@ -122,15 +110,10 @@ defkeycomp() { if [[ "$1" = -a ]]; then shift autoload "$1" - name="$1" - elif [[ "${1[1]}" = ' ' ]]; then - name="${1:t}" - else - name="$1" fi - keycomps[$name]="$1" + name="$1" shift - zle -C "$name" "$1" __main_key_complete + zle -C "$name" "$1" "$name" shift while (( $# )); do bindkey "$1" "$name" @@ -138,116 +121,101 @@ defkeycomp() { done } -# These can be used to easily save and restore the state of the special -# variables used by the completion code. - -alias compsave='local _oprefix _oiprefix _oargv _ocurrent; \ - _oprefix="$PREFIX"; \ - _oiprefix="$IPREFIX"; \ - _oargv=( "$@" ); \ - _ocurrent="$CURRENT"' -alias compreset='PREFIX="$_oprefix"; \ - IPREFIX="$_oiprefix"; \ - argv=( "$_oargv[@]" ); \ - CURRENT="$_ocur"' - - -# This is an easy way to get completion for sub-commands. - -alias compsub='__normal "$@" || return 1' - - # This searches $1 in the array for normal completions and calls the result. -compalso() { +_compalso() { local tmp tmp="$comps[$1]" - [[ -z "$tmp" ]] || callcomplete comps "$1" "$@" + [[ -z "$tmp" ]] || "$tmp" "$@" } +# These can be used to easily save and restore the state of the special +# variables used by the completion code. -# This generates matches. The first argument is the name of one of the -# arrays containing completion definitions. The second argument is the index -# into this array. The other arguments are the positional parameters to give -# to the completion function (containing the arguments from the command line). - -callcomplete() { - local file def +alias _compsave='local _oprefix$_level _oiprefix$_level _oargv$_level _ocurrent$_level _ocommand$_level _ocontext$_level; \ + eval "_oprefix${_level}=\"\$PREFIX\"; \ + _oiprefix${_level}=\"\$IPREFIX\"; \ + _oargv${_level}=( \"\$@\" ); \ + _ocurrent${_level}=\"\$CURRENT\"; \ + _ocommand${_level}=\"\$COMMAND\"; \ + _ocontext${_level}=\"\$CONTEXT\""' +alias _compreset='eval "PREFIX=\"\$_oprefix${_level}\"; \ + IPREFIX=\"\$_oiprefix${_level}\"; \ + argv=( \"\$_oargv${_level}[@]\" ); \ + CURRENT=\"\$_ocur${_level}\"; \ + COMMAND=\"\$_ocommand${_level}\"; \ + CONTEXT=\"\$_ocontext${_level}\""' - # Get the definition from the array. +# These can be used to build tests that modify the special parameters +# without having to reset them by hand. - eval "def=\$${1}[${2}]" +alias _if='(( $+_level )) || local _level=0; (( _level++ )); _compsave; if' +alias _elif='_compreset; elif' +alias _else='_compreset; else' +alias _fi='_compreset; fi; unset _oprefix$_level _oiprefix$_level _oargv$_level _ocurrent$_level _ocommand$_level _ocontext$_level; (( _level-- ))' - # If the definition starts with a space then this means that we should - # source a file to get the definition for an array. - if [[ "$def[1]" = ' ' ]]; then - # The definition starts with a space, so source the file and change - # the definition. +# Now we automatically make the definition files autoloaded. - file="$def[2,-1]" - builtin . "$file" - def="${file:t}" - eval "${1}[${2}]=$def" - fi +# First we get the name of a dump file if this will be used. - # Get rid of the array-name and -index. +: ${COMPDUMP:=$0.dump} - shift 2 - if [[ ${(P)+def} -eq 1 ]]; then - # It is a parameter name, call complist directly. +_i_files=( ${^~fpath}/_*~*~ ) +_i_initname=$0 +_i_done='' - complist "${(@P)def}" - else - # Otherwise it's a function name, call this function. +# If we have a dump file, load it. - "$def" "$@" +if [[ -f "$COMPDUMP" ]]; then + read -rA _i_line < "$COMPDUMP" + if [[ _i_autodump -eq 1 || $_i_line[2] -eq $#_i_files ]]; then + builtin . "$COMPDUMP" + _i_done=yes fi -} + unset _i_line +fi +if [[ -z "$_i_done" ]]; then + + for _i_dir in $fpath; do + [[ $_i_dir = . ]] && continue + for _i_file in $_i_dir/_*~*~(N); do + read -rA _i_line < $_i_file + _i_tag=$_i_line[1] + shift _i_line + if [[ $_i_tag = '#defcomp' ]]; then + defcomp -a ${_i_file:t} "${_i_line[@]}" + elif [[ $_i_tag = '#defpatcomp' ]]; then + defpatcomp -a "${_i_file:t}" "${_i_line[@]}" + elif [[ $_i_tag = '#defkeycomp' ]]; then + defkeycomp -a "${_i_file:t}" "${_i_line[@]}" + elif [[ $_i_tag = '#autoload' ]]; then + autoload ${_i_file:t} + fi + done + done + + bindkey | + while read -rA _i_line; do + if [[ "$_i_line[2]" = complete-word || + "$_i_line[2]" = delete-char-or-list || + "$_i_line[2]" = expand-or-complete || + "$_i_line[2]" = expand-or-complete-prefix || + "$_i_line[2]" = list-choices || + "$_i_line[2]" = menu-complete || + "$_i_line[2]" = menu-expand-or-complete || + "$_i_line[2]" = reverse-menu-complete ]]; then + zle -C _complete_$_i_line[2] $_i_line[2] _main_complete + bindkey "${_i_line[1][2,-2]}" _complete_$_i_line[2] + fi + done + unset _i_dir _i_line _i_file _i_tag -# Now we make the files automatically autoloaded. - -local dir file line func - -for dir in $fpath; do - [[ $dir = . ]] && continue - for file in $dir/__*~*~(N); do - read -rA line < $file - func=$line[1] - shift line - if [[ $func = '#function' ]]; then - defcomp -a ${file:t} "${line[@]}" - elif [[ $func = '#array' ]]; then - defcomp " $file" "${line[@]}" - elif [[ $func = '#pattern-function' ]]; then - defpatcomp -a ${file:t} "${line[@]}" - elif [[ $func = '#pattern-array' ]]; then - defcomp " $file" "${line[@]}" - elif [[ $func = '#key-function' ]]; then - defkeycomp -a "${file:t}" "${line[@]}" - elif [[ $func = '#key-array' ]]; then - defkeycomp " $file" "${line[@]}" - elif [[ $func = '#helper' ]]; then - autoload ${file:t} - fi - done -done - - -# Finally we make all this be called by changing the key bindings. - -bindkey | while read -A line; do - if [[ "$line[2]" = complete-word || - "$line[2]" = delete-char-or-list || - "$line[2]" = expand-or-complete || - "$line[2]" = expand-or-complete-prefix || - "$line[2]" = list-choices || - "$line[2]" = menu-complete || - "$line[2]" = menu-expand-or-complete || - "$line[2]" = reverse-menu-complete ]]; then - zle -C __complete_$line[2] $line[2] __main_complete - bindkey "${line[1][2,-2]}" __complete_$line[2] - fi - done + # if autodumping was requested, do it now. + + (( _i_autodump )) && builtin . ${_i_initname:h}/dump +fi + +unset _i_files _i_initname _i_done _i_autodump diff --git a/Misc/zftp-functions b/Misc/zftp-functions index a07e46d72..9d943fc1f 100644 --- a/Misc/zftp-functions +++ b/Misc/zftp-functions @@ -164,18 +164,21 @@ alias zfuget='noglob zfuget' setopt completealiases # -# zftp completions +# zftp completions: only use these if new-style completion is not +# active. # -compctl -f -x 'p[1]' \ - -k '(open params user login type ascii binary mode put putat - get getat append appendat ls dir local remote mkdir rmdir delete - close quit)' - \ - 'w[1,cd][1,ls][1,dir][1,rmdir]' -K zfcd_match -S/ -q - \ - 'W[1,get*]' -K zfget_match - 'w[1,delete][1,remote]' -K zfget_match - \ - 'w[1,open][1,params]' -k hosts -- zftp -compctl -K zfcd_match -S/ -q zfcd zfdir zfls -compctl -K zfget_match zfget zfgcp zfuget zfcget -compctl -k hosts zfopen zfparams +if [[ ${#patcomps} -eq 0 || ${patcomps[(i)zf*]} -gt ${#patcomps} ]]; then + compctl -f -x 'p[1]' \ + -k '(open params user login type ascii binary mode put putat + get getat append appendat ls dir local remote mkdir rmdir delete + close quit)' - \ + 'w[1,cd][1,ls][1,dir][1,rmdir]' -K zfcd_match -S/ -q - \ + 'W[1,get*]' -K zfget_match - 'w[1,delete][1,remote]' -K zfget_match - \ + 'w[1,open][1,params]' -k hosts -- zftp + compctl -K zfcd_match -S/ -q zfcd zfdir zfls + compctl -K zfget_match zfget zfgcp zfuget zfcget + compctl -k hosts zfanon zfopen zfparams +fi function zfanon { local opt optlist once @@ -245,6 +248,40 @@ function zfanon { fi } +function zfautocheck { + # This function is used to implement auto-open behaviour. + # + # With first argument including n, don't change to the old directory; else do. + # + # Set do_close to 1 if the connection was not previously open, 0 otherwise + # With first arguemnt including d, don't set do_close to 1. Broadly + # speaking, we use this mechanism to shut the connection after use + # if the connection had been explicitly closed (i.e. didn't time out, + # which zftp test investigates) and we are not using a directory + # command, which implies we are looking for something so should stay open + # for it. + + # Remember the old session: zflastsession will be overwritten by + # a successful open. + local lastsession=$zflastsession + + if [[ -z $ZFTP_HOST ]]; then + zfopen || return 1 + [[ $1 = *d* ]] || do_close=1 + elif zftp test 2>/dev/null; then + return 0 + else + zfopen || return 1 + fi + + if [[ $1 = *n* ]]; then + return 0 + else + zfcd ${lastsession#*:} + fi + +} + function zfcd { # zfcd: change directory on the remote server. # @@ -266,9 +303,11 @@ function zfcd { # second, we can no do 'zfcd $PWD' and the like, and that will # work just as long as the directory structures under the home match. - # Autoopen: if not already open, hope there are parameters set up to - # do so. If not, we get the right error message, so no harm done. - [[ -z $ZFTP_HOST ]] && { zfopen || return 1; } + if [[ $1 = /* ]]; then + zfautocheck -dn + else + zfautocheck -d + fi if [[ $1 = $HOME || $1 = $HOME/* ]]; then 1="~${1#$HOME}" @@ -291,6 +330,7 @@ function zfcd { local lastdir=$ZFTP_PWD zftp cd "$@" && zflastdir=$lastdir + print $zflastsession } function zfcd_match { @@ -303,6 +343,8 @@ function zfcd_match { local ZFTP_VERBOSE=45 # should we redirect 2>/dev/null or let the user see it? + local tmpf=${TMPPREFIX}zfcm$$ + if [[ $ZFTP_SYSTEM = UNIX* ]]; then # hoo, aren't we lucky: this makes things so much easier setopt localoptions rcexpandparam @@ -315,8 +357,9 @@ function zfcd_match { # If we're using -F, we get away with using a directory # to list, but not a glob. Don't ask me why. # I hate having to rely on awk here. - reply=($(zftp ls -F $dir | - awk '/\/$/ { print substr($1, 0, length($1)-1) }')) + zftp ls -F $dir >$tmpf + reply=($(awk '/\/$/ { print substr($1, 0, length($1)-1) }' $tmpf)) + rm -f $tmpf if [[ $dir = / ]]; then reply=(${dir}$reply) elif [[ -n $dir ]]; then @@ -423,6 +466,84 @@ function zfclose { zftp close } +function zfcput { + # Continuation put of files from remote server. + # For each file, if it's shorter over there, put the remainder from + # over here. This uses append, which is standard, so unlike zfcget it's + # expected to work on any reasonable server... err, as long as it + # supports SIZE and MDTM. (It could be enhanced so you can enter the + # size so far by hand.) You should probably be in binary transfer + # mode, thought it's not enforced. + # + # To read from midway through a local file, `tail +<n>c' is used. + # It would be nice to find a way of doing this which works on all OS's. + + setopt localoptions + unsetopt ksharrays shwordsplit + + local loc rem stat=0 locst remst offs tailtype + local tmpfile=${TMPPREFIX}zfcget$$ rstat + + # find how tail works. this is intensely annoying, since it's completely + # standard in C. od's no use, since we can only skip whole blocks. + if [[ $(echo abcd | tail +2c) = bcd ]]; then + tailtype=c + elif [[ $(echo abcd | tail --bytes=+2) = bcd ]]; then + tailtype=b + else + print "I can't get your \`tail' to start from from arbitrary characters.\n" \ + "If you know how to do this, let me know." 2>&1 + return 1 + fi + + for loc in $*; do + # zfcd directory hack to put the front back to ~ + rem=$loc + if [[ $rem = $HOME || $rem = $HOME/* ]]; then + rem="~${rem#$HOME}" + fi + if [[ ! -r $loc ]]; then + print "Can't read file $loc" + stat=1 + else + # Compare the sizes. + locst=($(zftp local $loc)) + zftp remote $rem >$tmpfile + rstat=$? + remst=($(<$tmpfile)) + rm -f $tmpfile + if [[ $rstat = 2 ]]; then + print "Server does not support remote status commands.\n" \ + "You will have to find out the size by hand and use zftp append." 2>&1 + stat=1 + continue + elif [[ $rstat = 1 ]]; then + # Not found, so just do a standard put. + zftp put $rem <$loc + elif [[ $remst[1] -gt $locst[1] ]]; then + print "Remote file is larger!" 2>&1 + continue; + elif [[ $locst[1] == $remst[1] ]]; then + print "Files are already the same size." 2>&1 + continue + else + # tail +<N>c takes the count of the character + # to start from, not the offset from zero. if we did + # this with years, then 2000 would be 1999. no y2k bug! + # brilliant. + (( offs = $remst[1] + 1 )) + if [[ $tailtype = c ]]; then + tail +${offs}c $loc | zftp append $rem || stat=1 + else + tail --bytes=+$offs $loc | zftp append $rem || stat=1 + fi + fi + fi + done + + return $stat +} + function zfdir { # Long directory of remote server. # The remote directory is cached. In fact, two caches are kept: @@ -440,9 +561,11 @@ function zfdir { # zfdir -f will force the existing cache to be ignored, e.g. if you know # or suspect the directory has changed. # zfdir -d will remove both caches without listing anything. - # If you need to pass -r, -f or -d to the dir itself, use zfdir -- -d etc. + # If you need to pass -r, -f or -d to the dir itself, use zfdir -- -d etc.; + # unrecognised options are passed through to dir, but zfdir options must + # appear first and unmixed with the others. - setopt localoptions unset + setopt localoptions unset extendedglob unsetopt shwordsplit ksharrays local file opt optlist redir i newargs force @@ -451,6 +574,9 @@ function zfdir { if [[ $1 = - || $1 = -- ]]; then shift; break; + elif [[ $1 != -[rfd]## ]]; then + # pass options through to ls + break; fi optlist=${1#-} for (( i = 1; i <= $#optlist; i++)); do @@ -465,13 +591,13 @@ function zfdir { zftp_fcache=() return 0 ;; - *) print option $opt not recognised >&2 - ;; esac done shift done + zfautocheck -d + # directory hack, see zfcd for (( i = 1; i <= $#argv; i++ )); do if [[ $argv[$i] = $HOME || $argv[$i] = $HOME/* ]]; then @@ -507,27 +633,38 @@ function zfdir { if [[ -n $file && -f $file ]]; then eval ${PAGER:-more} \$file else - zftp dir $* | tee $file | eval ${PAGER-:more} + if (zftp test); then + # Works OK in subshells + zftp dir $* | tee $file | eval ${PAGER-:more} + else + # Doesn't work in subshells (IRIX 6.2 --- why?) + zftp dir $* >$file + eval ${PAGER-:more} >$file + fi fi } function zfgcp { # ZFTP get as copy: i.e. first arguments are remote, last is local. # Supposed to work exactly like a normal copy otherwise, i.e. - # zfcp rfile lfile + # zfgcp rfile lfile # or - # zfcp rfile1 rfile2 rfile3 ... ldir + # zfgcp rfile1 rfile2 rfile3 ... ldir # Options: # -G don't to remote globbing, else do # -t update the local file times to the same time as the remote. # Currently this only works if you have the `perl' command, # and that perl is version 5 with the standard library. # See the function zfrtime for more gory details. + # + # If there is no current connection, try to use the existing set of open + # parameters to establish one and close it immediately afterwards. setopt localoptions unsetopt shwordsplit - local opt optlist nglob remlist rem loc stat=0 time + local opt optlist nglob remlist rem loc time + integer stat do_close while [[ $1 == -* ]]; do if [[ $1 == - || $1 == -- ]]; then @@ -549,6 +686,8 @@ function zfgcp { shift done + zfautocheck + # hmm, we should really check this after expanding the glob, # but we shouldn't expand the last argument remotely anyway. if [[ $# -gt 2 && ! -d $argv[-1] ]]; then @@ -584,6 +723,9 @@ function zfgcp { else zftp get $1 >$2 || stat=$? fi + + (( $do_close )) && zfclose + return $stat } @@ -594,8 +736,14 @@ function zfget { # Currently this only works if you have the `perl' command, # and that perl is version 5 with the standard library. # See the function zfrtime for more gory details. + # + # If the connection is not currently open, try to open it with the current + # parameters (set by a previous zfopen or zfparams), then close it after + # use. The file is put in the current directory (i.e. using the basename + # of the remote file only); for more control, use zfgcp. - local loc rem stat=0 optlist opt nglob remlist time + local loc rem optlist opt nglob remlist time + integer stat do_close while [[ $1 == -* ]]; do if [[ $1 == - || $1 == -- ]]; then @@ -617,6 +765,8 @@ function zfget { shift done + zfautocheck + for remlist in $*; do # zfcd directory hack to put the front back to ~ if [[ $remlist == $HOME || $remlist == $HOME/* ]]; then @@ -637,6 +787,8 @@ function zfget { fi done + (( $do_close )) && zfclose + return $stat } @@ -646,15 +798,20 @@ function zfget_match { 1="~${1#$HOME}" fi + local tmpf=${TMPPREFIX}zfgm$$ if [[ $ZFTP_SYSTEM == UNIX* && $1 == */* ]]; then # On the first argument to ls, we usually get away with a glob. - reply=($(zftp ls "$1*$2")) + zftp ls "$1*$2" >$tmpf + reply=($(<$tmpf)) + rm -f $tmpf else if (( $#zftp_fcache == 0 )); then # Always cache the current directory and use it # even if the system is UNIX. - zftp_fcache=($(zftp ls)) + zftp ls >$tmpf + zftp_fcache=($(<$tmpf)) + rm -f $tmpf fi reply=($zftp_fcache); fi @@ -671,6 +828,9 @@ function zfls { if [[ $1 = $HOME || $1 = $HOME/* ]]; then 1="~${1#$HOME}" fi + + zfautocheck -d + zftp ls $* } @@ -725,15 +885,62 @@ function zfparams { zftp params $* } +function zfpcp { + # ZFTP put as copy: i.e. first arguments are remote, last is local. + # Currently only supports + # zfcp lfile rfile + # if and only if there are two arguments + # or + # zfcp lfile1 lfile2 lfile3 ... rdir + # if and only if there are more than two (because otherwise it doesn't + # know if the last argument is a directory on the remote machine). + # argument. + + setopt localoptions + unsetopt shwordsplit + + local rem loc + integer stat do_close + + zfautocheck + + if (( $# > 2 )); then + local dir=$argv[-1] + argv[-1]= + # zfcd directory hack to put the front back to ~ + if [[ $dir = $HOME || $dir = $HOME/* ]]; then + dir="~${dir#$HOME}" + fi + for loc in $*; do + rem=$dir/${loc:t} + zftp put $rem <$loc || stat=1 + done + else + zftp put $2 <$1 || stat=$? + fi + + (( $do_close )) && zfclose + + return $stat +} + function zfput { # Simple put: dump every file under the same name, but stripping # off any directory parts. - local loc rem stat=0 + + local loc rem + integer stat do_close + + zfautocheck + for loc in $*; do rem=${loc:t} zftp put $rem <$loc [[ $? == 0 ]] || stat=$? done + + (( $do_close )) && zfclose + return $stat } @@ -764,13 +971,16 @@ function zfrglob { # globbing characters, and if extendedglob is set and we are # using zsh for the actual pattern matching also look for # extendedglob characters. - if [[ $remlist != *[][*?]* && - ( -n $zfrglob || ! -o extendedglob || $remlist != *[(|)~#^]* ) ]]; then + if [[ $pat != *[][*?]* && + ( -n $zfrglob || ! -o extendedglob || $pat != *[(|)#^]* ) ]]; then return 0 fi + local tmpf={$TMPPREFIX}zfrglob$$ if [[ $zfrglob != '' ]]; then - eval "$1=(\$(zftp ls \"$pat\" 2>/dev/null))" + zftp ls "$pat" >$tmpf 2>/dev/null + eval "$1=(\$(<\$tmpf))" + rm -f $tmpf else if [[ $ZFTP_SYSTEM = UNIX* && $pat = */* ]]; then # not the current directory and we know how to handle paths @@ -781,12 +991,18 @@ function zfrglob { dir=/ fi nondir=${pat##*/} - files=($(zftp ls "$dir" 2>/dev/null)) + zftp ls "$dir" 2>/dev/null >$tmpf + files=($(<$tmpf)) + files=(${files:t}) + rm -f $tmpf else # we just have to do an ls and hope that's right nondir=$pat if (( $#zftp_fcache == 0 )); then - zftp_fcache=($(zftp ls)) + # Why does `zftp_fcache=($(zftp ls))' sometimes not work? + zftp ls >$tmpf + zftp_fcache=($(<$tmpf)) + rm -f $tmpf fi files=($zftp_fcache) fi @@ -848,7 +1064,7 @@ function zfrtime { function zfstat { # Give a zftp status report using local variables. - # With option -v, connect the remote host and ask it what it + # With option -v, connect to the remote host and ask it what it # thinks the status is. setopt localoptions unset @@ -874,10 +1090,6 @@ function zfstat { shift done - # hack: in case the status from a subshell process hasn't been - # fixed yet - zftp type >&/dev/null - if [[ -n $ZFTP_HOST ]]; then print "Host:\t\t$ZFTP_HOST" print "IP:\t\t$ZFTP_IP" @@ -907,12 +1119,14 @@ function zfstat { fi else print "Not connected." + [[ -n $zflastsession ]] && print "Last session:\t$zflastsession" stat=1 fi # things which may be set even if not connected: [[ -n $ZFTP_REPLY ]] && print "Last reply:\t$ZFTP_REPLY" print "Verbosity:\t$ZFTP_VERBOSE" + print "Timeout:\t$ZFTP_TMOUT" print -n "Preferences:\t" for (( i = 1; i <= ${#ZFTP_PREFS}; i++ )); do case $ZFTP_PREFS[$i] in @@ -928,6 +1142,7 @@ function zfstat { print if [[ -n $ZFTP_HOST && $verbose = 1 ]]; then + zfautocheck -d print "Status of remote server:" # make sure we print the reply local ZFTP_VERBOSE=045 @@ -939,8 +1154,6 @@ function zfstat { function zftp_chpwd { # You may want to alter chpwd to call this when $ZFTP_USER is set. - # If so, call it with a non-zero first argument so it doesn't - # print the new FTP directory. # Cancel the filename cache for the current directory. zftp_fcache=() @@ -948,26 +1161,26 @@ function zftp_chpwd { # As this function is called when we close the connection, this # is the only place we need to do these two things. [[ -n $zfcurdir && -f $zfcurdir ]] && rm -f $zfcurdir + zfotherargs= if [[ -z $ZFTP_USER ]]; then # last call, after an FTP logout # delete the non-current cached directory [[ -n $zfotherdir && -f $zfotherdir ]] && rm -f $zfotherdir - zfotherargs= - # don't keep zflastdir between opens + # don't keep zflastdir between opens (do keep zflastsession) zflastdir= # return the display to standard # uncomment the following line if you have a chpwd which shows directories - # chpwd + chpwd else - [[ -z $zflastdir ]] && zflastdir=$ZFTP_PWD + [[ -n $ZFTP_PWD ]] && zflastdir=$ZFTP_PWD + zflastsession="$ZFTP_HOST:$ZFTP_PWD" local args if [[ -t 1 && -t 2 ]]; then - local str="$ZFTP_HOST:$ZFTP_PWD" - [[ -z $1 ]] && print $str + local str=$zflastsession [[ ${#str} -lt 70 ]] && str="%m: %~ $str" case $TERM in sun-cmd) print -n -P "\033]l$str\033\\" @@ -999,10 +1212,14 @@ function zftp_progress { } function zftype { - local type + local type zftmp=${TMPPREFIX}zftype$$ + + zfautocheck -d if (( $# == 0 )); then - type=$(zftp type) + zftp type >$zftmp + type=$(<$zftmp) + rm -f $zftmp if [[ $type = I ]]; then print "Current type is image (binary)" return 0 @@ -1054,8 +1271,9 @@ function zfuget { setopt localoptions unsetopt ksharrays shwordsplit - local loc rem stat=0 locstats remstats doit tmpfile=${TMPPREFIX}zfuget$$ + local loc rem locstats remstats doit tmpfile=${TMPPREFIX}zfuget$$ local rstat remlist verbose optlist opt bad i silent nglob time + integer stat do_close zfuget_print_time() { local tim=$1 @@ -1097,6 +1315,8 @@ function zfuget { [[ -n $bad ]] && return 1 + zfautocheck + for remlist in $*; do # zfcd directory hack to put the front back to ~ if [[ $remlist == $HOME || $remlist == $HOME/* ]]; then @@ -1166,6 +1386,9 @@ function zfuget { done fi done + + (( do_close )) && zfclose + return $stat } @@ -1183,8 +1406,9 @@ function zfuput { setopt localoptions unsetopt ksharrays shwordsplit - local loc rem stat=0 locstats remstats doit tmpfile=${TMPPREFIX}zfuput$$ + local loc rem locstats remstats doit tmpfile=${TMPPREFIX}zfuput$$ local rstat verbose optlist opt bad i silent + integer stat do_close zfuput_print_time() { local tim=$1 @@ -1222,6 +1446,8 @@ function zfuput { [[ -n $bad ]] && return 1 + zfautocheck + if [[ $ZFTP_VERBOSE = *5* ]]; then # should we turn it off locally? print "Messages with code 550 are harmless." >&2 @@ -1277,5 +1503,8 @@ function zfuput { zftp put $rem <$loc || stat=$? fi done + + (( do_close )) && zfclose + return $stat } diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index d71fa642f..413dd8f43 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -554,7 +554,7 @@ zfgetline(char *ln, int lnsize, int tmout) if (setjmp(zfalrmbuf)) { alarm(0); zwarnnam("zftp", "timeout getting response", NULL, 0); - return 5; + return 6; } zfalarm(tmout); @@ -676,7 +676,7 @@ zfgetmsg() int stopit, printing = 0, tmout; if (zcfd == -1) - return 5; + return 6; if (!(verbose = getsparam("ZFTP_VERBOSE"))) verbose = ""; zsfree(lastmsg); @@ -763,7 +763,7 @@ zfgetmsg() zfclose(); /* unexpected, so tell user */ zwarnnam("zftp", "remote server has closed connection", NULL, 0); - return 6; /* pretend it failed, because it did */ + return 6; } if (lastcode == 530) { /* user not logged in */ @@ -802,20 +802,21 @@ zfsendcmd(char *cmd) int ret, tmout; if (zcfd == -1) - return 5; + return 6; tmout = getiparam("ZFTP_TMOUT"); if (setjmp(zfalrmbuf)) { alarm(0); zwarnnam("zftp", "timeout sending message", NULL, 0); - return 5; + return 6; } zfalarm(tmout); ret = write(zcfd, cmd, strlen(cmd)); alarm(0); if (ret <= 0) { - zwarnnam("zftp send", "failed sending control message", NULL, 0); - return 5; /* FTP status code */ + zwarnnam("zftp send", "failure sending control message: %e", + NULL, errno); + return 6; } return zfgetmsg(); @@ -1023,11 +1024,11 @@ zfgetdata(char *name, char *rest, char *cmd, int getsize) /* accept the connection */ len = sizeof(zdsock); newfd = zfmovefd(accept(zdfd, (struct sockaddr *)&zdsock, &len)); + if (newfd < 0) + zwarnnam(name, "unable to accept data: %e", NULL, errno); zfclosedata(); - if (newfd < 0) { - zwarnnam(name, "unable to accept data.", NULL, 0); + if (newfd < 0) return 1; - } zdfd = newfd; /* this is now the actual data fd */ } else { /* @@ -1270,7 +1271,7 @@ zfread_block(int fd, char *bf, size_t sz, int tmout) n = zfread(fd, (char *)&hdr, sizeof(hdr), tmout); } while (n < 0 && errno == EINTR); if (n != 3 && !zfdrrrring) { - zwarnnam("zftp", "failed to read FTP block header", NULL, 0); + zwarnnam("zftp", "failure reading FTP block header", NULL, 0); return n; } /* size is stored in network byte order */ @@ -1324,7 +1325,7 @@ zfwrite_block(int fd, char *bf, size_t sz, int tmout) n = zfwrite(fd, (char *)&hdr, sizeof(hdr), tmout); } while (n < 0 && errno == EINTR); if (n != 3 && !zfdrrrring) { - zwarnnam("zftp", "failed to write FTP block header", NULL, 0); + zwarnnam("zftp", "failure writing FTP block header", NULL, 0); return n; } bfptr = bf; diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 124ae454a..0d5419817 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -261,15 +261,19 @@ struct cline { int llen; /* length of line */ char *word; /* prefered string to insert */ int wlen; /* length of word */ - Cmatcher matcher; /* which matcher was used */ int flags; /* see CLF_* below */ + Cline prefix; /* prefix we've build for new parts */ + Cline suffix; /* suffix we've build for new parts */ }; -#define CLF_END 1 -#define CLF_MID 2 -#define CLF_MISS 4 -#define CLF_DIFF 8 -#define CLF_SUF 16 +#define CLF_END 1 +#define CLF_MID 2 +#define CLF_MISS 4 +#define CLF_DIFF 8 +#define CLF_SUF 16 +#define CLF_NEW 32 +#define CLF_VAR 64 +#define CLF_JOIN 128 /* Flags for makecomplist*(). Things not to do. */ diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index cec71fc5b..e3b778508 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1685,6 +1685,10 @@ bin_compadd(char *name, char **argv, char *ops, int func) return 1; } for (; *argv && **argv == '-'; argv++) { + if (!(*argv)[1]) { + argv++; + break; + } for (p = *argv + 1; *p; p++) { sp = NULL; dm = 0; @@ -1945,9 +1949,9 @@ restrict_range(int b, int e) pparams = p; zsfree(compcontext); if ((compcurrent -= b + 1)) - compcontext = ztrdup("arg"); + compcontext = ztrdup("argument"); else - compcontext = ztrdup("cmd"); + compcontext = ztrdup("command"); } /**/ diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index f1078ef11..af78e1c06 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -98,12 +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, brbsl 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, brpcs, brscs; +static int brpl, brsl, brbsl, brpcs, brscs; /* The list of matches. fmatches contains the matches we first ignore * * because of fignore. */ @@ -212,6 +212,12 @@ static LinkList ccstack; static Cmlist mstack; +/* The completion matchers used when building new stuff for the line. */ + +static struct cmlist bmstack0, bmstack1; +static Cmlist bmstack; +static int had_lm; + /* A list with references to all matcher we used. */ static LinkList matchers; @@ -287,6 +293,12 @@ enum { COMP_COMPLETE, static int lastambig; +/* Non-zero if the string on the line came from a previous completion. * + * This is used to detect if the string should be taken as an exact * + * match (see do_ambiguous()). */ + +static int fromcomp; + /**/ void completecall(void) @@ -1356,6 +1368,7 @@ get_comp_string(void) if (*p == Outbrace) chuck(p); brsl = strlen(s) - (p - s); + brbsl = p - s; brend[sl] = '\0'; } /* we are still waiting for an outbrace and maybe commas */ @@ -1469,7 +1482,9 @@ addtoword(char **rwp, int *rwlenp, char *nw, { int al, rwlen = *rwlenp, nl; char *as, *rw = *rwp; - + + /* Get the string and length to insert: either from the line + * or from the match. */ if (m && (m->flags & CMF_LINE)) { al = m->llen; as = l; @@ -1477,6 +1492,7 @@ addtoword(char **rwp, int *rwlenp, char *nw, al = wl; as = w; } + /* Allocate some more space if needed. */ if (!rwlen || (nl = al + (nw - rw)) >= rwlen) { char *np; @@ -1496,6 +1512,7 @@ addtoword(char **rwp, int *rwlenp, char *nw, *rwp = rw = np; rwlen = nl; } + /* Copy it in the buffer. */ if (prep) { memmove(rw + al, rw, rwlen - al); memcpy(rw, as, al); @@ -1506,13 +1523,15 @@ addtoword(char **rwp, int *rwlenp, char *nw, return nw + al; } -/* This get a new Cline structure. */ +/* This returns a new Cline structure. */ static Cline -getcline(char *l, int ll, char *w, int wl, Cmatcher m, int fl) +getcline(char *l, int ll, char *w, int wl, int fl) { Cline r; + /* Preverably take it from the buffer list (freecl), if there + * is none, allocate a new one. */ if ((r = freecl)) freecl = r->next; else @@ -1523,19 +1542,20 @@ getcline(char *l, int ll, char *w, int wl, Cmatcher m, int fl) r->llen = ll; r->word = w; r->wlen = wl; - r->matcher = m; r->flags = fl; + r->prefix = r->suffix = NULL; return r; } -/* This add a Cline structure with the given parameters. */ +/* This adds a Cline structure with the given parameters to the list we + * are building during matching. */ static void addtocline(Cline *retp, Cline *lrp, char *l, int ll, char *w, int wl, Cmatcher m, int fl) { - Cline ln = getcline(l, ll, w, wl, m, fl); + Cline ln = getcline(l, ll, w, wl, fl); if (m && (m->flags & CMF_LINE)) ln->word = NULL; @@ -1547,8 +1567,1140 @@ addtocline(Cline *retp, Cline *lrp, *lrp = ln; } +/* When building up cline lists that are to be inserted at the end of the + * string or on the left hand side in the middle, we do this separately for + * multiple runs of characters separated by the anchors of `*' match patterns. + * This function builds such a list from the given string. */ + +static Cline +end_list(int len, char *str) +{ + Cline ret = NULL, *q = &ret; + Cmlist ms; + Cmatcher mp; + int t; + char *p = str; + + while (len) { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (mp->flags == CMF_RIGHT && mp->wlen == -1 && + !mp->llen && len >= mp->ralen && mp->ralen && + pattern_match(mp->right, str, NULL, NULL)) { + /* This is one of those patterns, so add a cline struct. + * We store the anchor string in the line and the contents + * (i.e. the strings between the anchors) in the word field. */ + *q = getcline(str, mp->ralen, p, str - p, 0); + q = &((*q)->next); + str += mp->ralen; + len -= mp->ralen; + p = str; + t = 1; + } + } + } + if (!t) { + str++; + len--; + } + } + /* This is the cline struct for the remaining string at the end. */ + if (p != str) { + *q = getcline("", 0, p, str - p, 0); + q = &((*q)->next); + } + *q = NULL; + + return ret; +} + +/* This builds a string that may be put on the line that fully matches the + * given strings. The return value is NULL if no such string could be built + * or that string, allocated on the heap. + * All this is a lot like the procedure in bld_new_pfx(), only slightly + * simpler, see that function for more comments. */ + +static char * +join_strs(int la, char *sa, int lb, char *sb) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + static char *line = NULL; + static int llen = 0; + static char *rs = NULL; + static int rl = 0; + + Cmlist ms; + Cmatcher mp; + int t, bl, rr = rl; + char *rp = rs; + + /* This is a buffer for the characters to use for pairs of correspondence + * classes. */ + if (la + 1 > ealen || lb + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = (la > lb ? la : lb) + 20); + } + while (la && lb) { + if (*sa != *sb) { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= la && mp->wlen <= lb) { + if (pattern_match(mp->word, sa, NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + lb, sb, ea))) { + line[mp->llen] = '\0'; + if (rr <= mp->llen) { + char *or = rs; + + rs = realloc(rs, (rl += 20)); + rr += 20; + rp += rs - or; + } + memcpy(rp, line, mp->llen); + rp += mp->llen; + rr -= mp->llen; + sa += mp->wlen; + sb += bl; + la -= mp->wlen; + lb -= bl; + t = 1; + } + } else if (pattern_match(mp->word, sb, NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + la, sa, ea))) { + line[mp->llen] = '\0'; + if (rr <= mp->llen) { + char *or = rs; + + rs = realloc(rs, (rl += 20)); + rr += 20; + rp += rs - or; + } + memcpy(rp, line, mp->llen); + rp += mp->llen; + rr -= mp->llen; + sa += bl; + sb += mp->wlen; + la -= bl; + lb -= mp->wlen; + t = 1; + } + } + } + } + } + if (!t) + break; + } else { + if (rr <= mp->llen) { + char *or = rs; + + rs = realloc(rs, (rl += 20)); + rr += 20; + rp += rs - or; + } + *rp++ = *sa; + rr--; + sa++; + sb++; + la--; + lb--; + } + } + if (la || lb) + return NULL; + + *rp = '\0'; + + return dupstring(rs); +} + +/* This gets two cline lists with separated runs and joins the corresponding + * elements in these lists. In olp and nlp we return the lengths of the matched + * portions in o and n respectively. As always the list in o is the one that + * contains the joined result after this function. */ + +static Cline +join_ends(Cline o, Cline n, int *olp, int *nlp) +{ + Cline ret = o; + int mol, mnl, smol = 0, smnl = 0; + char *j; + + while (o && n) { + if (o->llen == n->llen && !strncmp(o->line, n->line, o->llen)) { + /* The anchors are the same, so join the contents. */ + bld_pfx(o, n, &mol, &mnl); + smol += mol + o->llen; + smnl += mnl + n->llen; + } else if (!(o->flags & CLF_JOIN) && + (j = join_strs(o->llen, o->line, n->llen, n->line))) { + /* We could build a string matching both anchors, so use that + * and mark the cline so that we don't try to join it again. */ + o->flags |= CLF_JOIN; + o->llen = strlen(j); + o->line = j; + bld_pfx(o, n, &mol, &mnl); + smol += mol + o->llen; + smnl += mnl + n->llen; + } else { + /* Different anchor strings, so shorten the list. */ + bld_pfx(o, n, &mol, &mnl); + smol += mol; + smnl += mnl; + o->flags |= CLF_MISS; + o->next = NULL; + o->llen = 0; + if (olp) + *olp = smol; + if (nlp) + *nlp = smnl; + return ret; + } + /* If we reached the end of the new list but not the end of the old + * list, we mark the old list (saying that characters are missing here). */ + if (!(n = n->next) && o->next) + o->flags |= CLF_MISS; + o = o->next; + } + if (o) { + /* Shorten the list if we haven't reached the end. */ + if (n) + o->flags |= CLF_MISS; + o->next = NULL; + o->llen = 0; + } + if (olp) + *olp = smol; + if (nlp) + *nlp = smnl; + return ret; +} + +/* This builds all the possible line patterns for the pattern pat in the + * buffer line. Initially line is the same as lp, but during recursive + * calls lp is incremented for storing successive characters. Whenever + * a full possible string is build, we test if this line matches the + * string given by wlen and word. The last argument contains the characters + * to use for the correspondence classes, it was filled by a call to + * pattern_match() in the calling function. + * The return value is the length of the string matched in the word, it + * is zero if we couldn't build a line that matches the word. */ + +/**/ +static int +bld_line_pfx(Cpattern pat, char *line, char *lp, + int wlen, char *word, unsigned char *in) +{ + if (pat) { + /* Still working on the pattern. */ + + int i, l; + unsigned char c = 0; + + /* Get the number of the character for a correspondence class + * if it has a correxponding class. */ + if (pat->equiv) + if ((c = *in)) + in++; + + /* Walk through the table in the pattern and try the characters + * that may appear in the current position. */ + for (i = 0; i < 256; i++) + if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) { + *lp = i; + /* We stored the character, now call ourselves to build + * the rest. */ + if ((l = bld_line_pfx(pat->next, line, lp + 1, + wlen, word, in))) + return l; + } + } else { + /* We reached the end, i.e. the line string is fully build, now + * see if it matches the given word. */ + static unsigned char *ea = NULL; + static int ealen = 0; + + Cmlist ms; + Cmatcher mp; + int l = lp - line, t, rl = 0; + + /* Quick test if the strings are exactly the same. */ + if (l == wlen && !strncmp(line, word, l)) + return l; + + /* We need another buffer for correspondence classes. */ + if (l + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = l + 20); + } + /* We loop through the whole line string built. */ + while (l && wlen) { + if (*word == *line) { + /* The same character in both strings, skip over. */ + line++; + word++; + l--; + wlen--; + rl++; + } else { + t = 0; + for (ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && + pattern_match(mp->line, line, NULL, ea) && + pattern_match(mp->word, word, ea, NULL)) { + /* Both the line and the word pattern matched, + * now skip over the matched portions. */ + line += mp->llen; + word += mp->wlen; + l -= mp->llen; + wlen -= mp->wlen; + rl += mp->wlen; + t = 1; + } + } + } + if (!t) + /* Didn't match, give up. */ + return 0; + } + } + if (!l) + /* Unmatched portion in the line built, return no-match. */ + return rl; + } + return 0; +} + +/* This builds a list of cline structs describing a string to insert in + * the line in a place where we didn't had something on the line when + * completion was invoked. This is called after we get the second match + * so we have two strings to start with given by (ol,ow) and (nl,nw) and + * the cline list returned describes a string that matches both of these + * strings. + * In olp and nlp we return the number of characters in ow and nw that + * are not matched by the cline list returned. Especially this means that + * they are non-zero if there are such unmatched portions. + * In lp we return a pointer to the last cline struct created. */ + +static Cline +bld_new_pfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + static char *line = NULL; + static int llen = 0; + + Cmlist ms; + Cmatcher mp; + Cline ret = NULL, l = NULL, n; + char *p = ow; + int t, bl; + + /* This is a buffer for the characters to use for pairs of correspondence + * classes. */ + if (ol + 1 > ealen || nl + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = (ol > nl ? ol : nl) + 20); + } + /* Loop through the strings. */ + while (ol && nl) { + if (*ow != *nw) { + /* Not the same character, use the patterns. */ + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + /* We use only those patterns that match a non-empty + * string in both the line and the word, that match + * strings no longer than the string we still have + * to compare, that don't have anchors, and that don't + * use the line strings for insertion. */ + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= ol && mp->wlen <= nl) { + /* Try the pattern only if the word-pattern matches + * one of the strings. */ + if (pattern_match(mp->word, ow, NULL, ea)) { + /* Make the buffer where we build the possible + * line patterns big enough. */ + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + /* Then build all the possible lines and see + * if one of them matches the othe string. */ + if ((bl = bld_line_pfx(mp->line, line, line, + nl, nw, ea))) { + /* Yep, one of the lines matched the other + * string. */ + if (p != ow) { + /* There is still a substring that is + * the same in both strings, so add + * a cline for it. */ + char sav = *ow; + + *ow = '\0'; + n = getcline(NULL, 0, dupstring(p), + ow - p, 0); + *ow = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + /* Then we add the line string built. */ + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow += mp->wlen; + nw += bl; + ol -= mp->wlen; + nl -= bl; + p = ow; + t = 1; + } + } else if (pattern_match(mp->word, nw, NULL, ea)) { + /* Now do the same for the other string. */ + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_pfx(mp->line, line, line, + ol, ow, ea))) { + if (p != ow) { + char sav = *ow; + + *ow = '\0'; + n = getcline(NULL, 0, dupstring(p), + ow - p, 0); + *ow = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow += bl; + nw += mp->wlen; + ol -= bl; + nl -= mp->wlen; + p = ow; + t = 1; + } + } + } + } + } + if (!t) + /* No pattern matched, so give up. */ + break; + } else { + /* Same character, skip over. */ + ow++; + nw++; + ol--; + nl--; + } + } + if (p != ow) { + /* There is a equal substring in both strings, build a cline + * for it. */ + char sav = *ow; + + *ow = '\0'; + n = getcline(NULL, 0, dupstring(p), ow - p, 0); + *ow = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + if (l) + l->next = NULL; + else + ret = NULL; + + if (olp) + *olp = ol; + if (nlp) + *nlp = nl; + if (lp) + *lp = l; + + return ret; +} + +/* Given a cline list for an unmatched part of the string to insert in the + * line and a new match substring, modify the cline list so that it also + * matches this string. The cline list is shortened in the place where + * we can't build a cline matching the new string. + * However, the resulting cline list is returned. The string is described + * by len and word. In missp we return non-zero if the cline list returned + * had to be shortened (and hence doesn't fully match the strings it was + * built from anymore) or if it doesn't fully match the given string. + * This function checks the string left to right and thus is to be used + * for strings where we want a common prefix. */ + +static Cline +join_new_pfx(Cline line, int len, char *word, int *missp) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + + Cline ret = NULL, l = NULL, next; + int miss = 0; + + /* Walk through the list and the string. */ + while (line && len) { + next = line->next; + /* The line element is used in those places where a new line + * string was built. */ + if (line->line) { + Cmlist ms; + Cmatcher mp; + int ll = line->llen, t; + char *p = line->line; + + /* Make the buffer for the correspondence classes big enough. */ + if (line->llen + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = line->llen + 20); + } + /* Check if the line string from the cline list matches the + * new string. */ + while (ll && len) { + if (*p == *word) { + p++; + word++; + ll--; + len--; + } else { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= len && mp->llen <= len && + pattern_match(mp->word, word, NULL, ea) && + pattern_match(mp->line, p, ea, NULL)) { + /* We have a matched substring, skip over. */ + p += mp->llen; + word += mp->wlen; + ll -= mp->llen; + len -= mp->wlen; + t = 1; + } + } + } + if (!t) + /* No match, give up. */ + break; + } + } + if (p == line->line) { + /* We couldn't match any character from the string in the + * cline, so shorten the list and don't even keep this + * struct. */ + miss = 1; + len = 0; + } else { + /* At least the beginning of the cline string can be used. */ + if (ll) { + /* But there is a portion of the string that can't be + * used, so we have to shorten the list. */ + miss = 1; + *p = '\0'; + line->llen -= ll; + len = 0; + } + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + } + } else { + /* The cline doesn't have a string built by reverse matching, + * so we have to work on the original substring in the cline + * and the new string. */ + if (line->wlen == len && strncmp(line->word, word, len)) { + /* They are equal, accept and return. If there was + * another element in the list, shorten the list. */ + if (next) + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else { + char sav = word[len]; + + /* Check if one is the prefix of the other one. */ + word[len] = '\0'; + if (strpfx(word, line->word)) { + word[len] = sav; + + line->word[len] = '\0'; + line->wlen = len; + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else if (strpfx(line->word, word)) { + word[len] = sav; + + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else { + /* Not the same and no prefix, so we try to build a + * new line string matching the string in the cline + * and the new string. */ + int mol, mnl; + Cline sl, send; + + word[len] = sav; + + if ((sl = bld_new_pfx(line->wlen, line->word, + len, word, &mol, &mnl, &send))) { + /* We could build such a string, use it in the + * cline structure. */ + if (l) + l->next = sl; + else + ret = sl; + if (!mol) { + send->next = next; + word += len - mnl; + len = mnl; + } else + len = 0; + l = send; + } else + len = 0; + } + } + } + line = next; + } + *missp = (line || len || miss); + + return ret; +} + +/* This function gets two cline structs for which we want to build a + * common prefix to put on the line. The result is placed in the cline + * struct given as first argument. + * In olp and nlp we return the matched lengths for o and n, respectively + * (but this is only guaranteed to give correct results if this is the + * first call for the given o and n). */ + +/**/ +static void +bld_pfx(Cline o, Cline n, int *olp, int *nlp) +{ + if (o->flags & CLF_NEW) { + if (o->flags & (CLF_END | CLF_MID)) + /* We split the suffix in the middle and at the end into + * separate runs. */ + o->prefix = join_ends(o->prefix, end_list(n->wlen, n->word), + NULL, NULL); + else { + /* This flag is set if we already built a cline list for such + * a prefix. We join it with the string from the other cline + * struct. */ + int miss; + + o->prefix = join_new_pfx(o->prefix, n->wlen, n->word, &miss); + if (miss) + o->flags |= CLF_MISS; + } + } else if (o->flags & (CLF_END | CLF_MID)) { + o->flags |= CLF_NEW; + o->prefix = join_ends(end_list(o->wlen, o->word), + end_list(n->wlen, n->word), olp, nlp); + } else if (o->wlen && n->wlen) { + /* We haven't built a cline list for it yet. */ + char so = o->word[o->wlen], sn = n->word[n->wlen]; + char *new = o->word; + int newl = o->wlen, mol, mnl; + + /* If one of the strings is a prefix of the other one, just keep + * that prefix. */ + o->word[o->wlen] = n->word[n->wlen] = '\0'; + if (strpfx(n->word, o->word)) { + new = dupstring(n->word); + newl = n->wlen; + if (olp) + *olp = *nlp = n->wlen; + } else if (strpfx(o->word, n->word)) { + if (olp) + *olp = *nlp = o->wlen; + } else { + /* Otherwise build a cline list describing a string that + * matches both strings from the original cline structs + * and thus can be put in the command line to represent + * them. This cline list is stored in o. */ + o->flags |= CLF_NEW; + o->prefix = bld_new_pfx(o->wlen, o->word, n->wlen, n->word, + &mol, &mnl, NULL); + newl = 0; + new = ""; + if (mol || mnl) + o->flags |= CLF_MISS; + if (olp) { + *olp = o->wlen - mol; + *nlp = n->wlen - mnl; + } + } + o->word[o->wlen] = so; + n->word[n->wlen] = sn; + + o->word = new; + o->wlen = newl; + + if (!o->prefix && n->wlen != o->wlen) + o->flags |= CLF_MISS; + } +} + +/* The following function are like their counterparts above, only for + * the other direction. */ + +static int +bld_line_sfx(Cpattern pat, char *line, char *lp, + int wlen, char *word, unsigned char *in) +{ + if (pat) { + int i, l; + unsigned char c = 0; + + if (pat->equiv) + if ((c = *in)) + in++; + + for (i = 0; i < 256; i++) + if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) { + *lp = i; + if ((l = bld_line_pfx(pat->next, line, lp + 1, + wlen, word, in))) + return l; + } + } else { + static unsigned char *ea = NULL; + static int ealen = 0; + + Cmlist ms; + Cmatcher mp; + int l = lp - line, t, rl = 0; + + if (l == wlen && !strncmp(line, word, l)) + return l; + + line = lp; + word += wlen; + + if (l + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = l + 20); + } + while (l && wlen) { + if (word[-1] == line[-1]) { + line--; + word--; + l--; + wlen--; + rl++; + } else { + t = 0; + for (ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && + pattern_match(mp->line, line - mp->llen, + NULL, ea) && + pattern_match(mp->word, word - mp->wlen, + ea, NULL)) { + line -= mp->llen; + word -= mp->wlen; + l -= mp->llen; + wlen -= mp->wlen; + rl += mp->wlen; + t = 1; + } + } + } + if (!t) + return 0; + } + } + if (!l) + return rl; + } + return 0; +} + +static Cline +bld_new_sfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + static char *line = NULL; + static int llen = 0; + + Cmlist ms; + Cmatcher mp; + Cline ret = NULL, l = NULL, n; + char *p; + int t, bl; + + ow += ol; + nw += nl; + p = ow; + + if (ol + 1 > ealen || nl + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc((ealen = (ol > nl ? ol : nl) + 20)); + } + while (ol && nl) { + if (ow[-1] != nw[-1]) { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= ol && mp->wlen <= nl) { + if (pattern_match(mp->word, ow - mp->wlen, + NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_sfx(mp->line, line, line, + nl, nw, ea))) { + if (p != ow) { + char sav = *p; + + *p = '\0'; + n = getcline(NULL, 0, dupstring(ow), + p - ow, 0); + *p = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow -= mp->wlen; + nw -= bl; + ol -= mp->wlen; + nl -= bl; + p = ow; + t = 1; + } + } else if (pattern_match(mp->word, nw - mp->wlen, + NULL, ea)) { + if (mp->llen + 1 > llen) { + if (llen) + zfree(line, llen); + line = (char *) zalloc(llen = mp->llen + 20); + } + if ((bl = bld_line_sfx(mp->line, line, line, + ol, ow, ea))) { + if (p != ow) { + char sav = *p; + + *p = '\0'; + n = getcline(NULL, 0, dupstring(ow), + p - ow, 0); + *p = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + line[mp->llen] = '\0'; + n = getcline(dupstring(line), mp->llen, + NULL, 0, CLF_DIFF); + if (l) + l->next = n; + else + ret = n; + l = n; + ow -= bl; + nw -= mp->wlen; + ol -= bl; + nl -= mp->wlen; + p = ow; + t = 1; + } + } + } + } + } + if (!t) + break; + } else { + ow--; + nw--; + ol--; + nl--; + } + } + if (p != ow) { + char sav = *p; + + *p = '\0'; + n = getcline(NULL, 0, dupstring(ow), p - ow, 0); + *p = sav; + if (l) + l->next = n; + else + ret = n; + l = n; + } + if (l) + l->next = NULL; + else + ret = NULL; + + if (olp) + *olp = ol; + if (nlp) + *nlp = nl; + if (lp) + *lp = l; + + return ret; +} + +static Cline +join_new_sfx(Cline line, int len, char *word, int *missp) +{ + static unsigned char *ea = NULL; + static int ealen = 0; + + Cline ret = NULL, l = NULL, next; + int miss = 0, ind = 0; + + word += len; + + while (line && len) { + next = line->next; + if (line->line) { + Cmlist ms; + Cmatcher mp; + int ll = line->llen, t; + char *p = line->line + ll; + + if (line->llen + 1 > ealen) { + if (ealen) + zfree(ea, ealen); + ea = (unsigned char *) zalloc(ealen = line->llen + 20); + } + while (ll && len) { + if (p[-1] == word[-1]) { + p--; + word--; + ll--; + len--; + ind++; + } else { + for (t = 0, ms = bmstack; ms && !t; ms = ms->next) { + for (mp = ms->matcher; mp && !t; mp = mp->next) { + if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + mp->wlen <= len && mp->llen <= len && + pattern_match(mp->word, word - mp->wlen, + NULL, ea) && + pattern_match(mp->line, p - mp->llen, + ea, NULL)) { + p -= mp->llen; + word -= mp->wlen; + ll -= mp->llen; + len -= mp->wlen; + ind += mp->wlen; + t = 1; + } + } + } + if (!t) + break; + } + } + if (p == line->line + line->llen) { + miss = 1; + len = 0; + } else { + if (ll) { + miss = 1; + line->line = p; + line->llen -= ll; + len = 0; + } + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + } + } else { + if (line->wlen == len && strncmp(line->word, word - len, len)) { + if (next) + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else { + char sav = word[ind]; + + word[ind] = '\0'; + if (strpfx(word - len, line->word)) { + word[ind] = sav; + + line->word += line->wlen - len; + line->wlen = ind; + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else if (strpfx(line->word, word - len)) { + word[ind] = sav; + + miss = 1; + line->next = NULL; + if (l) + l->next = line; + else + ret = line; + len = 0; + } else { + int mol, mnl; + Cline sl, send; + + word[len] = sav; + + if ((sl = bld_new_sfx(line->wlen, line->word, + len, word - len, + &mol, &mnl, &send))) { + if (l) + l->next = sl; + else + ret = sl; + if (!mol) { + send->next = next; + word -= len - mnl; + len = mnl; + } else + len = 0; + l = send; + } else + len = 0; + } + } + } + line = next; + } + *missp = (line || len || miss); + + return ret; +} + +/**/ +static void +bld_sfx(Cline o, Cline n) +{ + if (o->flags & CLF_NEW) { + int miss; + + o->suffix = join_new_sfx(o->suffix, n->wlen, n->word, &miss); + if (miss) + o->flags |= CLF_MISS; + } else { + char so = o->word[o->wlen], sn = n->word[n->wlen]; + char *new = o->word; + int newl = o->wlen, mol, mnl; + + o->word[o->wlen] = n->word[n->wlen] = '\0'; + if (strpfx(n->word, o->word)) { + new = dupstring(n->word); + newl = n->wlen; + } else if (!strpfx(o->word, n->word)) { + o->flags |= CLF_NEW; + o->suffix = bld_new_sfx(o->wlen, o->word, n->wlen, n->word, + &mol, &mnl, NULL); + newl = 0; + new = ""; + if (mol || mnl) + o->flags |= CLF_MISS; + } + o->word[o->wlen] = so; + n->word[n->wlen] = sn; + + o->word = new; + o->wlen = newl; + + if (!o->suffix && n->wlen != o->wlen) + o->flags |= CLF_MISS; + } +} + /* Joins two Cline lists, building the most specific line string * - * that is possible. */ + * that is possible and returns it. This is done by modifying the + * cline list given as the first argument. */ static Cline join_clines(Cline o, Cline n) @@ -1556,12 +2708,21 @@ join_clines(Cline o, Cline n) Cline oo = o; if (!o) + /* This is the first time this was called, so just return the + * the second list. In future calls we will get this list as + * the first argument. */ return n; else { Cline f = freecl, q, op = NULL; int ol, nl; + freecl = NULL; + while (o && n) { + /* CLF_MID is set in the cline struct where the prefix and the + * suffix from the line meet. If we have reached the cline + * for it in one of the lists, search the corresponding + * cline in the other list, removing all structs up to it. */ if (o->flags & CLF_MID) { while (n && !(n->flags & CLF_MID)) { q = n->next; @@ -1606,64 +2767,52 @@ join_clines(Cline o, Cline n) if (!o || !n) break; if (o->flags & CLF_MID) { - char so, sn, *os, *ns; - int ol = o->llen, l, lo, ln; - - so = o->line[o->llen]; - sn = n->line[n->llen]; - o->line[o->llen] = '\0'; - n->line[n->llen] = '\0'; - l = pfxlen(o->line, n->line); - if (l != o->llen) - o->flags |= CLF_MISS; - o->line[o->llen] = so; - n->line[n->llen] = sn; - o->llen = l; - if ((lo = o->wlen) < 0) { - os = o->line + l; - lo = ol - l; - } else - os = o->word; - ns = n->line + l; - ln = n->llen - l; - so = os[lo]; - sn = ns[ln]; - os[lo] = '\0'; - ns[ln] = '\0'; - l = sfxlen(os, ns); - os[lo] = so; - ns[ln] = sn; - o->word = os + lo - l; - o->wlen = l; + /* These are the structs in the middle, so simplify the + * prefix and the suffix in it to their longest common + * versions. */ + + char *os, *ns, *oss = o->line, *nss = n->line; + int ol, nl, mol, mnl, oll = o->llen, nll = n->llen; + + os = o->word; + ol = o->wlen; + ns = n->word; + nl = n->wlen; + + o->word = o->line; + o->wlen = o->llen; + n->word = n->line; + n->wlen = n->llen; + bld_pfx(o, n, &mol, &mnl); + o->line = o->word; + o->llen = o->wlen; + + o->word = os; + o->wlen = ol; + n->word = ns; + n->wlen = nl; + + if (o->wlen < 0) { + o->word = oss + mol; + o->wlen = oll - mol; + } + if (n->wlen < 0) { + n->word = nss + mnl; + n->wlen = nll - mnl; + } + bld_sfx(o, n); } else if (o->word) { if (n->word) { - if (o->matcher == n->matcher && - (o->flags & CLF_SUF) == (n->flags & CLF_SUF) && - ((o->flags & CLF_END) || o->matcher->wlen < 0)) { - char so = o->word[o->wlen]; - char sn = n->word[n->wlen]; - int l; - - o->word[o->wlen] = '\0'; - n->word[n->wlen] = '\0'; - - /* We have two chunks from `*' patterns. * + if (o->llen == n->llen && + (o->flags & CLF_VAR) && (n->flags & CLF_VAR) && + (o->flags & (CLF_END | CLF_SUF)) == + (n->flags & (CLF_END | CLF_SUF))) { + /* We have two chunks from `*' patterns, * reduce them to the common prefix/suffix. */ - if (o->flags & CLF_SUF) { - l = sfxlen(o->word, n->word); - o->word[o->wlen] = so; - n->word[n->wlen] = sn; - o->word += o->wlen - l; - } else { - l = pfxlen(o->word, n->word); - o->word[o->wlen] = so; - n->word[n->wlen] = sn; - o->word = dupstring(o->word); - o->word[l] = '\0'; - } - o->wlen = l; - if (l < o->wlen || l < n->wlen) - o->flags |= CLF_MISS; + if (o->flags & CLF_SUF) + bld_sfx(o, n); + else + bld_pfx(o, n, NULL, NULL); } else if (o->wlen == n->wlen) { /* Otherwise keep them if they are equal. */ if (strncmp(o->word, n->word, o->wlen)) { @@ -1694,6 +2843,8 @@ join_clines(Cline o, Cline n) o = o->next; } if (o) { + /* The old list has elements not matched by the second list + * we put all these elements in the free list. */ for (q = o; q->next; q = q->next); q->next = f; @@ -1717,7 +2868,8 @@ join_clines(Cline o, Cline n) return oo; } -/* This returns a Cline for the given string. */ +/* This returns a Cline for the given string. In lp we return a pointer to + * the last cline struct created. */ static Cline str_cline(char *s, int l, Cline *lp) @@ -1726,8 +2878,10 @@ str_cline(char *s, int l, Cline *lp) if (l < 0) l = strlen(s); + /* We build one struct per character, this makes joining it faster + * and easier. */ while (l) { - *p = n = getcline(s, 1, NULL, 0, NULL, 0); + *p = n = getcline(s, 1, NULL, 0, 0); p = &(n->next); s++; @@ -1739,7 +2893,8 @@ str_cline(char *s, int l, Cline *lp) return r; } -/* This reverts the order of the chunks. */ +/* This reverts the order of the elements of the given cline list and + * returns a pointer to the new head. */ static Cline revert_clines(Cline p) @@ -1755,7 +2910,7 @@ revert_clines(Cline p) return r; } -/* Prepend a string to a Cline. */ +/* Prepend a string to a Cline and return a pointer to the new cline list. */ static Cline prepend_cline(char *s, Cline l) @@ -1764,7 +2919,7 @@ prepend_cline(char *s, Cline l) while (l) { *p = n = getcline(l->line, l->llen, l->word, l->wlen, - l->matcher, l->flags); + l->flags); p = &(n->next); l = l->next; @@ -1772,8 +2927,8 @@ prepend_cline(char *s, Cline l) return r; } -/* This simplifies the Cline to match the given strings. The strings are: * - * the common prefix and its length, a string with the suffix at the end * +/* This simplifies the Cline to match the given strings. The strings are: + * the common prefix and its length, a string with the suffix at the end * and the suffix length. */ static void @@ -1784,6 +2939,8 @@ merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl) pll = strlen(p); if (s) { + /* If there is a suffix, get a pointer to the beginning of the + * common suffix. */ int i = strlen(s); if (ainfo->suflen < 10000) @@ -1800,44 +2957,36 @@ merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl) pll -= pl; + /* Now build a cline list from the string(s) and join it with the + * cline list we have built while testing possible matches. */ l = str_cline(p, pl, &n); q = &(n->next); if (!sl) - *q = getcline(NULL, 0, p + pl, pll, NULL, CLF_END); + *q = getcline(NULL, 0, p + pl, pll, CLF_END | CLF_VAR); else { - *q = n = getcline(p + pl, pll, s, sll, NULL, CLF_MID); + *q = n = getcline(p + pl, pll, s, sll, CLF_MID); n->next = str_cline(s + sll, sl, NULL); } join_clines(lc, l); } -/* This inserts the Cline built into the command line. */ +/* This inserts the Cline built into the command line. The last two + * arguments are the relative positions where the begining and the + * end of the brace expansion strings have to be re-inserted. */ static void inst_cline(Cline l, int pl, int sl) { int m = -1, d = -1, b = -1, hb = 0, i = 0; - Cline p = l; - - if (brend && *brend) { - while (p) { - if (l->flags & CLF_MID) { - i += l->llen; - if (l->wlen >= 0) - i += l->wlen; - } else - i += l->llen; - p = p->next; - } - sl = i - (brsl + sl) - 1; - } - else - sl = -1; + /* Adjust the position of the braces. */ pl += brpl; + sl += brbsl; i = cs - wb; + + /* Insert the brace strings now? */ if (pl >= 0 && i >= pl && brbeg && *brbeg) { inststrlen(brbeg, 1, -1); pl = -1; @@ -1848,37 +2997,140 @@ inst_cline(Cline l, int pl, int sl) sl = -1; hb = 1; } + /* Walk the list. */ while (l) { + /* If this cline describes a suffix where something is missing + * for some matches, remember the position. */ if (m < 0 && (l->flags & (CLF_MISS | CLF_SUF)) == (CLF_MISS | CLF_SUF)) m = cs; - if (l->flags & CLF_MID) { - inststrlen(l->line, 1, l->llen); - if (b < 0) + + if (l->prefix) { + Cline sl = l->prefix, ssl; + int hm; + + if (l->flags & (CLF_END | CLF_MID)) { + /* At the end and in the middle the suffix we have + * separate runs. */ + for (; sl; sl = sl->next) { + hm = 0; + if (sl->prefix) { + for (ssl = sl->prefix; ssl; ssl = ssl->next) { + if (ssl->line) + /* line for a string derived from applying + * the matchers the other way round. */ + inststrlen(ssl->line, 1, ssl->llen); + else + /* and word for substrings equal in all + * matches. */ + inststrlen(ssl->word, 1, ssl->wlen); + /* If the string differs from any of the + * matches, remember the position. */ + if (d < 0 && (ssl->flags & CLF_DIFF)) + d = cs; + } + } else if (sl->wlen) + inststrlen(sl->word, 1, sl->wlen); + + if (m < 0 && (sl->flags & CLF_MISS)) + m = cs; + if (sl->llen) + inststrlen(sl->line, 1, sl->llen); + hm = (sl->flags & CLF_MISS); + } + if ((l->flags & CLF_MID) &&hm && b < 0) { + b = cs; + hb = 1; + } + } else { + /* The cline contains a newly build part of the string + * in a sub-list. */ + for (; sl; sl = sl->next) { + if (sl->line) + /* line for a string derived from applying the + * matchers the other way round. */ + inststrlen(sl->line, 1, sl->llen); + else + /* and word for substrings equal in all matches. */ + inststrlen(sl->word, 1, sl->wlen); + /* If the string differs from any of the matches, + * remember the position. */ + if (d < 0 && (sl->flags & CLF_DIFF)) + d = cs; + } + } + if (!(l->flags & (CLF_END | CLF_MID))) + i += l->llen; + } + if (l->suffix) { + Cline sl = revert_clines(l->suffix); + + if ((sl->flags & CLF_MISS) && b < 0) { b = cs; - if (l->wlen > 0) + hb = 1; + } + for (; sl; sl = sl->next) { + if (sl->line) + inststrlen(sl->line, 1, sl->llen); + else + inststrlen(sl->word, 1, sl->wlen); + if (d < 0 && (sl->flags & CLF_DIFF)) + d = cs; + } + } + if (l->flags & CLF_MID) { + /* The cline in the middle, insert the prefix and the + * suffix. */ + if (!l->prefix && l->llen) { + inststrlen(l->line, 1, l->llen); + if (b < 0) { + b = cs; + hb = l->flags & CLF_MISS; + } + } + if (!l->suffix && l->wlen > 0) inststrlen(l->word, 1, l->wlen); - } else if (l->word) { - inststrlen(l->word, 1, l->wlen); - } else { - inststrlen(l->line, 1, l->llen); + } else if (!l->prefix && !l->suffix) { + if (l->word && + !((pl >= 0 && brbeg && *brbeg && + i < pl && i + l->llen >= pl) || + (sl >= 0 && brend && *brend && + i < sl && i + l->llen >= sl))) { + /* We insert the prefered string stored in word only if we + * don't have to put the brace beginning or end here. */ + inststrlen(l->word, 1, l->wlen); + } else { + /* Otherwise just re-insert the original string. */ + inststrlen(l->line, 1, l->llen); + } + i += l->llen; } + /* Remember the position if there is a difference or a missing + * substring. */ if (d < 0 && (l->flags & CLF_DIFF)) d = cs; if (m < 0 && (l->flags & CLF_MISS)) m = cs; - i += l->llen; + + /* Probably insert the brace beginning or end. */ if (pl >= 0 && i >= pl && brbeg && *brbeg) { + cs -= i - pl; inststrlen(brbeg, 1, -1); + cs += i - pl; pl = -1; hb = 1; } - if (sl >= 0 && i >= sl && brend && *brend) { + if (sl >= 0 && i > sl && brend && *brend) { + cs -= i - sl; inststrlen(brend, 1, -1); + cs += i - sl; sl = -1; hb = 1; } l = l->next; } + /* Now place the cursor. Preferably in a position where something + * is missing, otherwise in a place where the string differs from + * any of the matches, or just leave it at the end. */ cs = (b >= 0 && hb ? b : (m >= 0 ? m : (d >= 0 ? d : cs))); if (cs > ll) cs = ll; @@ -1890,6 +3142,7 @@ inst_cline(Cline l, int pl, int sl) * given in `in' so that we can easily test if we found the * * corresponding character. */ +/**/ static int pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out) { @@ -1898,22 +3151,26 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out) while (p) { c = *((unsigned char *) s); - if (out) *out = 0; + if (out) + *out = 0; if (p->equiv) { if (in) { c = p->tab[c]; - if ((*in && *in != c) || (!*in && !c)) return 0; + if ((*in && *in != c) || (!*in && !c)) + return 0; } else if (out) { - if (!(*out = p->tab[c])) return 0; - } else { - if (!p->tab[c]) return 0; - } - if (in) in++; - if (out) out++; - } else { - if (!p->tab[c]) return 0; - } + if (!(*out = p->tab[c])) + return 0; + } else if (!p->tab[c]) + return 0; + + if (in && *in) + in++; + if (out) + out++; + } else if (!p->tab[c]) + return 0; s++; p = p->next; @@ -1921,18 +3178,23 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out) return 1; } -/* Do the matching for a prefix. */ +/* Do the matching for a prefix. l and w contain the strings on the line and + * for the generated word, respectively. In nlp we return a cline list for this + * match. In lp we return the length of the matched string. rlp is used to + * return a pointer to the last cline struct in the list returned in nlp, and + * in bplp we return the relative position where the brace beginning would + * have to be insrted in the string returned, which is the string to use + * as the completion. */ static char * -match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, - Cmatcher nm) +match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp) { static unsigned char *ea; static int ealen = 0; static char *rw; static int rwlen; - int ll = strlen(l), lw = strlen(w), mlw; + int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw, mlw; int il = 0, iw = 0, t, bc = brpl; char *nw = rw; Cmlist ms; @@ -1942,7 +3204,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, if (nlp) { *nlp = NULL; - if (ll > ealen) { + if (ll + 1 > ealen) { /* This is the `in'/`out' string for pattern matching. */ if (ealen) zfree(ea, ealen); @@ -1950,15 +3212,19 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, } } while (ll && lw) { + /* First try the matchers. */ for (ms = mstack; ms; ms = ms->next) { for (mp = ms->matcher; mp; mp = mp->next) { - if (nm == mp || lm == mp) { - nm = NULL; + if (lm == mp) continue; - } + t = 1; - /* Try to match the prefix, if any. */ - if (mp->flags & CMF_LEFT) { + if ((oll == ll || olw == lw) && !nlp && mp->wlen < 0) + /* If we were called recursively, don't use `*' patterns + * at the beginning (avoiding infinite recursion). */ + t = 0; + else if (mp->flags & CMF_LEFT) { + /* Try to match the left anchor, if any. */ if (il < mp->lalen || iw < mp->lalen) t = 0; else if (mp->left) @@ -1972,42 +3238,61 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, if (ll < mp->llen || lw < mp->wlen) t = 0; else if (mp->wlen < 0) { - /* This is reached, if we have a `*' pattern. */ + /* This is reached if we have a `*' pattern. */ if ((t = pattern_match(mp->line, l, NULL, NULL))) { - if (mp->flags & CMF_RIGHT) { - if (mp->right && ll >= mp->llen + mp->ralen) - t = pattern_match(mp->right, l + mp->llen, - NULL, NULL); - else - t = 0; - } + if (mp->flags & CMF_RIGHT) + /* Check if the anchor matches what's on the + * line. If it also matches the word, we don't + * use the matcher since we don't want one of + * these patterns on the line to match more + * than one such sub-string in the word. */ + t = (mp->right && ll >= mp->llen + mp->ralen && + pattern_match(mp->right, l + mp->llen, + NULL, NULL) && + lw >= mp->ralen && + !pattern_match(mp->right, w, NULL, NULL)); if (t) { + /* The anchor matched, so find out how many + * characters are matched by the `*' pattern. + * We do this by looping over the string + * and calling this function recursively. */ 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)) + if (match_pfx(p, q, NULL, NULL, NULL, NULL)) break; } if (k && i) { + /* We found a position where the rest of + * the line matched again (k != 0) and + * we skipped over at least one character + * (i != 0), so add a cline for this */ 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)); + CLF_SUF : 0) | CLF_VAR); } + /* ...and adjust the pointers and counters + * to continue after the matched portion. */ w = q; iw = j; - lw = k; l = p; il = jj; ll = kk; - bc -= i; - + bc -= mp->llen; + + /* In bc we count the characters used + * backward starting with the original + * position of the brace beginning. So, + * if it becomes less than or equal to + * zero, we have reached the place where + * the brace string would have to be + * inserted. */ if (bc <= 0 && bplp) { *bplp = nw - rw; bplp = NULL; @@ -2107,7 +3392,8 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, *rlp = lr; } else { - addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END); + addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, + CLF_VAR | CLF_END); nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0); } } @@ -2135,10 +3421,11 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp, return ((char *) 1); } -/* Do the matching for a suffix. */ +/* Do the matching for a suffix. Almost the same as match_pfx(), only in the +* other direction. */ static char * -match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) +match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp) { static unsigned char *ea; static int ealen = 0; @@ -2158,7 +3445,7 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) if (nlp) { *nlp = NULL; - if (ll > ealen) { + if (ll + 1 > ealen) { if (ealen) zfree(ea, ealen); ea = (unsigned char *) zalloc(ealen = ll + 20); @@ -2167,10 +3454,9 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) while (ll && lw) { for (ms = mstack; ms; ms = ms->next) { for (mp = ms->matcher; mp; mp = mp->next) { - if (nm == mp || lm == mp) { - nm = NULL; + if (lm == mp) continue; - } + t = 1; if (mp->flags & CMF_RIGHT) { if (il < mp->ralen || iw < mp->ralen) @@ -2187,22 +3473,21 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) else if (mp->wlen < 0) { if ((t = pattern_match(mp->line, l - mp->llen, NULL, NULL))) { - if (mp->flags & CMF_LEFT) { - if (mp->left && ll >= mp->llen + mp->lalen) - t = pattern_match(mp->left, - l - mp->llen - mp->lalen, - NULL, NULL); - else - t = 0; - } + if (mp->flags & CMF_LEFT) + t = (mp->left && ll >= mp->llen + mp->lalen && + pattern_match(mp->left, + l - mp->llen - mp->lalen, + NULL, NULL) && + lw >= mp->lalen && + !pattern_match(mp->left, w - mp->lalen, + NULL, NULL)); if (t) { int i = 0, j = iw, k = lw; int jj = il + mp->llen, kk = ll - mp->llen; char *p = l - mp->llen - 1, *q = w - 1; for (; k; i++, j++, k--, q--) - if (match_pfx(p, q, NULL, NULL, - NULL, NULL, mp)) + if (match_pfx(p, q, NULL, NULL, NULL, NULL)) break; if (k && i) { if (nlp) { @@ -2212,15 +3497,14 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) addtocline(nlp, &lr, l - mp->llen, mp->llen, w - i, i, mp, ((mp->flags & CMF_LEFT) ? - CLF_SUF : 0)); + CLF_SUF : 0) | CLF_VAR); } w = q + 1; iw = j; - lw = k; l = p + 1; il = jj; ll = kk; - bc -= i; + bc -= mp->llen; if (bc <= 0 && bslp) { *bslp = nw - rw; @@ -2252,7 +3536,8 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) } if (t) { if (nlp) { - nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1); + nw = addtoword(&rw, &rwlen, nw, mp, l - mp->llen, + w - mlw, mlw, 1); addtocline(nlp, &lr, l - mp->llen, mp->llen, w - mlw, mlw, mp, 0); } @@ -2321,7 +3606,14 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm) return ((char *) 1); } -/* Check if the word `w' matches. */ +/* Check if the word `w' is matched by the strings in pfx and sfx (the prefix + * and the suffix from the line. In clp a cline list is returned for w. + * qu is non-zero if the words has to be quoted before processed any further. + * bpl and bsl are used to report the positions where the brace-strings in + * the prefix and the suffix have to be re-inserted if this match is inserted + * in the line. + * The return value is the string to use as a completion or NULL if the prefix + * and the suffix don't match the word w. */ static char * comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl) @@ -2332,18 +3624,27 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl if (qu) w = quotename(w, NULL, NULL, NULL); + if (*sfx) { + /* We have a suffix, so this gets a bit more complicated. */ char *p, *s; int sl; Cline sli, last; - if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl, NULL))) { - if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl, NULL))) { + /* First see if the prefix matches. In pl we get the length of + * the string matched by it. */ + if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) { + /* Then try to match the rest of the string with the suffix. */ + if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) { int pml, sml; + /* Now append the cline list for the suffix to the one + * for the prefix. */ last->llen -= sl; last->next = revert_clines(sli); + /* And store the correct parts of the prefix and the suffix + * in the cline struct in the middle. */ pml = strlen(p); sml = strlen(s); r = (char *) halloc(pml + sml + last->llen + 1); @@ -2351,6 +3652,8 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl strncpy(r + pml, last->line, last->llen); strcpy(r + pml + last->llen, s); } else { + /* Suffix didn't match, so free the cline list for the + * prefix. */ last->next = freecl; freecl = pli; @@ -2359,9 +3662,12 @@ 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, NULL))) + } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl))) + /* We had only the prefix to match and that didn't match. */ return NULL; + /* If there are path prefixes or suffixes, pre- and append them to + * the cline list built. */ if (lppre && *lppre) { Cline l, t = str_cline(lppre, -1, &l); @@ -2409,21 +3715,26 @@ instmatch(Cmatch m) { int l, r = 0, ocs, a = cs; + /* Ignored prefix. */ if (m->ipre) { inststrlen(m->ipre, 1, (l = strlen(m->ipre))); r += l; - } + } + /* -P prefix. */ if (m->pre) { inststrlen(m->pre, 1, (l = strlen(m->pre))); r += l; } + /* Path prefix. */ if (m->ppre) { inststrlen(m->ppre, 1, (l = strlen(m->ppre))); r += l; } + /* The string itself. */ inststrlen(m->str, 1, (l = strlen(m->str))); r += l; ocs = cs; + /* Re-insert the brace beginning, if any. */ if (brbeg && *brbeg) { cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0); l = strlen(brbeg); @@ -2433,10 +3744,12 @@ instmatch(Cmatch m) ocs += l; cs = ocs; } + /* Path suffix. */ if (m->psuf) { inststrlen(m->psuf, 1, (l = strlen(m->psuf))); r += l; } + /* Re-insert the brace end. */ if (brend && *brend) { a = cs; cs -= m->brsl; @@ -2447,6 +3760,7 @@ instmatch(Cmatch m) cs = a + l; } else brscs = -1; + /* -S suffix */ if (m->suf) { inststrlen(m->suf, 1, (l = strlen(m->suf))); r += l; @@ -2455,13 +3769,17 @@ instmatch(Cmatch m) return r; } +/* This is used by compadd to add a couple of matches. The arguments are + * the strings given via options. The last argument is the array with + * the matches. */ + /**/ void addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, char *suf, char *group, char *rems, char *remf, char *ign, int flags, int aflags, Cmatcher match, char **argv) { - char *s, *t, *e, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL; + char *s, *t, *e, *me, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL; int lpl, lsl, i, pl, sl, test, bpl, bsl, lsm, llpl; Aminfo ai; Cline lc = NULL; @@ -2470,6 +3788,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, struct cmlist mst; Cmlist oms = mstack; + /* Select the set of matches. */ if (aflags & CAF_ALT) { l = fmatches; ai = fainfo; @@ -2477,26 +3796,44 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, l = matches; ai = ainfo; } + /* Store the matcher in our stack of matchers. */ if (match) { mst.next = mstack; mst.matcher = match; mstack = &mst; + if (had_lm && mnum) + bmstack1.matcher = NULL; + else { + bmstack1.matcher = match; + had_lm = 1; + } addlinknode(matchers, match); match->refc++; + } else { + bmstack1.matcher = NULL; + if (mnum) + had_lm = 1; } + /* Use menu-completion (-U)? */ if ((aflags & CAF_MENU) && isset(AUTOMENU)) usemenu = 1; + /* Get the suffixes to ignore. */ if (ign) aign = get_user_var(ign); + /* Switch back to the heap that was used when the completion widget + * was invoked. */ SWITCHHEAPS(compheap) { HEAPALLOC { + /* Get the contents of the completion variables if we have + * to perform matching. */ if (aflags & CAF_MATCH) { lipre = dupstring(compiprefix); lpre = dupstring(compprefix); llpl = strlen(lpre); lsuf = dupstring(compsuffix); } + /* Now duplicate the strings we have from the command line. */ if (ipre) ipre = (lipre ? dyncat(lipre, ipre) : dupstring(ipre)); else if (lipre) @@ -2522,6 +3859,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, untokenize(prpre); } else prpre = dupstring(prpre); + /* Select the group in which to store the matches. */ if (group) { endcmgroup(NULL); begcmgroup(group, (aflags & CAF_NOSORT)); @@ -2533,6 +3871,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, rems = NULL; } else if (rems) rems = dupstring(rems); + /* Build the common -P prefix. */ if (ai->pprefix) { if (pre) ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0'; @@ -2541,12 +3880,16 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } else ai->pprefix = dupstring(pre ? pre : ""); + /* Walk through the matches given. */ for (; (s = dupstring(*argv)); argv++) { sl = strlen(s); + lc = NULL; ms = NULL; bpl = brpl; bsl = brsl; if ((!psuf || !*psuf) && aign) { + /* Do the suffix-test. If the match has one of the + * suffixes from ign, we put it in the alternate set. */ char **pt = aign; int filell; @@ -2564,6 +3907,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } } if (aflags & CAF_MATCH) { + /* Do the matching. */ t = (ppre ? dyncat(ppre, s) : s); pl = sl + lpl; if ((test = (llpl <= pl && !strncmp(t, lpre, pl)))) @@ -2576,30 +3920,61 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, test = 1; if (!test) continue; - e = s + sl; + me = e = s + sl; } else { e = s; - pl = lpl; + me = s + sl; + pl = sl; } + /* Quoting? */ if (!(aflags & CAF_QUOTE)) { te = s + pl; s = quotename(s, &e, te, &pl); sl = strlen(s); } - if (!ms && ai->firstm) { + /* The rest is almost the same as in addmatch(). */ + if (!ms) { if (sl < ai->minlen) ai->minlen = sl; - if ((i = sfxlen(ai->firstm->str, s)) < ai->suflen) + if (!mstack && ai->firstm && + (i = sfxlen(ai->firstm->str, s)) < ai->suflen) ai->suflen = i; } t = s; if (ppre) t = dyncat(ppre, t); + if (!ms && mstack) { + Cline *clp = &lc, tlc; + char *ss = dupstring(s), *ee = me + (ss - s); + + if (ppre && *ppre) { + *clp = str_cline(ppre, strlen(ppre), &tlc); + clp = &(tlc->next); + } + if (ee != ss + sl || (lpsuf && *lpsuf)) { + *clp = tlc = getcline(ss, ee - ss, + NULL, 0, CLF_MID); + clp = &(tlc->next); + if (ee != ss + sl) { + *clp = str_cline(ee, (ss + sl) - ee, &tlc); + clp = &(tlc->next); + } + if (lpsuf && *lpsuf) { + *clp = str_cline(lpsuf, strlen(lpsuf), &tlc); + clp = &(tlc->next); + } + } else { + *clp = tlc = getcline(NULL, 0, ss, sl, + CLF_END | CLF_VAR); + clp = &(tlc->next); + } + *clp = NULL; + } if (ipre && *ipre) { Cline tlc = prepend_cline(ipre, lc); ai->noipre = 0; - if (!ms) { + if (!ms && !mstack) { if ((aflags & CAF_MATCH) || ai->icpl > pl) ai->icpl = pl; if ((aflags & CAF_MATCH) || ai->icsl > lsl) @@ -2620,7 +3995,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, lc = tlc; } else ai->iprefix = ""; - if (!ms) { + if (!ms && !mstack) { if ((aflags & CAF_MATCH) || ai->cpl > pl) ai->cpl = pl; if ((aflags & CAF_MATCH) || ai->csl > lsl) @@ -2635,6 +4010,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, mnum++; ai->count++; + /* Finally add the match. */ cm = (Cmatch) halloc(sizeof(struct cmatch)); cm->ppre = ppre; cm->psuf = psuf; @@ -2674,6 +4050,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre, } LASTALLOC; } SWITCHBACKHEAPS; + /* We switched back to the current heap, now restore the stack of + * matchers. */ mstack = oms; } @@ -2854,10 +4232,11 @@ addmatch(char *s, char *t) } if (!test) return; - if (!ms && !ispattern && ai->firstm) { + if (!ms) { if (sl < ai->minlen) ai->minlen = sl; - if ((test = sfxlen(ai->firstm->str, s)) < ai->suflen) + if (!mstack && !ispattern && ai->firstm && + (test = sfxlen(ai->firstm->str, s)) < ai->suflen) ai->suflen = test; } @@ -2875,11 +4254,41 @@ addmatch(char *s, char *t) t = s; if (lppre) t = dyncat(lppre, t); + if (!ms && mstack) { + Cline *clp = &lc, tlc; + char *ss = dupstring(s), *ee = e + (ss - s); + + if (lppre && *lppre) { + *clp = str_cline(lppre, strlen(lppre), &tlc); + clp = &(tlc->next); + } + if (pl) { + *clp = str_cline(ss, pl, &tlc); + clp = &(tlc->next); + } + if (ee != ss + sl || (lpsuf && *lpsuf)) { + *clp = tlc = getcline(ss + pl, (ee - ss) - pl, NULL, 0, CLF_MID); + clp = &(tlc->next); + if (ee != ss + sl) { + *clp = str_cline(ee, (ss + sl) - ee, &tlc); + clp = &(tlc->next); + } + if (lpsuf && *lpsuf) { + *clp = str_cline(lpsuf, strlen(lpsuf), &tlc); + clp = &(tlc->next); + } + } else { + *clp = tlc = getcline(NULL, 0, ss + pl, sl - pl, + CLF_END | CLF_VAR); + clp = &(tlc->next); + } + *clp = NULL; + } if (ipre && *ipre) { Cline tlc = prepend_cline(ipre, lc); ai->noipre = 0; - if (!ms) { + if (!ms && !mstack) { ai->icpl = lppl + mpl; ai->icsl = lpsl + msl; if (ai->iaprefix) @@ -2899,7 +4308,7 @@ addmatch(char *s, char *t) } else ai->iprefix = ""; - if (!ms) { + if (!ms && !mstack) { ai->cpl = lppl + mpl; ai->csl = lpsl + msl; if (ai->aprefix) @@ -3146,7 +4555,8 @@ getreal(char *str) addlinknode(l, dupstring(str)); prefork(l, 0); noerrs = ne; - if (!errflag && nonempty(l)) + if (!errflag && nonempty(l) && + ((char *) peekfirst(l)) && ((char *) peekfirst(l))[0]) return dupstring(peekfirst(l)); errflag = 0; @@ -3464,14 +4874,13 @@ static int brange, erange; static unsigned long ccont; -/* Create the completion list. This is called whenever some bit of * - * completion code needs the list. If the list is already available * - * (validlist!=0), this function doesn't do anything. Along with * - * the list is maintained the prefixes/suffixes etc. When any of * - * this becomes invalid -- e.g. if some text is changed on the * - * command line -- invalidatelist() should be called, to set * - * validlist to zero and free up the memory used. This function * - * returns non-zero on error. */ +/* Create the completion list. This is called whenever some bit of * + * completion code needs the list. * + * Along with the list is maintained the prefixes/suffixes etc. When * + * any of this becomes invalid -- e.g. if some text is changed on the * + * command line -- invalidatelist() should be called, to set * + * validlist to zero and free up the memory used. This function * + * returns non-zero on error. */ /**/ static int @@ -3482,8 +4891,6 @@ makecomplist(char *s, int incmd, int lst) /* If we already have a list from a previous execution of this * * function, skip the list building code. */ - if (validlist) - return !nmatches; if ((m = cmatcher)) { Cmlist mm, *mp = &mm; @@ -3499,13 +4906,19 @@ makecomplist(char *s, int incmd, int lst) m = mm; } compmatcher = 1; + bmstack = &bmstack1; + bmstack1.next = &bmstack0; + bmstack0.next = NULL; + bmstack0.matcher = bmstack1.matcher = NULL; for (;;) { if (m) { ms.next = NULL; ms.matcher = m->matcher; mstack = &ms; + bmstack0.matcher = m->matcher; } else mstack = NULL; + ainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); fainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); @@ -3515,9 +4928,11 @@ makecomplist(char *s, int incmd, int lst) freecl = NULL; - lastambig = 0; + if (!validlist) + lastambig = 0; amatches = 0; mnum = 0; + had_lm = 0; begcmgroup("default", 0); ccused = newlinklist(); @@ -3573,11 +4988,13 @@ ctokenize(char *p) if (*p == '\\') bslash = 1; else { - if (*p == '$' || *p == '=') { + if (*p == '$' || *p == '=' || *p == '{' || *p == '}') { if (bslash) p[-1] = Bnull; else - *p = (*p == '$' ? String : Equals); + *p = (*p == '$' ? String : + (*p == '=' ? Equals : + (*p == '{' ? Inbrace : Outbrace))); } bslash = 0; } @@ -4154,8 +5571,18 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) ms.next = mstack; ms.matcher = cc->matcher; mstack = &ms; + if (had_lm && mnum) + bmstack1.matcher = NULL; + else { + bmstack1.matcher = cc->matcher; + had_lm = 1; + } addlinknode(matchers, cc->matcher); cc->matcher->refc++; + } else { + bmstack1.matcher = NULL; + if (mnum) + had_lm = 1; } /* Insert the prefix (compctl -P), if any. */ if (cc->prefix) { @@ -5001,7 +6428,7 @@ invalidatelist(void) listmatches(); if(validlist) freematches(); - lastambig = menucmp = validlist = showinglist = 0; + lastambig = menucmp = validlist = showinglist = fromcomp = 0; menucur = NULL; compwidget = NULL; } @@ -5056,15 +6483,17 @@ get_user_var(char *nam) } else { /* Otherwise it should be a parameter name. */ char **arr = NULL, *val; - if (!(arr = getaparam(nam)) && !(arr = gethparam(nam)) && - (val = getsparam(nam))) { + + if ((arr = getaparam(nam)) || (arr = gethparam(nam))) + return (incompfunc ? arrdup(arr) : arr); + + if ((val = getsparam(nam))) { arr = (char **)ncalloc(2*sizeof(char *)); arr[0] = val; arr[1] = NULL; } return arr; } - } /* This is strcmp with ignoring backslashes. */ @@ -5437,14 +6866,13 @@ static void do_ambiguous(void) { int p = (usemenu || ispattern), atend = (cs == we); - int am = 0; menucmp = 0; /* If we have to insert the first match, call do_single(). This is * * how REC_EXACT takes effect. We effectively turn the ambiguous * * completion into an unambiguous one. */ - if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && + if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !fromcomp && (usemenu == 0 || unset(AUTOMENU))) { do_single(ainfo->exactm); invalidatelist(); @@ -5464,16 +6892,21 @@ do_ambiguous(void) * normal menu completion options. */ do_ambig_menu(); } else { - int ics = cs, ocs, pl = 0, l, lp, ls; + VARARR(char, oline, ll); + int sl = 0, oll = ll; + int ocs, pl = 0, l, lp, ls, la = 0; char *ps; Cline lc; if (!ainfo) return; + /* Copy the line buffer to be able to easily test if it changed. */ + memcpy(oline, line, ll); + fixsuffix(); - /* Delete the old stuff from the command line. */ + /* First remove the old string from the line. */ cs = wb; foredel(we - wb); @@ -5499,8 +6932,6 @@ do_ambiguous(void) ls = ainfo->csl; } if (lc) { - int sl = 0; - if (lp) { if (ls) { if (ainfo->firstm->psuf) @@ -5514,6 +6945,7 @@ do_ambiguous(void) merge_cline(lc, ps, lp, NULL, 0, 0); } inst_cline(lc, pl, sl); + } else { inststrlen(ps, 1, -1); ocs = cs; @@ -5536,13 +6968,16 @@ do_ambiguous(void) } cs = ocs; } - /* If REC_EXACT and AUTO_MENU are set and what we inserted is an * - * exact match, we want to start menu completion now. Otherwise * - * on the next call to completion the inserted string would be * - * taken as a match and no menu completion would be started. */ + /* la is non-zero if listambiguous may be used. Copying and + * comparing the line looks like BFI but it is the easiest + * solution. Really. */ + la = (ll != oll || strncmp(oline, (char *) line, ll)); - if (isset(RECEXACT) && !lc && ps && ainfo->minlen == strlen(ps)) - am = 1; + /* If REC_EXACT and AUTO_MENU are set and what we inserted is an * + * exact match, we want menu completion the next time round * + * so we set fromcomp,to ensure that the word on the line is not * + * taken as an exact match. */ + fromcomp = isset(AUTOMENU); /* * If the LIST_AMBIGUOUS option (meaning roughly `show a list only * @@ -5550,9 +6985,11 @@ do_ambiguous(void) * prefix was inserted, return now, bypassing the list-displaying * * code. On the way, invalidate the list and note that we don't * * want to enter an AUTO_MENU imediately. */ - if(isset(LISTAMBIGUOUS) && !am && - (ics != cs || (ainfo->suflen && !atend))) { + if(isset(LISTAMBIGUOUS) && la) { + int fc = fromcomp; + invalidatelist(); + fromcomp = fc; lastambig = 0; return; } @@ -5564,8 +7001,6 @@ do_ambiguous(void) if (isset(AUTOLIST) && !isset(BASHAUTOLIST) && !amenu && !showinglist && smatches >= 2) showinglist = -2; - if (am) - lastambig = 1; } /* This is a stat that ignores backslashes in the filename. The `ls' * diff --git a/Src/cond.c b/Src/cond.c index a46833f66..00beb8e65 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -30,6 +30,13 @@ #include "zsh.mdh" #include "cond.pro" +int tracingcond; + +static char *condstr[COND_MOD] = { + "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", + "-ne", "-lt", "-gt", "-le", "-ge" +}; + /**/ int evalcond(Cond c) @@ -38,11 +45,23 @@ evalcond(Cond c) switch (c->type) { case COND_NOT: + if (tracingcond) + fprintf(stderr, " %s", condstr[c->type]); return !evalcond(c->left); case COND_AND: - return evalcond(c->left) && evalcond(c->right); + if (evalcond(c->left)) { + if (tracingcond) + fprintf(stderr, " %s", condstr[c->type]); + return evalcond(c->right); + } else + return 0; case COND_OR: - return evalcond(c->left) || evalcond(c->right); + if (!evalcond(c->left)) { + if (tracingcond) + fprintf(stderr, " %s", condstr[c->type]); + return evalcond(c->right); + } else + return 1; case COND_MOD: case COND_MODI: { @@ -58,6 +77,9 @@ evalcond(Cond c) return 0; } } + if (tracingcond) + tracemodcond((char *)c->left, (char **)c->right, + c->type == COND_MODI); return cd->handler((char **) c->right, cd->condid); } else { @@ -71,6 +93,8 @@ evalcond(Cond c) zerr("unrecognized condition: `%s'", (char *) c->left, 0); return 0; } + if (tracingcond) + tracemodcond((char *)c->left, a, c->type == COND_MODI); a[0] = (char *) c->left; return cd->handler(a, cd->condid); } else @@ -86,6 +110,20 @@ evalcond(Cond c) if (c->type != COND_STREQ && c->type != COND_STRNEQ) untokenize(c->right); } + + if (tracingcond) { + if (c->type < COND_MOD) { + char *rt = (char *)c->right; + if (c->type == COND_STREQ || c->type == COND_STRNEQ) { + rt = dupstring(rt); + untokenize(rt); + } + fprintf(stderr, " %s %s %s", (char *)c->left, condstr[c->type], + rt); + } else + fprintf(stderr, " -%c %s", c->type, (char *)c->left); + } + switch (c->type) { case COND_STREQ: return matchpat(c->left, c->right); @@ -294,3 +332,21 @@ cond_match(char **args, int num, char *str) return matchpat(str, s); } + +/**/ +static void +tracemodcond(char *name, char **args, int inf) +{ + char **aptr; + MUSTUSEHEAP("tracemodcond"); + args = duparray(args, (VFunc) dupstring); + for (aptr = args; *aptr; aptr++) + untokenize(*aptr); + if (inf) { + fprintf(stderr, " %s %s %s", args[0], name, args[1]); + } else { + fprintf(stderr, " %s", name); + while (*args) + fprintf(stderr, " %s", *args++); + } +} diff --git a/Src/exec.c b/Src/exec.c index bb331f426..59061af3f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1208,7 +1208,7 @@ addvars(LinkList l, int export) addlinknode(vl, v->str); } else vl = v->arr; - prefork(vl, v->type == PM_SCALAR ? 7 : 3); + prefork(vl, v->type == PM_SCALAR ? (PF_SINGLE|PF_ASSIGN) : PF_ASSIGN); if (errflag) return; if (isset(GLOBASSIGN) || v->type != PM_SCALAR) @@ -1356,7 +1356,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } /* Do prefork substitutions */ - prefork(args, assign ? 2 : isset(MAGICEQUALSUBST)); + prefork(args, (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0); if (type == SIMPLE) { int unglobbed = 0; @@ -2519,13 +2519,26 @@ spawnpipes(LinkList l) } } +extern int tracingcond; + /* evaluate a [[ ... ]] */ /**/ static int execcond(Cmd cmd) { - return !evalcond(cmd->u.cond); + int stat; + if (isset(XTRACE)) { + fprintf(stderr, "%s[[", prompt4 ? prompt4 : ""); + tracingcond++; + } + stat = !evalcond(cmd->u.cond); + if (isset(XTRACE)) { + fprintf(stderr, " ]]\n"); + fflush(stderr); + tracingcond--; + } + return stat; } /* evaluate a ((...)) arithmetic command */ @@ -2537,8 +2550,17 @@ execarith(Cmd cmd) char *e; long val = 0; - while ((e = (char *) ugetnode(cmd->args))) + if (isset(XTRACE)) + fprintf(stderr, "%s((", prompt4 ? prompt4 : ""); + while ((e = (char *) ugetnode(cmd->args))) { + if (isset(XTRACE)) + fprintf(stderr, " %s", e); val = matheval(e); + } + if (isset(XTRACE)) { + fprintf(stderr, " ))\n", stderr); + fflush(stderr); + } errflag = 0; return !val; } @@ -2613,6 +2635,18 @@ execshfunc(Cmd cmd, Shfunc shf) deletejob(jobtab + thisjob); } + if (isset(XTRACE)) { + LinkNode lptr; + fprintf(stderr, "%s", prompt4 ? prompt4 : prompt4); + for (lptr = firstnode(cmd->args); lptr; incnode(lptr)) { + if (lptr != firstnode(cmd->args)) + fputc(' ', stderr); + fprintf(stderr, "%s", (char *)getdata(lptr)); + } + fputc('\n', stderr); + fflush(stderr); + } + doshfunc(shf->nam, shf->funcdef, cmd->args, shf->flags, 0); if (!list_pipe) diff --git a/Src/glob.c b/Src/glob.c index 5815f05a8..516f75618 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -1810,7 +1810,7 @@ xpandredir(struct redir *fn, LinkList tab) fake = newlinklist(); addlinknode(fake, fn->name); /* ...which undergoes all the usual shell expansions */ - prefork(fake, isset(MULTIOS) ? 0 : 4); + prefork(fake, isset(MULTIOS) ? 0 : PF_SINGLE); /* Globbing is only done for multios. */ if (!errflag && isset(MULTIOS)) globlist(fake); diff --git a/Src/params.c b/Src/params.c index 88b5fdf73..77166209f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -568,8 +568,10 @@ createparam(char *name, int flags) if (isset(ALLEXPORT) && !oldpm) flags |= PM_EXPORTED; - } else + } else { pm = (Param) alloc(sizeof *pm); + pm->nam = nulstring; + } pm->flags = flags; if(!(pm->flags & PM_SPECIAL)) @@ -1835,6 +1837,12 @@ arrhashsetfn(Param pm, char **val) while (*aptr) { /* The parameter name is ztrdup'd... */ v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET); + /* + * createparam() doesn't return anything if the parameter + * already existed. + */ + if (!v->pm) + v->pm = (Param) paramtab->getnode(paramtab, *aptr); zsfree(*aptr++); /* ...but we can use the value without copying. */ setstrvalue(v, *aptr++); diff --git a/Src/subst.c b/Src/subst.c index 422c9c4e9..66c363145 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -42,11 +42,7 @@ char nulstring[] = {Nularg, '\0'}; * - Brace expansion * - Tilde and equals substitution * - * Bits 0 and 1 of flags are used in filesub. - * bit 0 is set when we are doing MAGIC_EQUALSUBST or normal - * assignment but not a typeset. - * bit 1 is set on a real assignment (both typeset and normal). - * bit 2 is a flag to paramsubst (single word sub) + * PF_* flags are defined in zsh.h */ /**/ @@ -70,20 +66,22 @@ prefork(LinkList list, int flags) return; } else { if (isset(SHFILEEXPANSION)) - filesub((char **)getaddrdata(node), flags & 3); - if (!(node = stringsubst(list, node, flags & 4))) + filesub((char **)getaddrdata(node), + flags & (PF_TYPESET|PF_ASSIGN)); + if (!(node = stringsubst(list, node, flags & PF_SINGLE))) return; } } for (node = firstnode(list); node; incnode(node)) { if (*(char *)getdata(node)) { remnulargs(getdata(node)); - if (unset(IGNOREBRACES) && !(flags & 4)) + if (unset(IGNOREBRACES) && !(flags & PF_SINGLE)) while (hasbraces(getdata(node))) xpandbraces(list, &node); if (unset(SHFILEEXPANSION)) - filesub((char **)getaddrdata(node), flags & 3); - } else if (!(flags & 4)) + filesub((char **)getaddrdata(node), + flags & (PF_TYPESET|PF_ASSIGN)); + } else if (!(flags & PF_SINGLE)) uremnode(list, node); if (errflag) return; @@ -234,7 +232,7 @@ singsub(char **s) foo = newlinklist(); addlinknode(foo, *s); - prefork(foo, 4); + prefork(foo, PF_SINGLE); if (errflag) return; *s = (char *) ugetnode(foo); @@ -287,8 +285,10 @@ multsub(char **s, char ***a, int *isarr, char *sep) return !l; } -/* ~, = subs: assign = 2 => typeset; assign = 1 => something that looks - like an assignment but may not be; assign = 3 => normal assignment */ +/* + * ~, = subs: assign & PF_TYPESET => typeset or magic equals + * assign & PF_ASSIGN => normal assignment + */ /**/ void @@ -302,12 +302,8 @@ filesub(char **namptr, int assign) if (!assign) return; - if (assign < 3) { + if (assign & PF_TYPESET) { if ((*namptr)[1] && (sub = strchr(*namptr + 1, Equals))) { - if (assign == 1) - for (ptr = *namptr; ptr != sub; ptr++) - if (!iident(*ptr) && !INULL(*ptr)) - return; str = sub + 1; if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) { sub[1] = '\0'; diff --git a/Src/utils.c b/Src/utils.c index ee06cfeca..4ff4a91ba 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -2261,7 +2261,7 @@ duplist(LinkList l, VFunc func) } /**/ -static char ** +char ** duparray(char **arr, VFunc func) { char **ret, **rr; diff --git a/Src/zsh.h b/Src/zsh.h index 87ec3e0c0..fa5b5632c 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -950,6 +950,11 @@ struct param { #define SUB_ALL 0x0100 /* match complete string */ #define SUB_GLOBAL 0x0200 /* global substitution ${..//all/these} */ +/* Flags as the second argument to prefork */ +#define PF_TYPESET 0x01 /* argument handled like typeset foo=bar */ +#define PF_ASSIGN 0x02 /* argument handled like the RHS of foo=bar */ +#define PF_SINGLE 0x04 /* single word substitution */ + /* node for named directory hash table (nameddirtab) */ struct nameddir { diff --git a/config.guess b/config.guess index 206b98874..0d66b2e56 100755 --- a/config.guess +++ b/config.guess @@ -526,6 +526,9 @@ EOF RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` diff --git a/configure.in b/configure.in index dd6ce1e69..e41bf47d6 100644 --- a/configure.in +++ b/configure.in @@ -1036,7 +1036,7 @@ char *argv[]; # sni_cc_version=`$CC -V 2>&1 | head -1` case "$sni_cc_version" in - CDS* ) + *CDS* ) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-Blargedynsym}" ;; * ) diff --git a/patchlist.txt b/patchlist.txt index 05fc91e98..9ec40d8dd 100644 --- a/patchlist.txt +++ b/patchlist.txt @@ -445,3 +445,61 @@ new-style completions: not posted Me: 5281 now works, 5364 Me: make dependencies for main.o, Makemod, zshpaths.h, 5365 + + pws-9 + +Bart: CVS should ignore version.h, 5367 + +Oliver Kiddle: another change of mailing list host, 5372 + +Oliver: compctl -T documentation for ~dirs, 5374 + +Andrej: Reliant UNIX configuration, 5377 + +Sven: Manual for new completion (so far), 5384, 5397 + +Me: dump new completion status for fast initialisation, 5393 + +Me: bug fixlet for __path_files, 5398 + +Sven: overhaul of do_ambiguous with some bug-fixage, 5399, 5407 + +Sven: print needs - in Functions/Completion/dump, 5400; auto-dump and use +$COMPDUMP file, 5402 + +Sven: files -> __files, 5401 + +Me: magicequalsubst now affects all arguments ...=~...:~..., 5403 + +Me: set -x output for [[ ... ]], 5408 + +Me: IRIX 6.5 problems in Etc/MACHINES (see 5410 from Helmut Jarausch). + +Sven: 5412: better matcher control. + +Sven: 5415: anchors in matchers shouldn't match variable part of completion + +Sven: 5417: multiple subscripts with undefined array + +Sven: 5418: small addmatches fixes + +Me: 5421: setting same element of assoc array in full array assignment crashed + +Sven: 5422: braces in completions were not tokenized; array parameters were +used uncopied + +Sven: 5423: compadd accepts either - or -- to end options + +Sven: 5424: addmatches fix when not doing matching + +Me: 5425: fix pattern matching for new completion + +Sven: 5429: $CONTEXT strings + +Sven: 5430: rewrite Functions/Completions with simplified syntax (no #array +type completions). + +Me: 5436: set -x for function calls and ((...)). + +Me: unposted (but see 5440): zftp changes: more return 6's, functions now +do auto-open and avoid subshells. |