summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:17:36 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:17:36 +0000
commit904b939cbd81a542303da2c58288b95b153106f5 (patch)
tree84b3751ed1deacc51eb186023101360ae92ef221
parentzsh-3.1.5-pws-9 (diff)
downloadzsh-904b939cbd81a542303da2c58288b95b153106f5.tar
zsh-904b939cbd81a542303da2c58288b95b153106f5.tar.gz
zsh-904b939cbd81a542303da2c58288b95b153106f5.tar.bz2
zsh-904b939cbd81a542303da2c58288b95b153106f5.tar.lz
zsh-904b939cbd81a542303da2c58288b95b153106f5.tar.xz
zsh-904b939cbd81a542303da2c58288b95b153106f5.tar.zst
zsh-904b939cbd81a542303da2c58288b95b153106f5.zip
zsh-3.1.5-pws-10zsh-3.1.5-pws-10
-rw-r--r--Completion/Base/_command_names3
-rw-r--r--Completion/Base/_condition10
-rw-r--r--Completion/Base/_default13
-rw-r--r--Completion/Base/_match_pattern31
-rw-r--r--Completion/Base/_match_test15
-rw-r--r--Completion/Base/_precommand5
-rw-r--r--Completion/Base/_redirect3
-rw-r--r--Completion/Base/_subscript4
-rw-r--r--Completion/Base/_vars3
-rw-r--r--Completion/Builtins/_aliases3
-rw-r--r--Completion/Builtins/_arrays3
-rw-r--r--Completion/Builtins/_autoload3
-rw-r--r--Completion/Builtins/_bg_jobs3
-rw-r--r--Completion/Builtins/_bindkey7
-rw-r--r--Completion/Builtins/_builtin7
-rw-r--r--Completion/Builtins/_cd3
-rw-r--r--Completion/Builtins/_command7
-rw-r--r--Completion/Builtins/_dirs3
-rw-r--r--Completion/Builtins/_disable6
-rw-r--r--Completion/Builtins/_echotc3
-rw-r--r--Completion/Builtins/_enable6
-rw-r--r--Completion/Builtins/_fc7
-rw-r--r--Completion/Builtins/_functions3
-rw-r--r--Completion/Builtins/_hash13
-rw-r--r--Completion/Builtins/_jobs3
-rw-r--r--Completion/Builtins/_kill11
-rw-r--r--Completion/Builtins/_limits3
-rw-r--r--Completion/Builtins/_sched3
-rw-r--r--Completion/Builtins/_set7
-rw-r--r--Completion/Builtins/_setopt7
-rw-r--r--Completion/Builtins/_source7
-rw-r--r--Completion/Builtins/_trap7
-rw-r--r--Completion/Builtins/_unhash6
-rw-r--r--Completion/Builtins/_unsetopt7
-rw-r--r--Completion/Builtins/_vars_eq3
-rw-r--r--Completion/Builtins/_wait7
-rw-r--r--Completion/Builtins/_which3
-rw-r--r--Completion/Builtins/_zftp50
-rw-r--r--Completion/Builtins/_zle7
-rw-r--r--Completion/Builtins/_zmodload9
-rw-r--r--Completion/Commands/_correct_filename37
-rw-r--r--Completion/Commands/_most_recent_file4
-rw-r--r--Completion/Core/_comp_parts147
-rw-r--r--Completion/Core/_compalso13
-rw-r--r--Completion/Core/_files26
-rw-r--r--Completion/Core/_main_complete48
-rw-r--r--Completion/Core/_normal54
-rw-r--r--Completion/Core/_path_files311
-rw-r--r--Completion/Core/compdump89
-rw-r--r--Completion/Core/compinit269
-rw-r--r--Completion/README107
-rw-r--r--Completion/User/_a2ps22
-rw-r--r--Completion/User/_compress3
-rw-r--r--Completion/User/_configure12
-rw-r--r--Completion/User/_dd13
-rw-r--r--Completion/User/_dvi3
-rw-r--r--Completion/User/_find21
-rw-r--r--Completion/User/_gunzip3
-rw-r--r--Completion/User/_gzip3
-rw-r--r--Completion/User/_hosts3
-rw-r--r--Completion/User/_make3
-rw-r--r--Completion/User/_man11
-rw-r--r--Completion/User/_mh70
-rw-r--r--Completion/User/_pdf3
-rw-r--r--Completion/User/_ps3
-rw-r--r--Completion/User/_rcs9
-rw-r--r--Completion/User/_rlogin9
-rw-r--r--Completion/User/_strip2
-rw-r--r--Completion/User/_stty16
-rw-r--r--Completion/User/_tar11
-rw-r--r--Completion/User/_tex3
-rw-r--r--Completion/User/_uncompress3
-rw-r--r--Completion/User/_x_options5
-rw-r--r--Completion/User/_xfig3
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Zsh/compwid.yo27
-rw-r--r--Doc/Zsh/expn.yo65
-rw-r--r--Doc/Zsh/guide.yo4
-rwxr-xr-xMisc/globtests32
-rw-r--r--Src/.cvsignore1
-rw-r--r--Src/Builtins/rlimits.c12
-rw-r--r--Src/Zle/comp.h7
-rw-r--r--Src/Zle/comp1.c5
-rw-r--r--Src/Zle/compctl.c12
-rw-r--r--Src/Zle/zle_thingy.c11
-rw-r--r--Src/Zle/zle_tricky.c941
-rw-r--r--Src/exec.c2
-rw-r--r--Src/glob.c441
-rw-r--r--Src/hashtable.c20
-rw-r--r--Src/module.c49
-rw-r--r--Src/params.c13
-rw-r--r--Src/subst.c5
-rw-r--r--Src/system.h11
-rw-r--r--Src/zsh.export1
-rw-r--r--acconfig.h7
-rw-r--r--configure.in54
-rw-r--r--patchlist.txt117
97 files changed, 2943 insertions, 541 deletions
diff --git a/Completion/Base/_command_names b/Completion/Base/_command_names
new file mode 100644
index 000000000..d3b8a109a
--- /dev/null
+++ b/Completion/Base/_command_names
@@ -0,0 +1,3 @@
+#defcomp -command-
+
+complist -c
diff --git a/Completion/Base/_condition b/Completion/Base/_condition
new file mode 100644
index 000000000..3e45e1b8f
--- /dev/null
+++ b/Completion/Base/_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/Completion/Base/_default b/Completion/Base/_default
new file mode 100644
index 000000000..8bcf14f6a
--- /dev/null
+++ b/Completion/Base/_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/Completion/Base/_match_pattern b/Completion/Base/_match_pattern
new file mode 100644
index 000000000..c5debc0b9
--- /dev/null
+++ b/Completion/Base/_match_pattern
@@ -0,0 +1,31 @@
+#autoload
+
+# This function is called from functions that do matching whenever they
+# need to build a pattern that is used to match possible completions.
+# It gets the name of the calling function and two names of parameters
+# as arguments. The first one is used in the calling function to build
+# the pattern used for matching possible completions. The content of this
+# parameter on entry to this function is the string taken from the line.
+# Here it parameter should be changed to a pattern that matches words as
+# the match specs currently in use do.
+# In the calling function this pattern may be changed again or used only
+# in parts. The second parameter whose name is given as the third argument
+# allows to give pattern flags liek `(#l)' that are to be used whenever
+# matching is done.
+#
+# As an example, if you have global match specifications like:
+#
+# compctl -M 'm:{a-z}={A-Z}' 'm:{a-z}={A-Z} r:|[.-]=* r:|=*'
+#
+# This function would look like:
+#
+# eval "${3}='(#l)'"
+# [[ MATCHER -eq 2 ]] && eval "$1='${(P)2:gs/./*./:gs/-/*-/}'"
+#
+# The first line makes sure that matching is done case-insensitive as
+# specified by `m:{a-z}={A-Z}'. The second line replaces dots and hyphens
+# in the given string by patterns matching any characters before them,
+# like the `r:|[.-]=* r:|=*'. To make this work, the function `_match_test'
+# would have to be changed to `(( MATCHERS <= 2 ))'
+#
+# The default implementation of this function is empty.
diff --git a/Completion/Base/_match_test b/Completion/Base/_match_test
new file mode 100644
index 000000000..e8b6e6424
--- /dev/null
+++ b/Completion/Base/_match_test
@@ -0,0 +1,15 @@
+#autoload
+
+# This function is called at the beginning of functions that do matching in
+# shell code. It should test the value of the `MATCHER' special parameter
+# and return non-zero if the calling function should try to generate matches
+# for the global match specification in use.
+#
+# This function gets one argument, the name of the function calling it.
+#
+# If you have a global match specification with more than one set of patterns
+# you may want to modify this function to return non-zero for all of your
+# match specifications and modify the function `_match_pattern' to build the
+# pattern to use in the calling function.
+
+(( MATCHER == 1 ))
diff --git a/Completion/Base/_precommand b/Completion/Base/_precommand
new file mode 100644
index 000000000..2cf661147
--- /dev/null
+++ b/Completion/Base/_precommand
@@ -0,0 +1,5 @@
+#defcomp - nohup nice eval time rusage noglob nocorrect exec
+
+[[ -position 1 -1 ]]
+
+_normal "$@"
diff --git a/Completion/Base/_redirect b/Completion/Base/_redirect
new file mode 100644
index 000000000..32113ad7c
--- /dev/null
+++ b/Completion/Base/_redirect
@@ -0,0 +1,3 @@
+#defcomp -redirect-
+
+_files
diff --git a/Completion/Base/_subscript b/Completion/Base/_subscript
new file mode 100644
index 000000000..2b827a117
--- /dev/null
+++ b/Completion/Base/_subscript
@@ -0,0 +1,4 @@
+#defcomp -subscript-
+
+_compalso -math- "$@"
+[[ ${(Pt)${COMMAND}} = assoc* ]] && complist -k "( ${(kP)${COMMAND}} )"
diff --git a/Completion/Base/_vars b/Completion/Base/_vars
new file mode 100644
index 000000000..7153b6f38
--- /dev/null
+++ b/Completion/Base/_vars
@@ -0,0 +1,3 @@
+#defcomp -math- getopts read unset vared
+
+complist -v
diff --git a/Completion/Builtins/_aliases b/Completion/Builtins/_aliases
new file mode 100644
index 000000000..1038a726e
--- /dev/null
+++ b/Completion/Builtins/_aliases
@@ -0,0 +1,3 @@
+#defcomp unalias
+
+complist -a
diff --git a/Completion/Builtins/_arrays b/Completion/Builtins/_arrays
new file mode 100644
index 000000000..cbeac7118
--- /dev/null
+++ b/Completion/Builtins/_arrays
@@ -0,0 +1,3 @@
+#defcomp shift
+
+complist -A
diff --git a/Completion/Builtins/_autoload b/Completion/Builtins/_autoload
new file mode 100644
index 000000000..4f506baeb
--- /dev/null
+++ b/Completion/Builtins/_autoload
@@ -0,0 +1,3 @@
+#defcomp autoload
+
+complist -s '${^fpath}/*(N:t)'
diff --git a/Completion/Builtins/_bg_jobs b/Completion/Builtins/_bg_jobs
new file mode 100644
index 000000000..511bb8308
--- /dev/null
+++ b/Completion/Builtins/_bg_jobs
@@ -0,0 +1,3 @@
+#defcomp bg
+
+complist -z -P '%'
diff --git a/Completion/Builtins/_bindkey b/Completion/Builtins/_bindkey
new file mode 100644
index 000000000..8eddeb2a8
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_builtin b/Completion/Builtins/_builtin
new file mode 100644
index 000000000..a967932ee
--- /dev/null
+++ b/Completion/Builtins/_builtin
@@ -0,0 +1,7 @@
+#defcomp builtin
+
+if [[ -position 2 -1 ]]; then
+ _normal "$@"
+else
+ complist -eB
+fi
diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd
new file mode 100644
index 000000000..f3ce67ec7
--- /dev/null
+++ b/Completion/Builtins/_cd
@@ -0,0 +1,3 @@
+#defcomp cd
+
+_files -W cdpath -g '*(-/)'
diff --git a/Completion/Builtins/_command b/Completion/Builtins/_command
new file mode 100644
index 000000000..b2812de25
--- /dev/null
+++ b/Completion/Builtins/_command
@@ -0,0 +1,7 @@
+#defcomp command
+
+if [[ -position 2 -1 ]]; then
+ _normal "$@"
+else
+ complist -em
+fi
diff --git a/Completion/Builtins/_dirs b/Completion/Builtins/_dirs
new file mode 100644
index 000000000..bc426e322
--- /dev/null
+++ b/Completion/Builtins/_dirs
@@ -0,0 +1,3 @@
+#defcomp rmdir df du dircmp
+
+_files -/
diff --git a/Completion/Builtins/_disable b/Completion/Builtins/_disable
new file mode 100644
index 000000000..063b65a7d
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_echotc b/Completion/Builtins/_echotc
new file mode 100644
index 000000000..85ebb97ce
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_enable b/Completion/Builtins/_enable
new file mode 100644
index 000000000..22ff53ee7
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_fc b/Completion/Builtins/_fc
new file mode 100644
index 000000000..f0d2c03fd
--- /dev/null
+++ b/Completion/Builtins/_fc
@@ -0,0 +1,7 @@
+#defcomp fc
+
+if [[ -mcurrent -1 -*e ]]; then
+ complist -c
+elif [[ -mcurrent -1 -[ARWI]## ]]; then
+ _files
+fi
diff --git a/Completion/Builtins/_functions b/Completion/Builtins/_functions
new file mode 100644
index 000000000..8a352ea08
--- /dev/null
+++ b/Completion/Builtins/_functions
@@ -0,0 +1,3 @@
+#defcomp unfunction
+
+complist -F
diff --git a/Completion/Builtins/_hash b/Completion/Builtins/_hash
new file mode 100644
index 000000000..171c5e2e8
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_jobs b/Completion/Builtins/_jobs
new file mode 100644
index 000000000..018883c61
--- /dev/null
+++ b/Completion/Builtins/_jobs
@@ -0,0 +1,3 @@
+#defcomp fg jobs
+
+complist -j -P '%'
diff --git a/Completion/Builtins/_kill b/Completion/Builtins/_kill
new file mode 100644
index 000000000..50796d36f
--- /dev/null
+++ b/Completion/Builtins/_kill
@@ -0,0 +1,11 @@
+#defcomp kill
+
+local list
+
+if [[ -iprefix '-' ]]; then
+ complist -k "($signals[1,-3])"
+else
+ complist -P '%' -j
+ list=("$(ps 2>/dev/null)")
+ complist -y '$list' -s '`ps 2>/dev/null | tail +2 | cut -c1-5`'
+fi
diff --git a/Completion/Builtins/_limits b/Completion/Builtins/_limits
new file mode 100644
index 000000000..35ccbe07e
--- /dev/null
+++ b/Completion/Builtins/_limits
@@ -0,0 +1,3 @@
+#defcomp limit unlimit
+
+complist -k "(${(j: :)${(f)$(limit)}%% *})"
diff --git a/Completion/Builtins/_sched b/Completion/Builtins/_sched
new file mode 100644
index 000000000..1e8ae3445
--- /dev/null
+++ b/Completion/Builtins/_sched
@@ -0,0 +1,3 @@
+#defcomp sched
+
+[[ -position 2 -1 ]] && _normal "$@"
diff --git a/Completion/Builtins/_set b/Completion/Builtins/_set
new file mode 100644
index 000000000..5597025bc
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_setopt b/Completion/Builtins/_setopt
new file mode 100644
index 000000000..4abb3ccee
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_source b/Completion/Builtins/_source
new file mode 100644
index 000000000..aae2c7320
--- /dev/null
+++ b/Completion/Builtins/_source
@@ -0,0 +1,7 @@
+#defcomp source
+
+if [[ -position 2 -1 ]]; then
+ _normal "$@"
+else
+ _files
+fi
diff --git a/Completion/Builtins/_trap b/Completion/Builtins/_trap
new file mode 100644
index 000000000..59e81c589
--- /dev/null
+++ b/Completion/Builtins/_trap
@@ -0,0 +1,7 @@
+#defcomp trap
+
+if [[ -position 1 ]]; then
+ complist -c
+else
+ complist -k signals
+fi
diff --git a/Completion/Builtins/_unhash b/Completion/Builtins/_unhash
new file mode 100644
index 000000000..fe40c25a2
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_unsetopt b/Completion/Builtins/_unsetopt
new file mode 100644
index 000000000..90d642b51
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_vars_eq b/Completion/Builtins/_vars_eq
new file mode 100644
index 000000000..fcbb0148c
--- /dev/null
+++ b/Completion/Builtins/_vars_eq
@@ -0,0 +1,3 @@
+#defcomp declare export integer local readonly typeset
+
+complist -v -q -S '='
diff --git a/Completion/Builtins/_wait b/Completion/Builtins/_wait
new file mode 100644
index 000000000..29a7f6002
--- /dev/null
+++ b/Completion/Builtins/_wait
@@ -0,0 +1,7 @@
+#defcomp wait
+
+local list
+
+complist -P '%' -j
+list=("$(ps 2>/dev/null)")
+complist -y '$list' -s '`ps 2>/dev/null | tail +2 | cut -c1-5`'
diff --git a/Completion/Builtins/_which b/Completion/Builtins/_which
new file mode 100644
index 000000000..324256e3d
--- /dev/null
+++ b/Completion/Builtins/_which
@@ -0,0 +1,3 @@
+#defcomp which whence where type
+
+complist -caF
diff --git a/Completion/Builtins/_zftp b/Completion/Builtins/_zftp
new file mode 100644
index 000000000..9be9c94db
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Builtins/_zle b/Completion/Builtins/_zle
new file mode 100644
index 000000000..bb1102e74
--- /dev/null
+++ b/Completion/Builtins/_zle
@@ -0,0 +1,7 @@
+#defcomp zle
+
+if [[ -word 1 -N && -position 3 ]]; then
+ complist -F
+else
+ complist -b
+fi
diff --git a/Completion/Builtins/_zmodload b/Completion/Builtins/_zmodload
new file mode 100644
index 000000000..112acb57c
--- /dev/null
+++ b/Completion/Builtins/_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/Completion/Commands/_correct_filename b/Completion/Commands/_correct_filename
new file mode 100644
index 000000000..edf1c65c2
--- /dev/null
+++ b/Completion/Commands/_correct_filename
@@ -0,0 +1,37 @@
+#defkeycomp complete-word \C-xc
+
+# Function to correct a filename. Can be used as a completion widget,
+# or as a function in its own right, in which case it will print the
+# corrected filename to standard output.
+#
+# You can adapt max_approx to the maximum number of mistakes
+# which are allowed in total.
+
+emulate -LR zsh
+setopt extendedglob
+
+local file="$PREFIX$SUFFIX" trylist
+integer approx max_approx=6
+
+[[ -z $WIDGET ]] && file=$1
+
+if [[ -e "$file" ]]; then
+ if [[ -n $WIDGET ]]; then
+ compadd "$file"
+ else
+ print "$file"
+ fi
+ return
+fi
+
+for (( approx = 1; approx <= max_approx; approx++ )); do
+ trylist=( (#a$approx)"$file"(N) )
+ (( $#trylist )) && break
+done
+(( $#trylist )) || return 1
+
+if [[ -n $WIDGET ]]; then
+ compadd -U "${trylist[@]}"
+else
+ print "${trylist[@]}"
+fi
diff --git a/Completion/Commands/_most_recent_file b/Completion/Commands/_most_recent_file
new file mode 100644
index 000000000..ff5645de5
--- /dev/null
+++ b/Completion/Commands/_most_recent_file
@@ -0,0 +1,4 @@
+#defkeycomp complete-word \C-xm
+local file
+file=($~PREFIX*$~SUFFIX(om[1]N))
+(( $#file )) && compadd -f $file
diff --git a/Completion/Core/_comp_parts b/Completion/Core/_comp_parts
new file mode 100644
index 000000000..7c24fd19d
--- /dev/null
+++ b/Completion/Core/_comp_parts
@@ -0,0 +1,147 @@
+#autoload
+
+# This function can be used to separately complete parts of strings
+# where each part may be one of a set of matches and different parts
+# have different sets.
+# Arguments are alternatingly arrays and separator strings. Arrays may
+# be given by name or literally as words separated by white space in
+# parentheses, e.g.:
+#
+# _comp_parts '(foo bar)' @ hosts
+#
+# This will make this function complete the strings in the array
+# `friends'. If the string on the line contains a `@', the substring
+# after it will be completed from the array `hosts'. Of course more
+# arrays may be given, each preceded by another separator string.
+#
+# This function understands the `-J group', `-V group', and
+# `-X explanation' options.
+#
+# This function does part of the matching itself and calls the functions
+# `_match_test' and `_match_pattern' for this.
+
+local str arr sep test testarr tmparr prefix suffixes matchers autosuffix
+local matchflags opt group expl
+
+# Test if we should use this function for the global matcher in use.
+
+_match_test _comp_parts || return
+
+# Get the options.
+
+group=()
+expl=()
+while getopts "J:V:X:" opt; do
+ case "$opt" in
+ [JV]) group=("-$opt" "$OPTARG");;
+ X) expl=(-X "$OPTARG");;
+ esac
+done
+shift OPTIND-1
+
+# Get the string from the line.
+
+str="$PREFIX$SUFFIX"
+prefix=""
+
+# Walk through the arguments to find the longest unambiguous prefix.
+
+while [[ $# -gt 1 ]]; do
+ # Get the next array and separator.
+ arr="$1"
+ sep="$2"
+
+ if [[ "$arr[1]" == '(' ]]; then
+ tmparr=( ${=arr[2,-2]} )
+ arr=tmparr
+ fi
+ # Is the separator on the line?
+ [[ "$str" != *${sep}* ]] && break
+
+ # Build a pattern matching the possible matches and get all these
+ # matches in an array.
+ test="${str%%${sep}*}"
+ matchflags=""
+ _match_pattern _comp_parts test matchflags
+ test="${matchflags}${test}"
+ testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
+
+ # If there are no matches we give up. If there is more than one
+ # match, this is the part we will complete.
+ (( $#testarr )) || return
+ [[ $#testarr -gt 1 ]] && break
+
+ # Only one match, add it to the prefix and skip over it in `str',
+ # continuing with the next array and separator.
+ prefix="${prefix}${testarr[1]}${sep}"
+ str="${str#*${sep}}"
+ shift 2
+done
+
+# Get the array to work upon.
+arr="$1"
+if [[ "$arr[1]" == '(' ]]; then
+ tmparr=( ${=arr[2,-2]} )
+ arr=tmparr
+fi
+if [[ $# -le 1 || "$str" != *${2}* ]]; then
+ # No more separators, build the matches.
+ matchflags=""
+ test="$str"
+ _match_pattern _comp_parts test matchflags
+ test="${matchflags}${test}"
+ testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
+fi
+
+[[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return
+
+# Now we build the suffixes to give to the completion code.
+shift
+matchers=()
+suffixes=("")
+autosuffix=()
+
+while [[ $# -gt 0 && "$str" == *${1}* ]]; do
+ # Remove anything up to the the suffix.
+ str="${str#*${1}}"
+
+ # Again, we get the string from the line up to the next separator
+ # and build a pattern from it.
+ if [[ $# -gt 2 ]]; then
+ test="${str%%${3}*}"
+ else
+ test="$str"
+ fi
+ matchflags=""
+ _match_pattern _comp_parts test matchflags
+ test="${matchflags}${test}"
+
+ # We incrementally add suffixes by appending to them the seperators
+ # and the strings from the next array that match the pattern we built.
+
+ arr="$2"
+ if [[ "$arr[1]" == '(' ]]; then
+ tmparr=( ${=arr[2,-2]} )
+ arr=tmparr
+ fi
+ suffixes=("${^suffixes[@]}${1}${(@M)^${(@P)arr}:#${~test}*}")
+
+ # We want the completion code to generate the most specific suffix
+ # for us, so we collect matching specifications that allow partial
+ # word matching before the separators on the fly.
+ matchers=("$matchers[@]" "r:|${1}=*")
+ shift 2
+done
+
+# If we were given at least one more separator we make the completion
+# code offer it by appending it as a autoremovable suffix.
+(( $# )) && autosuffix=(-qS "$1")
+
+# If we have collected matching specifications, we build an array
+# from it that can be used as arguments to `compadd'.
+[[ $#matchers -gt 0 ]] && matchers=(-M "$matchers")
+
+# Add the matches for each of the suffixes.
+for i in "$suffixes[@]"; do
+ compadd "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" -p "$prefix" -s "$i" - "$testarr[@]"
+done
diff --git a/Completion/Core/_compalso b/Completion/Core/_compalso
new file mode 100644
index 000000000..23a40e2d0
--- /dev/null
+++ b/Completion/Core/_compalso
@@ -0,0 +1,13 @@
+#autoload
+
+# This searches $1 in the array for normal completions and calls the result.
+# It is used to include completions for another command or special context
+# into the list generated by the calling function.
+# For example the function for `-subscript-' could call this as in
+# `_compalso -math- "$@"' to get the completions that would be generated
+# for a mathematical context.
+
+local tmp
+
+tmp="$_comps[$1]"
+[[ -z "$tmp" ]] || "$tmp" "$@"
diff --git a/Completion/Core/_files b/Completion/Core/_files
new file mode 100644
index 000000000..d2cce35e7
--- /dev/null
+++ b/Completion/Core/_files
@@ -0,0 +1,26 @@
+#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 "$@"
+
+if [[ $# -ne 0 && -nmatches nm ]]; then
+ local opt opts
+
+ # We didn't get any matches for those types of files described by
+ # the `-g' or `-/' option. Now we try it again accepting all files.
+ # First we get those options that we have to use even if then. If
+ # we find out that the `-f' option was given, we already accepted
+ # all files and give up immediatly.
+
+ opts=()
+ while getopts "P:S:W:F:J:V:X:f/g:" opt; do
+ [[ "$opt" = f ]] && return
+ [[ "$opt" = [PSWFJVX] ]] && opts=("$opts[@]" "-$opt" "$OPTARG")
+ done
+
+ _path_files "$opts[@]"
+fi
diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete
new file mode 100644
index 000000000..c7f5a5a96
--- /dev/null
+++ b/Completion/Core/_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/Completion/Core/_normal b/Completion/Core/_normal
new file mode 100644
index 000000000..19da6d79b
--- /dev/null
+++ b/Completion/Core/_normal
@@ -0,0 +1,54 @@
+#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.
+
+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
+
+# 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/Completion/Core/_path_files b/Completion/Core/_path_files
new file mode 100644
index 000000000..83b6e8a09
--- /dev/null
+++ b/Completion/Core/_path_files
@@ -0,0 +1,311 @@
+#autoload
+
+# Utility function for in-path completion.
+# Supported arguments are: `-f', `-/', `-g <patterns>', `-J <group>',
+# `-V <group>', `-W paths', `-X explanation', and `-F <ignore>'. All but
+# the last have the same syntax and meaning as for `complist'. The
+# `-F <ignore>' option may be used to give a list of suffixes either by
+# giving the name of an array or literally by giving them in a string
+# surrounded by parentheses. Files with one of the suffixes thus given
+# are treated like files with one of the suffixes in the `fignore' array
+# in normal completion.
+#
+# This function uses the helper functions `_match_test' and `_match_pattern'.
+
+# First see if we should generate matches for the global matcher in use.
+
+_match_test _path_files || return
+
+# Yes, so...
+
+local nm prepaths str linepath realpath donepath patstr prepath testpath rest
+local tmp1 collect tmp2 suffixes i ignore matchflags opt group sopt pats gopt
+local addpfx addsfx expl
+
+setopt localoptions nullglob rcexpandparam globdots extendedglob
+unsetopt markdirs globsubst shwordsplit nounset
+
+prepaths=('')
+ignore=()
+group=()
+sopt='-'
+gopt=''
+pats=()
+addpfx=()
+addsfx=()
+expl=()
+
+# Get the options.
+
+while getopts "P:S:W:F:J:V:X:f/g:" opt; do
+ case "$opt" in
+ P) addpfx=(-P "$OPTARG")
+ ;;
+ S) addsfx=(-S "$OPTARG")
+ ;;
+ W) tmp1="$OPTARG"
+ if [[ "$tmp1[1]" = '(' ]]; then
+ prepaths=( ${^=tmp1[2,-2]}/ )
+ else
+ prepaths=( ${(P)=${tmp1}} )
+ (( ! $#prepaths )) && prepaths=( ${tmp1}/ )
+ fi
+ (( ! $#prepaths )) && prepaths=( '' )
+ ;;
+ F) tmp1="$OPTARG"
+ if [[ "$tmp1[1]" = '(' ]]; then
+ ignore=( ${^=tmp1[2,-2]}/ )
+ else
+ ignore=( ${(P)${tmp1}} )
+ fi
+ (( $#ignore )) && ignore=(-F "( $ignore )")
+ ;;
+ [JV]) group=("-$opt" "$OPTARG")
+ ;;
+ X) expl=(-X "$OPTARG")
+ ;;
+ f) sopt="${sopt}f"
+ pats=("$pats[@]" '*')
+ ;;
+ /) sopt="${sopt}/"
+ pats=("$pats[@]" '*(-/)')
+ ;;
+ g) gopt='-g'
+ pats=("$pats[@]" ${=OPTARG})
+ ;;
+ esac
+done
+
+# If we were given no file selection option, we behave as if we were given
+# a `-f'.
+
+if [[ "$sopt" = - ]]; then
+ if [[ -z "$gopt" ]]; then
+ sopt='-f'
+ pats=('*')
+ else
+ unset sopt
+ fi
+fi
+
+# str holds the whole string from the command line with a `*' between
+# the prefix and the suffix.
+
+str="${PREFIX:q}*${SUFFIX:q}"
+
+# If the string began with a `~', the quoting turned this into `\~',
+# remove the slash.
+
+[[ "$str" = \\\~* ]] && str="$str[2,-1]"
+
+# We will first try normal completion called with `complist', but only if we
+# weren't given a `-F' option.
+
+if (( ! $#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 [[ -z "$gopt" ]]; then
+ complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt
+ else
+ complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt -g "$pats"
+ fi
+
+ # If this generated any matches, we don't want to do in-path completion.
+
+ [[ -nmatches nm ]] || return
+
+ # No `-F' option, so we want to use `fignore'.
+
+ ignore=(-F fignore)
+fi
+
+# 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\=$linepath
+ 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 by calling `_match_pattern'.
+patstr="$str"
+matchflags=""
+_match_pattern _path_files patstr matchflags
+
+# We almost expect the pattern to have changed `..' into `*.*.', `/.' into
+# `/*.', and probably to contain two or more consecutive `*'s. Since these
+# have special meaning for globbing, we remove them. But before that, we
+# add the pattern for matching any characters before a slash.
+
+patstr="$patstr:gs-/-*/-:gs/*.*.//:gs-/*.-/.-:gs/**/*/"
+
+# 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}${~matchflags}${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$^pats )
+ 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}/${~matchflags}${~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}/${~matchflags}${~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 "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -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$^pats )
+ suffixes=( "${(@)suffixes:gs.**.*.}" )
+ tmp2=( ${~tmp1}${~matchflags}${~suffixes} )
+ if [[ $#tmp2 -eq 0 && "$sopt" = */* ]]; then
+ [[ "$testpath[-1]" = / ]] && testpath="$testpath[1,-2]"
+ compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -f - "$linepath$testpath"
+ else
+ compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -p "$linepath$testpath" -W "$prepath$realpath$testpath" -f "$ignore[@]" - ${(@)tmp2#$tmp1}
+ fi
+done
diff --git a/Completion/Core/compdump b/Completion/Core/compdump
new file mode 100644
index 000000000..8be096f50
--- /dev/null
+++ b/Completion/Core/compdump
@@ -0,0 +1,89 @@
+# This is a file to be sourced to dump the definitions for new-style
+# completion defined by 'compinit' in the same directory. The output
+# should be directed into the "compinit.dump" in the same directory as
+# compinit. If you rename init, just stick .dump onto the end of whatever
+# you have called it and put it in the same directory. This is handled
+# automatically if you invoke compinit with the option -d.
+#
+# 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. Again, compinit -d handles this
+# automatically.
+#
+# It relies on KSH_ARRAYS not being set.
+
+# Print the number of files used for completion. This is used in compinit
+# to see if auto-dump should re-dump the dump-file.
+
+_d_file=${COMPDUMP-${0:h}/compinit.dump}
+
+typeset -U _d_files
+_d_files=( ${^~fpath}/_*~*~(N:t) )
+
+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 ${(ok)_comps}; do
+ print -r - "'${_d_f//\'/'\\''}'" "'${_comps[$_d_f]//\'/'\\''}'"
+done >> $_d_file
+print ")" >> $_d_file
+
+print "\n_patcomps=(" >> $_d_file
+for _d_f in "$_patcomps[@]"; do
+ print -r - "'${_d_f//\'/'\\''}'"
+done >> $_d_file
+print ")" >> $_d_file
+
+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 '_*' | sort |
+while read -rA _d_line; do
+ [[ ${_d_line[2]} = function ]] && print -r - ${_d_line[1]%:}
+done))
+
+# print them out: about five to a line looks neat
+
+while (( $#_d_als )); do
+ print -n autoload
+ for (( _i = 0; _i < 5; _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/Completion/Core/compinit b/Completion/Core/compinit
new file mode 100644
index 000000000..ec5867838
--- /dev/null
+++ b/Completion/Core/compinit
@@ -0,0 +1,269 @@
+# Initialisation for new style completion. This mainly contains some helper
+# function and aliases. Everything else is split into different files in this
+# 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 a underscores (like `_setopt).
+# The first line of these files will be read and has to say what should be
+# done with its contents:
+#
+# `#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
+#
+# `#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
+#
+# `#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,
+# expand-or-complete, expand-or-complete-prefix, list-choices,
+# menu-complete, menu-expand-or-complete, or reverse-menu-complete).
+# This creates a widget behaving like <style> so that the
+# completions are chosen as given in the the rest of the file,
+# 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.
+#
+# `#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 `compdump' for how to speed up initialiation.
+#
+# If you are using global matching specifications with `compctl -M ...'
+# have a look at the files `_match_test' and `_match_pattern'. To make
+# all the example functions use matching as specified with `-M' these
+# need some editing.
+#
+# If we got the `-d'-flag, we will automatically dump the new state (at
+# the end).
+
+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
+_patcomps=()
+
+# This function is used to register or delete completion functions. For
+# registering completion functions, it is invoked with the name of the
+# function as it's first argument (after the options). The other
+# arguments depend on what type of completion function is defined. If
+# none of the `-p' and `-k' options is given a function for a command is
+# defined. The arguments after the function name are then interpreted as
+# the names of the command for which the function generates matches.
+# With the `-p' option a function for a name pattern is defined. This
+# function will be invoked when completing for a command whose name
+# matches the pattern given as argument after the function name (in this
+# case only one argument is accepted).
+# With the `-k' option a function for a special completion keys is
+# defined and immediatly bound to those keys. Here, the extra arguments
+# are the name of one of the builtin completion widgets and any number
+# of key specifications as accepted by the `bindkey' builtin.
+# In any case the `-a' option may be given which makes the function
+# whose name is given as the first argument be autoloaded. When defining
+# a function for command names the `-n' option may be given and keeps
+# the definitions from overriding any previous definitions for the
+# commands.
+# For deleting definitions, the `-d' option must be given. Without the
+# `-p' option, this deletes definitions for functions for the commands
+# whose names are given as arguments. If combined with the `-p' option
+# it deletes the definitions for the patterns given as argument.
+# The `-d' option may not be combined with the `-k' option, i.e.
+# definitions for key function can not be removed.
+#
+# Examples:
+#
+# compdef -a foo bar baz
+# make the completion for the commands `bar' and `baz' use the
+# function `foo' and make this function be autoloaded
+#
+# compdef -p foo 'c*'
+# make completion for all command whose name begins with a `c'
+# generate matches by calling the function `foo' before generating
+# matches defined for the command itself
+#
+# compdef -k foo list-choices '^X^M' '\C-xm'
+# make the function `foo' be invoked when typing `Control-X Control-M'
+# or `Control-X m'; the function should generate matches and will
+# behave like the `list-choices' builtin widget
+#
+# compdef -d bar baz
+# delete the definitions for the command names `bar' and `baz'
+
+compdef() {
+ local opt autol type func delete new i
+
+ # Get the options.
+
+ while getopts "anpkd" opt; do
+ case "$opt" in
+ a) autol=yes;;
+ n) new=yes;;
+ [pk]) if [[ -n "$type" ]]; then
+ # Error if both `-p' and `-k' are given (or one of them
+ # twice).
+ echo "$0: type already set to $type"
+ return 1
+ fi
+ if [[ "$opt" = p ]]; then
+ type=pattern
+ else
+ type=key
+ fi
+ ;;
+ d) delete=yes;;
+ esac
+ done
+ shift OPTIND-1
+
+ if [[ -z "$delete" ]]; then
+ # Adding definitions, first get the name of the function name
+ # and probably do autoloading.
+
+ func="$1"
+ [[ -n "$autol" ]] && autoload "$func"
+ shift
+
+ case "$type" in
+ pattern)
+ if [[ $# -gt 1 ]]; then
+ echo "$0: only one pattern allowed"
+ return 1
+ fi
+ # Patterns are stored in strings like `c* foo', with a space
+ # between the pattern and the function name.
+
+ _patcomps=("$_patcomps[@]" "$1 $func")
+ ;;
+ key)
+ if [[ $# -lt 2 ]]; then
+ echo "$0: missing keys"
+ return 1
+ fi
+
+ # Define the widget.
+ zle -C "$func" "$1" "$func"
+ shift
+
+ # And bind the keys...
+ for i; do
+ bindkey "$i" "$func"
+ done
+ ;;
+ *)
+ # For commands store the function name in the `_comps'
+ # associative array, command names as keys.
+ for i; do
+ [[ -z "$new" || "${+_comps[$i]}" -eq 0 ]] && _comps[$i]="$func"
+ done
+ ;;
+ esac
+ else
+ # Handle the `-d' option, deleting.
+ case "$type" in
+ pattern)
+ # Note the space.
+ for i; do
+ _patcomps=("${(@)patcomps:#$i *}")
+ done
+ ;;
+ key)
+ # Oops, cannot do that yet.
+
+ echo "$0: cannot restore key bindings"
+ return 1
+ ;;
+ *)
+ # Deleting definitons for command is even simpler.
+ for i; do
+ unset "_comps[$i]"
+ done
+ esac
+ fi
+}
+
+# Now we automatically make the definition files autoloaded.
+
+# First we get the name of a dump file if this will be used.
+
+: ${COMPDUMP:=$0.dump}
+
+if [[ ! -o extendedglob ]]; then
+ _i_noextglob=yes
+ setopt extendedglob
+fi
+
+typeset -U _i_files
+_i_files=( ${^~fpath}/_*~*~(N:t) )
+_i_initname=$0
+_i_done=''
+
+# If we have a dump file, load it.
+
+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
+ compdef -na "${_i_file:t}" "${_i_line[@]}"
+ elif [[ $_i_tag = '#defpatcomp' ]]; then
+ compdef -pa "${_i_file:t}" "${_i_line[@]}"
+ elif [[ $_i_tag = '#defkeycomp' ]]; then
+ compdef -ka "${_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
+
+ # If autodumping was requested, do it now.
+
+ (( _i_autodump )) && builtin . ${_i_initname:h}/compdump
+fi
+
+[[ -z "$_i_noextglob" ]] || unsetopt extendedglob
+
+unset _i_files _i_initname _i_done _i_autodump _i_noextglob
diff --git a/Completion/README b/Completion/README
new file mode 100644
index 000000000..ac2accfca
--- /dev/null
+++ b/Completion/README
@@ -0,0 +1,107 @@
+The subdirectories contain code for the new function-based completion
+system. Broadly speaking, this uses shell functions defined for each
+command to determine how the arguments of a command should be completed.
+
+You should copy all the files you need or want to a directory of your own,
+which should be included in your autoload path as defined by $fpath. Then
+in your .zshrc you should source the file which appears here in
+Core/compinit. It is recommnded that you use the -d option, which outputs
+a file containing the necessary variables, bindkeys etc., making later
+loading much faster. For example,
+ [[ -f ~/completion/compinit ]] && . ~/completion/compinit -d
+This will rebind any keys which do completion to use the new system.
+For more detailed instructions, including how to add new completions, see
+the top of Core/compinit .
+
+The subdirectories contain:
+
+Core:
+ The basic functions and files to be sourced. You will certainly need
+ these, and will most likely not feel like altering them (or, in some
+ cases, even reading them, unless you are a shell wizard). The files are:
+ compinit
+ As already described, this is not a function, but is sourced once
+ (with the `source' or `.' commands) to set up the completion system.
+ compdump
+ This dumps the completions status for faster initialisation. The
+ easiest way of doing this is to use the -d option to compinit rather
+ than calling compdump directly.
+ _comp_parts
+ Utility used for completing words with multiple separate parts, such as
+ `<user>@<host>'
+ _compalso
+ Utility for calling a function to add additional completions to an
+ already existing set.
+ _files
+ A frontend to _path_files which will default to any old file if the
+ specified file was not found.
+ _main_complete
+ The main entry point called by the key bindings which compinit sets
+ up (the main `completion widget' in zsh jargon).
+ _normal
+ The function called by _main_complete to handle the most common
+ cases, such as completing a command name or its arguments. This
+ function dispatches to the various other functions for individual
+ commands. (Actually, the system is fairly context-sensitive, so
+ it is wider than just command+argument.)
+ _path_files
+ The function usually called to complete filenames and directories. It
+ replaces the standard -f and -/ options for the basic completion
+ commands: it can do various extra tricks, such as expanding a whole
+ path at once, e.g. F/C/C/_p<TAB> -> Functions/Completion/Core/_path_files
+Base:
+ You will almost certainly want these files, too, which handle standard
+ tasks like completing files. However, you may want to edit them for
+ your own particular setup. Files are:
+ _command_names
+ This handles completion of the command word, i.e. the first thing
+ on the command line. You may want to alter this, for example,
+ to complete parameters to assign to.
+ _condition
+ This handles completing inside [[ ... ]] .
+ _default
+ This handles completion of command arguments when no special function
+ exists. Usually this means completing files, but you can modify this
+ as you wish.
+ _match_pattern
+ _match_test
+ These are used by Base/_path_files (and hence also Base/_files) for
+ file completion with control over matching (whether to complete
+ case-insensitively, or to allow insertion before `.', etc.) See
+ _match_test for instructions. Note _path_files expects these files
+ to be present.
+ _precommand
+ Allows completion when the first word on the line has to be ignored,
+ for example `noglob ...' should ignore the noglob and just complete
+ as if it wasn't there. Add other such commands to the top line.
+ _redirect
+ Completes after `<' or `<': this version calls _files.
+ _subscript
+ For completion in subscripts of parameters, e.g $foo[...].
+ _vars
+ Completion for commands which need variables (so this could also be in
+ the Builtins directory), but also in math environments such as ((...)).
+Builtins:
+ Define completions for various shell builtins. The top line of each file
+ says which builtins they apply to; in many cases you can guess from the
+ name. Note in particular that _zftp defines completions for all commands
+ beginning `zf', not just for the module command zftp. This is only
+ really useful if you use zftp with the zf* function suite (zfopen, zfget,
+ ...).
+User:
+ This contains a pot pourri of completions for various external commands.
+ Not all will work unmodified on your system.
+Commands:
+ These functions define separate completion commands which do not use
+ the usual context information, and hence have to be bound separately
+ to keys. As they appear, they have bindings which you can change or
+ delete by altering the top line of the file. To bind a function
+ (strictly speaking, the corresponding completion widget) yourself
+ after completion is loaded, use `bindkey '<key-string>' <_function_name>'.
+ The files are:
+ _correct_filename, bound to \C-xc
+ Correct the word under the cursor as a filename. This is significantly
+ more powerful than the standard \e$ (spell-word) binding.
+ _most_recent_file, bound to \C-xm
+ Insert the name of the most recent file matching the pattern
+ so far on the command line.
diff --git a/Completion/User/_a2ps b/Completion/User/_a2ps
new file mode 100644
index 000000000..9aa9d3d99
--- /dev/null
+++ b/Completion/User/_a2ps
@@ -0,0 +1,22 @@
+#defcomp a2ps
+
+if [[ -prefix -- ]]; then
+ _comp_parts '(--borders --compact --truncate-lines --interpret
+ --print-anyway --delegate)' '=' '(yes no)'
+ _comp_parts '(--major)' '=' '(rows columns)'
+ _comp_parts '(--end-of-line)' '=' '(r n nr rn any)'
+
+ complist -S= -k '(--medium --columns --rows --line-numbers
+ --font-size --lines-per-page --chars-per-line
+ --tabsize --non-printable-format --encoding
+ --title --stdin --prologue --highlight-level
+ --strip-level --output --version-control --suffix
+ --printer --copies --sides --page-prefeed
+ --no-page-prefeed)'
+ complist -qS= -k '(--margin --header --underlay --left-title
+ --right-title --left-footer --footer --right-footer
+ --pages --pretty-print)'
+ complist -k '(--landscape --portrait --catman --no-header)'
+else
+ _files -F fignore -g "*~*.ps"
+fi
diff --git a/Completion/User/_compress b/Completion/User/_compress
new file mode 100644
index 000000000..860aeb5b0
--- /dev/null
+++ b/Completion/User/_compress
@@ -0,0 +1,3 @@
+#defcomp compress
+
+_files -g '*~*.Z'
diff --git a/Completion/User/_configure b/Completion/User/_configure
new file mode 100644
index 000000000..de8d5fba5
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_dd b/Completion/User/_dd
new file mode 100644
index 000000000..2458541ea
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_dvi b/Completion/User/_dvi
new file mode 100644
index 000000000..bb2fc293e
--- /dev/null
+++ b/Completion/User/_dvi
@@ -0,0 +1,3 @@
+#defcomp xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype
+
+_files -g '*.(dvi|DVI)'
diff --git a/Completion/User/_find b/Completion/User/_find
new file mode 100644
index 000000000..ca4f79908
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_gunzip b/Completion/User/_gunzip
new file mode 100644
index 000000000..35a27e774
--- /dev/null
+++ b/Completion/User/_gunzip
@@ -0,0 +1,3 @@
+#defcomp gunzip zcat
+
+_files -g '*.[gG][z]'
diff --git a/Completion/User/_gzip b/Completion/User/_gzip
new file mode 100644
index 000000000..3cda1e4ed
--- /dev/null
+++ b/Completion/User/_gzip
@@ -0,0 +1,3 @@
+#defcomp gzip
+
+_files -g '*~*.[gG][zZ]'
diff --git a/Completion/User/_hosts b/Completion/User/_hosts
new file mode 100644
index 000000000..3acc327ac
--- /dev/null
+++ b/Completion/User/_hosts
@@ -0,0 +1,3 @@
+#defcomp ftp ncftp ping rwho rup xping traceroute nslookup
+
+complist -k hosts
diff --git a/Completion/User/_make b/Completion/User/_make
new file mode 100644
index 000000000..d576b0308
--- /dev/null
+++ b/Completion/User/_make
@@ -0,0 +1,3 @@
+#defcomp make gmake pmake
+
+complist -s "\$(awk '/^[a-zA-Z0-9][^/ ]+:/ {print \$1}' FS=: [mM]akefile)"
diff --git a/Completion/User/_man b/Completion/User/_man
new file mode 100644
index 000000000..8204fba0b
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_mh b/Completion/User/_mh
new file mode 100644
index 000000000..67ce49fd2
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_pdf b/Completion/User/_pdf
new file mode 100644
index 000000000..7d7756c3e
--- /dev/null
+++ b/Completion/User/_pdf
@@ -0,0 +1,3 @@
+function acroread
+
+_files -g '*.(pdf|PDF)'
diff --git a/Completion/User/_ps b/Completion/User/_ps
new file mode 100644
index 000000000..6bc0643b2
--- /dev/null
+++ b/Completion/User/_ps
@@ -0,0 +1,3 @@
+#defcomp gs ghostview gview psnup psselect pswrap pstops pstruct lpr
+
+_files -g '*([pP][sS]|eps)'
diff --git a/Completion/User/_rcs b/Completion/User/_rcs
new file mode 100644
index 000000000..537db6278
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_rlogin b/Completion/User/_rlogin
new file mode 100644
index 000000000..e36554f23
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_strip b/Completion/User/_strip
new file mode 100644
index 000000000..6962ac455
--- /dev/null
+++ b/Completion/User/_strip
@@ -0,0 +1,2 @@
+#defcomp strip
+_files -g '*(*)'
diff --git a/Completion/User/_stty b/Completion/User/_stty
new file mode 100644
index 000000000..6b54b5007
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_tar b/Completion/User/_tar
new file mode 100644
index 000000000..91767e44d
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_tex b/Completion/User/_tex
new file mode 100644
index 000000000..1f645e2a2
--- /dev/null
+++ b/Completion/User/_tex
@@ -0,0 +1,3 @@
+#defcomp tex latex slitex
+
+_files -g '*.(tex|TEX|texinfo|texi)'
diff --git a/Completion/User/_uncompress b/Completion/User/_uncompress
new file mode 100644
index 000000000..e25805d50
--- /dev/null
+++ b/Completion/User/_uncompress
@@ -0,0 +1,3 @@
+#defcomp uncompress zmore
+
+_files -g '*.Z'
diff --git a/Completion/User/_x_options b/Completion/User/_x_options
new file mode 100644
index 000000000..cc469286d
--- /dev/null
+++ b/Completion/User/_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/Completion/User/_xfig b/Completion/User/_xfig
new file mode 100644
index 000000000..fcd2bba9f
--- /dev/null
+++ b/Completion/User/_xfig
@@ -0,0 +1,3 @@
+#defcomp xfig
+
+_files -g '*.fig'
diff --git a/Config/version.mk b/Config/version.mk
index 3e36faf5c..496dea13a 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-9
-VERSION_DATE='February 18, 1999'
+VERSION=3.1.5-pws-10
+VERSION_DATE='February 26, 1999'
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index 2cb12e2c2..4208317fb 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -162,7 +162,7 @@ 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(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ])
xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ])
item([ tt(-M) var(match-spec) ] [ tt(--) ] [ var(words) ... ])(
@@ -182,14 +182,17 @@ 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
-...
+This gives a string that should be inserted in the line before the
+match but that should not appear in the list of matches.
)
item(tt(-s) var(hidden-suffix))(
-...
+Like `tt(-p)', but gives a string to insert after the match.
)
item(tt(-i) var(ignored-prefix))(
-...
+This gives a string to insert into the command line just before any
+string given with the `tt(-P)' option. Without `tt(-P)' the string is
+inserted before the string given with `tt(-p)' or directly before the
+match.
)
item(tt(-J) var(name))(
As for tt(compctl) and tt(complist) this gives the name of the group
@@ -198,6 +201,10 @@ of matches the words should be stored in.
item(tt(-V) var(name))(
Like tt(-J) but naming a unsorted group.
)
+item(tt(-X) var(explanation))(
+The var(explanation) string will be printed with the list of matches,
+as for tt(compctl -X).
+)
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
@@ -382,15 +389,19 @@ 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)
+true if the cursor is after a word that is equal to var(string); this
+removes all words up to and including the matched word from the
+positional parameters
)
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
+there is also a word that is equal to var(string2), this is true only
+if the cursor is before it; as a side effect, all words before
+var(string1) and after var(string2) (both inclusive) are removed from
+the positional parameters
)
item(tt(-mbetween) var(pattern1) var(pattern2))(
like tt(-between) but using pattern matching
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index ccf245367..fa56bc0af 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -869,7 +869,7 @@ can be specified by separating two characters by a `tt(-)'.
A `tt(-)' or `tt(])' may be matched by including it as the
first character in the list.
There are also several named classes of characters, in the form
-`tt([:)var(name)(tt:])' with the following meanings: `tt([:alnum:])'
+`tt([:)var(name)tt(:])' with the following meanings: `tt([:alnum:])'
alphanumeric, `tt([:alpha:])' alphabetic,
`tt([:blank:])' space or tab,
`tt([:cntrl:])' control character, `tt([:digit:])' decimal
@@ -988,12 +988,18 @@ item(I)(
Case sensitive: locally negates the effect of tt(i) or tt(l) from
that point on.
)
+item(tt(a)var(num))(
+Approximate matching: var(num) errors are allowed in the string matched by
+the pattern. The rules for this are described in the next subsection.
+)
enditem()
For example, the test string tt(fooxx) can be matched by the pattern
tt(LPAR()#i)tt(RPAR()FOOXX), but not by tt(LPAR()#l)tt(RPAR()FOOXX),
tt(LPAR()#i)tt(RPAR()FOO)tt(LPAR()#I)tt(RPAR()XX) or
-tt(LPAR()LPAR()#i)tt(RPAR()FOOX)tt(RPAR()X).
+tt(LPAR()LPAR()#i)tt(RPAR()FOOX)tt(RPAR()X). The string
+tt(LPAR()#ia2)tt(RPAR()readme) specifies case-insensitive matching of
+tt(readme) with up to two errors.
When using the ksh syntax for grouping both tt(KSH_GLOB) and
tt(EXTENDED_GLOB) must be set and the left parenthesis should be
@@ -1004,6 +1010,61 @@ examining whole paths case-insensitively every directory must be
searched for all files which match, so that a pattern of the form
tt(LPAR()#i)tt(RPAR()/foo/bar/...) is potentially slow.
+subsect(Approximate Matching)
+When matching approximately, the shell keeps a count of the errors found,
+which cannot exceed the number specified in the
+tt(LPAR()#a)var(num)tt(RPAR()) flags. Four types of error are recognised:
+
+startitem()
+item(1.)(
+Different characters, as in tt(fooxbar) and tt(fooybar).
+)
+item(2.)(
+Transposition of characters, as in tt(banana) and tt(abnana).
+)
+item(3.)(
+A character missing in the target string, as with the pattern tt(road) and
+target string tt(rod).
+)
+item(4.)(
+An extra character appearing in the target string, as with tt(stove)
+and tt(strove).
+)
+enditem()
+
+Thus, the pattern tt(LPAR()#a3)tt(RPAR()abcd) matches tt(dcba), with the
+errors occurring by using the first rule twice and the second once,
+grouping the string as tt([d][cb][a]) and tt([a][bc][d]).
+
+Non-literal parts of the pattern must match exactly, including characters
+in character ranges: hence tt(LPAR()#a1)tt(RPAR()???) matches strings of
+length four, by applying rule 4 to an empty part of the pattern, but not
+strings of length three, since all the tt(?) must match. Other characters
+which must match exactly are initial dots in filenames (unless the
+tt(GLOB_DOTS) option is set), and all slashes in file names, so that
+tt(a/bc) is two errors from tt(ab/c) (the slash cannot be transposed with
+another character). Similarly, errors are counted separately for
+non-contiguous strings in the pattern, so that tt(LPAR()ab|cd)tt(RPAR()ef)
+is two errors from tt(aebf).
+
+When using exclusion via the tt(~) operator, approximate matching is
+treated entirely separately for the excluded part and must be activated
+separately. Thus, tt(LPAR()#a1)tt(RPAR()README~READ_ME) matches
+tt(READ.ME) but not tt(READ_ME), as the trailing tt(READ_ME) is matched
+without approximation. However,
+tt(LPAR()#a1)tt(RPAR()README~LPAR()#a1)tt(RPAR()READ_ME)
+does not match any pattern of the form tt(READ)var(?)tt(ME) as all
+such forms are now excluded.
+
+Apart from exclusions, there is only one overall error count; however, the
+maximum errors allowed may be altered locally, and this can be delimited by
+grouping. For example,
+tt(LPAR()#a1)tt(RPAR()cat)tt(LPAR()LPAR()#a0)tt(RPAR()dog)tt(RPAR()fox)
+allows one error in total, which may not occur in the tt(dog) section, and
+the pattern
+tt(LPAR()#a1)tt(RPAR()cat)tt(LPAR()#a0)tt(RPAR()dog)tt(LPAR()#a1)tt(RPAR()fox)
+is equivalent.
+
subsect(Recursive Globbing)
A pathname component of the form `tt(LPAR())var(foo)tt(/RPAR()#)'
matches a path consisting of zero or more directories
diff --git a/Doc/Zsh/guide.yo b/Doc/Zsh/guide.yo
index e0005e339..96e12fc58 100644
--- a/Doc/Zsh/guide.yo
+++ b/Doc/Zsh/guide.yo
@@ -144,8 +144,8 @@ then be processed with bf(dvips) and optionally bf(gs) (Ghostscript) to
produce a nicely formatted printed guide.
)
item(The HTML guide)(
-Mark Borges, tt(<mdb@cdc.noaa.gov), maintains an HTML version of this
-guide at tt(http://www.peak.org/zsh/Doc/zsh_toc.html).
+An HTML version of this guide is available at the Zsh web site via
+tt(http://sunsite.auc.dk/zsh/Doc/index.html).
(The HTML version is produced with bf(texi2html), which may be obtained
from tt(http://wwwcn.cern.ch/dci/texi2html/). The command is
`tt(texi2html -split_chapter -expandinfo zsh.texi)'.)
diff --git a/Misc/globtests b/Misc/globtests
index 9a12a11e1..3bd634ca9 100755
--- a/Misc/globtests
+++ b/Misc/globtests
@@ -103,6 +103,7 @@ t :] [:]]#
t [ [[]
t ] []]
t [] [^]]]
+# Case insensitive matching
t fooxx (#i)FOOXX
f fooxx (#l)FOOXX
t FOOXX (#l)fooxx
@@ -113,5 +114,36 @@ f fooxx ((#i)FOOX)X
f BAR (bar|(#i)foo)
t FOO (bar|(#i)foo)
t Modules (#i)*m*
+# Approximate matching
+t READ.ME (#ia1)readme
+f READ..ME (#ia1)readme
+t README (#ia1)readm
+t READM (#ia1)readme
+t README (#ia1)eadme
+t EADME (#ia1)readme
+t READEM (#ia1)readme
+f ADME (#ia1)readme
+f README (#ia1)read
+t bob (#a1)[b][b]
+f bob (#a1)[b][b]a
+t bob (#a1)[b]o[b]a
+f bob (#a1)[c]o[b]
+t abcd (#a2)XbcX
+t abcd (#a2)ad
+t ad (#a2)abcd
+t abcd (#a2)bd
+t bd (#a2)abcd
+t badc (#a2)abcd
+# This next one is a little tricky: a[d]bc[] = a[]bc[d]
+t adbc (#a2)abcd
+f dcba (#a2)abcd
+# the next one is [d][cb][a] = [a][bc][d] with a transposition
+t dcba (#a3)abcd
+t aabaXaaabY (#a1)(a#b)#Y
+t aabaXaaabY (#a1)(a#b)(a#b)Y
+t aaXaaaaabY (#a1)(a#b)(a#b)Y
+f read.me (#ia1)README~READ.ME
+t read.me (#ia1)README~READ_ME
+f read.me (#ia1)README~(#a1)READ_ME
EOT
print "$failed tests failed."
diff --git a/Src/.cvsignore b/Src/.cvsignore
index edec5401b..fcef4da67 100644
--- a/Src/.cvsignore
+++ b/Src/.cvsignore
@@ -19,6 +19,7 @@ zsh
libzsh.so*
sigcount.h
signames.c
+version.h
zshpaths.h
zshxmods.h
bltinmods.list
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 2af8dc6ac..7825a900d 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -37,7 +37,7 @@
# include "rlimits.h"
-# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED)
+# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED)
static rlim_t
zstrtorlimt(const char *s, char **t, int base)
{
@@ -62,9 +62,9 @@ zstrtorlimt(const char *s, char **t, int base)
*t = (char *)s;
return ret;
}
-# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */
+# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
# define zstrtorlimt(a, b, c) zstrtol((a), (b), (c))
-# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */
+# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
/* Display resource limits. hard indicates whether `hard' or `soft' *
* limits should be displayed. lim specifies the limit, or may be -1 *
@@ -107,9 +107,15 @@ showlimits(int hard, int lim)
else
printf("%qdkB\n", val / 1024L);
# else
+# ifdef RLIM_T_IS_LONG_LONG
+ printf("%lldMB\n", val / (1024L * 1024L));
+ else
+ printf("%lldkB\n", val / 1024L);
+# else
printf("%ldMB\n", val / (1024L * 1024L));
else
printf("%ldkB\n", val / 1024L);
+# endif /* RLIM_T_IS_LONG_LONG */
# endif /* RLIM_T_IS_QUAD_T */
}
}
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 0d5419817..76ac67114 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -271,9 +271,10 @@ struct cline {
#define CLF_MISS 4
#define CLF_DIFF 8
#define CLF_SUF 16
-#define CLF_NEW 32
-#define CLF_VAR 64
-#define CLF_JOIN 128
+#define CLF_PNEW 32
+#define CLF_SNEW 64
+#define CLF_VAR 128
+#define CLF_JOIN 256
/* Flags for makecomplist*(). Things not to do. */
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 77fe0a1fe..72f3cea53 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -49,10 +49,10 @@ void (*makecompparamsptr) _((void));
/* pointers to functions required by compctl and defined by zle */
/**/
-void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char **));
+void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **));
/**/
-char *(*comp_strptr) _((int*,int*));
+char *(*comp_strptr) _((int*, int*, int));
/**/
int (*getcpatptr) _((char *, int, char *, int));
@@ -410,6 +410,7 @@ setup_comp1(Module m)
cc_first.mask2 = CC_CCCONT;
compcontext = compcommand = compprefix = compsuffix =
compiprefix = NULL;
+ makecompparamsptr = NULL;
return 0;
}
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index e3b778508..f71d67510 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1676,7 +1676,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
char *p, **sp, *e;
char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL;
- char *ign = NULL, *rf = NULL;
+ char *ign = NULL, *rf = NULL, *expl = NULL;
int f = 0, a = 0, dm;
Cmatcher match = NULL;
@@ -1757,6 +1757,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
e = "matching specification expected after -%c";
dm = 1;
break;
+ case 'X':
+ sp = &expl;
+ e = "string expected after -%c";
+ break;
case 'r':
f |= CMF_REMOVE;
sp = &rs;
@@ -1802,7 +1806,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
match = cpcmatcher(match);
addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
- rs, rf, ign, f, a, match, argv);
+ rs, rf, ign, f, a, match, expl, argv);
freecmatcher(match);
return 0;
@@ -2049,8 +2053,8 @@ cond_strcl(char **a, int id)
zerr("zle not loaded, zle condition not available", NULL, 0);
return 1;
}
- i = getcpatptr(comp_strptr(&ipl, NULL), i, s, id);
- if (i != -1) {
+ i = getcpatptr(comp_strptr(&ipl, NULL, 1), i, s, id);
+ if (i != -1 && i >= ipl) {
ignore_prefix(i - ipl);
return 1;
}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index d055c45c1..28781e7f0 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -478,6 +478,17 @@ bin_zle_complete(char *name, char **args, char *ops, char func)
Thingy t;
Widget w, cw;
+#ifdef DYNAMIC
+ if (!require_module(name, "compctl", 0, 0)) {
+ zerrnam(name, "can't load compctl module", NULL, 0);
+ return 1;
+ }
+#else
+ if (!makecompparamsptr) {
+ zerrnam(name, "compctl module not available", NULL, 0);
+ return 1;
+ }
+#endif
t = rthingy(args[1]);
cw = t->widget;
unrefthingy(t);
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index af78e1c06..4b42640e1 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -214,11 +214,9 @@ 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;
+static Cmlist bmatchers;
-/* A list with references to all matcher we used. */
+/* A list with references to all matchers we used. */
static LinkList matchers;
@@ -293,12 +291,24 @@ 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()). */
+/* This says what of the state the line is in when completion is started *
+ * came from a previous completion. If the FC_LINE bit is set, the *
+ * string was inserted. If FC_INWORD is set, the last completion moved *
+ * the cursor into the word although it was at the end of it when the *
+ * last completion was invoked. *
+ * This is used to detect if the string should be taken as an exact *
+ * match (see do_ambiguous()) and if the cursor has to be moved to the *
+ * end of the word before generating the completions. */
static int fromcomp;
+/* This holds the end-position of the last string inserted into the line. */
+
+static int lastend;
+
+#define FC_LINE 1
+#define FC_INWORD 2
+
/**/
void
completecall(void)
@@ -607,6 +617,12 @@ docomplete(int lst)
return;
}
+ /* We may have to reset the cursor to its position after the *
+ * string inserted by the last completion. */
+
+ if (fromcomp & FC_INWORD)
+ cs = lastend;
+
/* Check if we have to start a menu-completion (via automenu). */
if ((amenu = (isset(AUTOMENU) && lastambig &&
@@ -1567,6 +1583,83 @@ addtocline(Cline *retp, Cline *lrp,
*lrp = ln;
}
+/* This compares two cpattern lists and returns non-zero if they are
+ * equal. */
+
+static int
+cmp_cpatterns(Cpattern a, Cpattern b)
+{
+ while (a) {
+ if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256))
+ return 0;
+ a = a->next;
+ b = b->next;
+ }
+ return 1;
+}
+
+/* This compares two cmatchers and returns non-zero if they are equal. */
+
+static int
+cmp_cmatchers(Cmatcher a, Cmatcher b)
+{
+ return (a == b ||
+ (a->flags == b->flags &&
+ a->llen == b->llen && a->wlen == b->wlen &&
+ (!a->llen || cmp_cpatterns(a->line, b->line)) &&
+ (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) &&
+ (!(a->flags & CMF_LEFT) ||
+ (a->lalen == b->lalen &&
+ (!a->lalen || cmp_cpatterns(a->left, b->left)))) &&
+ (!(a->flags & CMF_RIGHT) ||
+ (a->ralen == b->ralen &&
+ (!a->ralen || cmp_cpatterns(a->right, b->right))))));
+}
+
+/* Add the given matchers to the bmatcher list. */
+
+static void
+add_bmatchers(Cmatcher m)
+{
+ Cmlist old = bmatchers, *q = &bmatchers, n;
+
+ for (; m; m = m->next) {
+ if ((!m->flags && m->wlen > 0 && m->llen > 0) ||
+ (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) {
+ *q = n = (Cmlist) halloc(sizeof(struct cmlist));
+ n->matcher = m;
+ q = &(n->next);
+ }
+ }
+ *q = old;
+}
+
+/* This is called when the matchers in the mstack have changed to
+ * ensure that the bmatchers list contains no matchers not in mstack. */
+
+static void
+update_bmatchers(void)
+{
+ Cmlist p = bmatchers, q = NULL, ms;
+ Cmatcher mp;
+ int t;
+
+ while (p) {
+ t = 0;
+ for (ms = mstack; ms && !t; ms = ms->next)
+ for (mp = ms->matcher; mp && !t; mp = mp->next)
+ t = cmp_cmatchers(mp, p->matcher);
+
+ p = p->next;
+ if (!t) {
+ if (q)
+ q->next = p;
+ else
+ bmatchers = p;
+ }
+ }
+}
+
/* 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.
@@ -1582,21 +1675,20 @@ end_list(int len, char *str)
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;
- }
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ 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) {
@@ -1616,7 +1708,7 @@ end_list(int len, char *str)
/* 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.
+ * or that string in local static memory, dup it.
* All this is a lot like the procedure in bld_new_pfx(), only slightly
* simpler, see that function for more comments. */
@@ -1644,60 +1736,59 @@ join_strs(int la, char *sa, int lb, char *sb)
}
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;
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ 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;
+ rs = realloc(rs, (rl += 20));
+ rr += 20;
+ rp += rs - or;
}
- } 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;
+ 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;
+ 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;
}
}
}
@@ -1725,7 +1816,7 @@ join_strs(int la, char *sa, int lb, char *sb)
*rp = '\0';
- return dupstring(rs);
+ return rs;
}
/* This gets two cline lists with separated runs and joins the corresponding
@@ -1752,26 +1843,55 @@ join_ends(Cline o, Cline n, int *olp, int *nlp)
* 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;
+ o->line = dupstring(j);
bld_pfx(o, n, &mol, &mnl);
smol += mol + o->llen;
smnl += mnl + n->llen;
} else {
- /* Different anchor strings, so shorten the list. */
+ /* Different anchors, see if we can find matching anchors
+ * further down the lists. */
+ Cline to, tn;
+ int t = 0;
+
+ /* But first build the common prefix. */
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;
+
+ for (to = o; to && !t; to = to->next) {
+ for (tn = n; tn && !t; tn = tn->next) {
+ if ((t = ((to->llen == tn->llen &&
+ !strncmp(to->line, tn->line, to->llen)) ||
+ (!(to->flags & CLF_JOIN) &&
+ join_strs(to->llen, to->line,
+ tn->llen, tn->line)))))
+ break;
+ }
+ if (t)
+ break;
+ }
+ if (t) {
+ /* Found matching anchors, continue with them. */
+ o->line = to->line;
+ o->llen = to->llen;
+ o->next = to->next;
+ o->flags |= CLF_MISS;
+ n = tn;
+ } else {
+ /* No matching anchors found, shorten the list. */
+ 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). */
+ * 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;
@@ -1859,20 +1979,19 @@ bld_line_pfx(Cpattern pat, char *line, char *lp,
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;
- }
+ for (ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ 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)
@@ -1923,100 +2042,99 @@ bld_new_pfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp)
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;
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ /* 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);
+ *ow = '\0';
+ n = getcline(NULL, 0, dupstring(p),
+ ow - p, 0);
+ *ow = sav;
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;
+ /* 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);
+ *ow = '\0';
+ n = getcline(NULL, 0, dupstring(p),
+ ow - p, 0);
+ *ow = sav;
if (l)
l->next = n;
else
ret = n;
l = n;
- ow += bl;
- nw += mp->wlen;
- ol -= bl;
- nl -= mp->wlen;
- p = ow;
- t = 1;
}
+ 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;
}
}
}
@@ -2107,19 +2225,18 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
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;
- }
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ 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)
@@ -2148,6 +2265,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
l->next = line;
else
ret = line;
+ l = line;
}
} else {
/* The cline doesn't have a string built by reverse matching,
@@ -2163,6 +2281,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
l->next = line;
else
ret = line;
+ l = line;
len = 0;
} else {
char sav = word[len];
@@ -2180,6 +2299,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
l->next = line;
else
ret = line;
+ l = line;
len = 0;
} else if (strpfx(line->word, word)) {
word[len] = sav;
@@ -2190,6 +2310,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
l->next = line;
else
ret = line;
+ l = line;
len = 0;
} else {
/* Not the same and no prefix, so we try to build a
@@ -2208,6 +2329,7 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
l->next = sl;
else
ret = sl;
+ l = sl;
if (!mol) {
send->next = next;
word += len - mnl;
@@ -2238,7 +2360,11 @@ join_new_pfx(Cline line, int len, char *word, int *missp)
static void
bld_pfx(Cline o, Cline n, int *olp, int *nlp)
{
- if (o->flags & CLF_NEW) {
+ if (olp)
+ *olp = 0;
+ if (nlp)
+ *nlp = 0;
+ if (o->flags & CLF_PNEW) {
if (o->flags & (CLF_END | CLF_MID))
/* We split the suffix in the middle and at the end into
* separate runs. */
@@ -2255,7 +2381,7 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp)
o->flags |= CLF_MISS;
}
} else if (o->flags & (CLF_END | CLF_MID)) {
- o->flags |= CLF_NEW;
+ o->flags |= CLF_PNEW;
o->prefix = join_ends(end_list(o->wlen, o->word),
end_list(n->wlen, n->word), olp, nlp);
} else if (o->wlen && n->wlen) {
@@ -2280,7 +2406,7 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp)
* 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->flags |= CLF_PNEW;
o->prefix = bld_new_pfx(o->wlen, o->word, n->wlen, n->word,
&mol, &mnl, NULL);
newl = 0;
@@ -2300,7 +2426,8 @@ bld_pfx(Cline o, Cline n, int *olp, int *nlp)
if (!o->prefix && n->wlen != o->wlen)
o->flags |= CLF_MISS;
- }
+ } else
+ o->wlen = 0;
}
/* The following function are like their counterparts above, only for
@@ -2353,20 +2480,19 @@ bld_line_sfx(Cpattern pat, char *line, char *lp,
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;
- }
+ for (ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ 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)
@@ -2404,84 +2530,83 @@ bld_new_sfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp)
}
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;
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ 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);
+ *p = '\0';
+ n = getcline(NULL, 0, dupstring(ow),
+ p - ow, 0);
+ *p = sav;
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;
+ 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);
+ *p = '\0';
+ n = getcline(NULL, 0, dupstring(ow),
+ p - ow, 0);
+ *p = sav;
if (l)
l->next = n;
else
ret = n;
l = n;
- ow -= bl;
- nw -= mp->wlen;
- ol -= bl;
- nl -= mp->wlen;
- p = ow;
- t = 1;
}
+ 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;
}
}
}
@@ -2554,21 +2679,20 @@ join_new_sfx(Cline line, int len, char *word, int *missp)
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;
- }
+ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+ mp = ms->matcher;
+ 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)
@@ -2663,7 +2787,7 @@ join_new_sfx(Cline line, int len, char *word, int *missp)
static void
bld_sfx(Cline o, Cline n)
{
- if (o->flags & CLF_NEW) {
+ if (o->flags & CLF_SNEW) {
int miss;
o->suffix = join_new_sfx(o->suffix, n->wlen, n->word, &miss);
@@ -2679,7 +2803,7 @@ bld_sfx(Cline o, Cline n)
new = dupstring(n->word);
newl = n->wlen;
} else if (!strpfx(o->word, n->word)) {
- o->flags |= CLF_NEW;
+ o->flags |= CLF_SNEW;
o->suffix = bld_new_sfx(o->wlen, o->word, n->wlen, n->word,
&mol, &mnl, NULL);
newl = 0;
@@ -2732,7 +2856,7 @@ join_clines(Cline o, Cline n)
n = q;
}
}
- if (n->flags & CLF_MID) {
+ if (n && n->flags & CLF_MID) {
while (o && !(o->flags & CLF_MID)) {
o->word = NULL;
o->flags |= CLF_DIFF;
@@ -3128,6 +3252,7 @@ inst_cline(Cline l, int pl, int sl)
}
l = l->next;
}
+ lastend = cs;
/* 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. */
@@ -3281,6 +3406,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
* to continue after the matched portion. */
w = q;
iw = j;
+ lw = k;
l = p;
il = jj;
ll = kk;
@@ -3501,6 +3627,7 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
}
w = q + 1;
iw = j;
+ lw = k;
l = p + 1;
il = jj;
ll = kk;
@@ -3765,6 +3892,7 @@ instmatch(Cmatch m)
inststrlen(m->suf, 1, (l = strlen(m->suf)));
r += l;
}
+ lastend = cs;
cs = ocs;
return r;
}
@@ -3777,10 +3905,10 @@ instmatch(Cmatch m)
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)
+ int flags, int aflags, Cmatcher match, char *exp, char **argv)
{
- char *s, *t, *e, *me, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL;
- int lpl, lsl, i, pl, sl, test, bpl, bsl, lsm, llpl;
+ char *s, *t, *e, *me, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL;
+ int lpl, lsl, i, pl, sl, test, bpl, bsl, llpl, llsl;
Aminfo ai;
Cline lc = NULL;
LinkList l;
@@ -3788,43 +3916,39 @@ 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;
- } else {
- 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 {
+ if (exp) {
+ expl = (Cexpl) halloc(sizeof(struct cexpl));
+ expl->count = expl->fcount = 0;
+ expl->str = dupstring(exp);
+ } else
+ expl = NULL;
+
+ /* Store the matcher in our stack of matchers. */
+ if (match) {
+ mst.next = mstack;
+ mst.matcher = match;
+ mstack = &mst;
+
+ if (!mnum)
+ add_bmatchers(match);
+
+ addlinknode(matchers, match);
+ match->refc++;
+ }
+ if (mnum && (mstack || bmatchers))
+ update_bmatchers();
+
+ /* Get the suffixes to ignore. */
+ if (ign)
+ aign = get_user_var(ign);
/* Get the contents of the completion variables if we have
* to perform matching. */
if (aflags & CAF_MATCH) {
@@ -3832,6 +3956,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
lpre = dupstring(compprefix);
llpl = strlen(lpre);
lsuf = dupstring(compsuffix);
+ llsl = strlen(lsuf);
}
/* Now duplicate the strings we have from the command line. */
if (ipre)
@@ -3848,8 +3973,6 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
lsl = strlen(psuf);
} else
lsl = 0;
- if (aflags & CAF_MATCH)
- lsm = (psuf ? !strcmp(psuf, lsuf) : (!lsuf || !*lsuf));
if (pre)
pre = dupstring(pre);
if (suf)
@@ -3865,6 +3988,17 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
begcmgroup(group, (aflags & CAF_NOSORT));
if (aflags & CAF_NOSORT)
mgroup->flags |= CGF_NOSORT;
+ } else {
+ endcmgroup(NULL);
+ begcmgroup("default", 0);
+ }
+ /* Select the set of matches. */
+ if (aflags & CAF_ALT) {
+ l = fmatches;
+ ai = fainfo;
+ } else {
+ l = matches;
+ ai = ainfo;
}
if (remf) {
remf = dupstring(remf);
@@ -3908,19 +4042,19 @@ 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))))
- test = lsm;
+ test = (sl >= llpl + llsl &&
+ strpfx(lpre, s) && strsfx(lsuf, s));
if (!test && mstack &&
- (ms = comp_match(lpre, lsuf,
- (psuf ? dyncat(t, psuf) : t),
+ (ms = comp_match(lpre, lsuf, s,
&lc, (aflags & CAF_QUOTE),
&bpl, &bsl)))
test = 1;
+
if (!test)
continue;
- me = e = s + sl;
+ pl = sl - llsl;
+ me = s + sl - llsl;
+ e = s + llpl;
} else {
e = s;
me = s + sl;
@@ -3928,8 +4062,10 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
}
/* Quoting? */
if (!(aflags & CAF_QUOTE)) {
- te = s + pl;
- s = quotename(s, &e, te, &pl);
+ int tmp = me - s;
+
+ s = quotename(s, &e, me, &tmp);
+ me = s + tmp;
sl = strlen(s);
}
/* The rest is almost the same as in addmatch(). */
@@ -3944,31 +4080,48 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
if (ppre)
t = dyncat(ppre, t);
if (!ms && mstack) {
+ int bl = ((aflags & CAF_MATCH) ? llpl : 0);
Cline *clp = &lc, tlc;
char *ss = dupstring(s), *ee = me + (ss - s);
if (ppre && *ppre) {
- *clp = str_cline(ppre, strlen(ppre), &tlc);
+ *clp = tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR);
clp = &(tlc->next);
}
- if (ee != ss + sl || (lpsuf && *lpsuf)) {
- *clp = tlc = getcline(ss, ee - ss,
+ if (bl) {
+ *clp = str_cline(ss, bl, &tlc);
+ clp = &(tlc->next);
+ }
+ if (ee != ss + sl) {
+ *clp = tlc = getcline(ss + bl, ee - ss - bl,
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);
- }
+ *clp = str_cline(ee, (ss + sl) - ee, &tlc);
+ clp = &(tlc->next);
} else {
- *clp = tlc = getcline(NULL, 0, ss, sl,
+ *clp = tlc = getcline(NULL, 0, ss + bl, sl - bl,
+ CLF_END | CLF_VAR);
+ clp = &(tlc->next);
+ }
+ if (psuf && *psuf) {
+ *clp = tlc = getcline(NULL, 0, psuf, lsl,
CLF_END | CLF_VAR);
clp = &(tlc->next);
}
*clp = NULL;
+ } else if (mstack) {
+ Cline tlc;
+
+ if (ppre && *ppre) {
+ tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR);
+ tlc->next = lc;
+ lc = tlc;
+ }
+ if (psuf && *psuf) {
+ for (tlc = lc; tlc->next; tlc = tlc->next);
+ tlc->next = getcline(NULL, 0, psuf, lsl,
+ CLF_END | CLF_VAR);
+ }
}
if (ipre && *ipre) {
Cline tlc = prepend_cline(ipre, lc);
@@ -4047,6 +4200,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
}
}
compnmatches = mnum;
+ if (exp)
+ addexpl();
} LASTALLOC;
} SWITCHBACKHEAPS;
@@ -4129,8 +4284,10 @@ addmatch(char *s, char *t)
mpl = fpl; msl = fsl;
} else {
if ((cp = filecomp)) {
- if ((test = domatch(s, filecomp, 0)))
+ if ((test = domatch(s, filecomp, 0))) {
+ e = s + sl;
cc = 1;
+ }
} else {
e = s + sl - fsl;
if ((test = !strncmp(s, fpre, fpl)))
@@ -4168,6 +4325,7 @@ addmatch(char *s, char *t)
strcat(tt, s);
if (lpsuf)
strcat(tt, lpsuf);
+ e += (tt - s);
untokenize(s = tt);
sl = strlen(s);
}
@@ -4196,9 +4354,10 @@ addmatch(char *s, char *t)
((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) ||
((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
if (sl >= rpl + rsl || mstack) {
- if (cp)
+ if (cp) {
test = domatch(s, patcomp, 0);
- else {
+ e = s + sl;
+ } else {
e = s + sl - rsl;
if ((test = !strncmp(s, rpre, rpl)))
if ((test = !strcmp(e, rsuf))) {
@@ -4888,9 +5047,11 @@ makecomplist(char *s, int incmd, int lst)
{
struct cmlist ms;
Cmlist m;
+ char *os = s;
- /* If we already have a list from a previous execution of this *
- * function, skip the list building code. */
+ /* We build a copy of the list of matchers to use to make sure that this
+ * works even if a shell function called from the completion code changes
+ * the global matchers. */
if ((m = cmatcher)) {
Cmlist mm, *mp = &mm;
@@ -4906,16 +5067,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;
+
+ /* Walk through the global matchers. */
for (;;) {
+ bmatchers = NULL;
if (m) {
ms.next = NULL;
ms.matcher = m->matcher;
mstack = &ms;
- bmstack0.matcher = m->matcher;
+
+ /* Store the matchers used in the bmatchers list which is used
+ * when building new parts for the string to insert into the
+ * line. */
+ add_bmatchers(m->matcher);
} else
mstack = NULL;
@@ -4932,12 +5096,12 @@ makecomplist(char *s, int incmd, int lst)
lastambig = 0;
amatches = 0;
mnum = 0;
- had_lm = 0;
begcmgroup("default", 0);
ccused = newlinklist();
ccstack = newlinklist();
+ s = dupstring(os);
if (compfunc)
callcompfunc(s, compfunc);
else
@@ -5004,7 +5168,7 @@ ctokenize(char *p)
/**/
char *
-comp_str(int *ipl, int *pl)
+comp_str(int *ipl, int *pl, int untok)
{
char *p = dupstring(compprefix);
char *s = dupstring(compsuffix);
@@ -5012,12 +5176,14 @@ comp_str(int *ipl, int *pl)
char *str;
int lp, ls, lip;
- ctokenize(p);
- remnulargs(p);
- ctokenize(s);
- remnulargs(s);
- ctokenize(ip);
- remnulargs(ip);
+ if (!untok) {
+ ctokenize(p);
+ remnulargs(p);
+ ctokenize(s);
+ remnulargs(s);
+ ctokenize(ip);
+ remnulargs(ip);
+ }
ls = strlen(s);
lip = strlen(ip);
lp = strlen(p);
@@ -5041,7 +5207,7 @@ makecomplistcall(Compctl cc)
SWITCHHEAPS(compheap) {
HEAPALLOC {
int ooffs = offs, lip, lp;
- char *str = comp_str(&lip, &lp);
+ char *str = comp_str(&lip, &lp, 0);
offs = lip + lp;
cc->refc++;
@@ -5073,7 +5239,7 @@ makecomplistctl(int flags)
SWITCHHEAPS(compheap) {
HEAPALLOC {
int ooffs = offs, lip, lp;
- char *str = comp_str(&lip, &lp), *t;
+ char *str = comp_str(&lip, &lp, 0), *t;
char *os = cmdstr, **ow = clwords, **p, **q;
int on = clwnum, op = clwpos;
@@ -5571,19 +5737,16 @@ 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;
- }
+
+ if (!mnum)
+ add_bmatchers(cc->matcher);
+
addlinknode(matchers, cc->matcher);
cc->matcher->refc++;
- } else {
- bmstack1.matcher = NULL;
- if (mnum)
- had_lm = 1;
}
+ if (mnum && (mstack || bmatchers))
+ update_bmatchers();
+
/* Insert the prefix (compctl -P), if any. */
if (cc->prefix) {
int pl = 0;
@@ -6344,7 +6507,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
}
if ((tt = cc->explain)) {
- if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) {
+ tt = dupstring(tt);
+ if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
singsub(&tt);
untokenize(tt);
}
@@ -6358,7 +6522,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
begcmgroup("default", 0);
}
else if ((tt = cc->explain)) {
- if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) {
+ tt = dupstring(tt);
+ if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
singsub(&tt);
untokenize(tt);
}
@@ -6631,7 +6796,7 @@ begcmgroup(char *n, int nu)
}
}
mgroup = (Cmgroup) halloc(sizeof(struct cmgroup));
- mgroup->name = n;
+ mgroup->name = dupstring(n);
mgroup->flags = mgroup->lcount = mgroup->mcount = 0;
mgroup->matches = NULL;
mgroup->ylist = NULL;
@@ -6872,7 +7037,7 @@ do_ambiguous(void)
/* 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) && !fromcomp &&
+ if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !(fromcomp & FC_LINE) &&
(usemenu == 0 || unset(AUTOMENU))) {
do_single(ainfo->exactm);
invalidatelist();
@@ -6945,7 +7110,6 @@ do_ambiguous(void)
merge_cline(lc, ps, lp, NULL, 0, 0);
}
inst_cline(lc, pl, sl);
-
} else {
inststrlen(ps, 1, -1);
ocs = cs;
@@ -6966,6 +7130,7 @@ do_ambiguous(void)
cs -= brsl;
inststrlen(brend, 1, -1);
}
+ lastend = cs;
cs = ocs;
}
/* la is non-zero if listambiguous may be used. Copying and
@@ -6973,11 +7138,13 @@ do_ambiguous(void)
* solution. Really. */
la = (ll != oll || strncmp(oline, (char *) line, ll));
- /* 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 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. Also we remember if we just moved the *
+ * cursor into the word. */
+ fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
+ ((atend && cs != lastend) ? FC_INWORD : 0));
/*
* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
diff --git a/Src/exec.c b/Src/exec.c
index 59061af3f..764b7140c 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2558,7 +2558,7 @@ execarith(Cmd cmd)
val = matheval(e);
}
if (isset(XTRACE)) {
- fprintf(stderr, " ))\n", stderr);
+ fprintf(stderr, " ))\n");
fflush(stderr);
}
errflag = 0;
diff --git a/Src/glob.c b/Src/glob.c
index 516f75618..47fa63567 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -71,6 +71,7 @@ struct gmatch {
int badcshglob;
static int mode; /* != 0 if we are parsing glob patterns */
+static int scanning; /* != 0 if we are scanning file paths */
static int pathpos; /* position in pathbuf */
static int matchsz; /* size of matchbuf */
static int matchct; /* number of matches found */
@@ -134,7 +135,7 @@ struct complist {
struct comp {
Comp left, right, next, exclude;
char *str;
- int stat;
+ int stat, errsmax;
};
/* Type of Comp: a closure with one or two #'s, the end of a *
@@ -162,6 +163,18 @@ struct comp {
#define GF_PATHADD 1 /* file glob, adding path components */
#define GF_TOPLEV 2 /* outside (), so ~ ends main match */
+/* Next character after one which may be a Meta (x is any char *) */
+#define METANEXT(x) (*(x) == Meta ? (x)+2 : (x)+1)
+/*
+ * Increment pointer which may be on a Meta (x is a pointer variable),
+ * returning the incremented value (i.e. like pre-increment).
+ */
+#define METAINC(x) ((x) += (*(x) == Meta) ? 2 : 1)
+/*
+ * Return unmetafied char from string (x is any char *)
+ */
+#define UNMETA(x) (*(x) == Meta ? (x)[1] ^ 32 : *(x))
+
static char *pptr; /* current place in string being matched */
static Comp tail;
static int first; /* are leading dots special? */
@@ -352,6 +365,25 @@ haswilds(char *str)
return 0;
}
+/* Flags to apply to current level of grouping */
+
+static int addflags;
+
+/*
+ * Approximate matching.
+ *
+ * errsmax is used while parsing to store the current number
+ * of errors allowed. While executing it is usually set to -1,
+ * but can be set to something else to fix a different maximum
+ * no. of errors which acts as a limit on the local value: see
+ * scanner() for why this is necessary.
+ *
+ * errsfound is used while executing a pattern to record the
+ * number of errors found so far.
+ */
+
+static int errsmax, errsfound;
+
/* Do the globbing: scanner is called recursively *
* with successive bits of the path until we've *
* tried all of it. */
@@ -363,6 +395,7 @@ scanner(Complist q)
Comp c;
int closure;
int pbcwdsav = pathbufcwd;
+ int errssofar = errsfound;
struct dirsav ds;
ds.ino = ds.dev = 0;
@@ -381,7 +414,7 @@ scanner(Complist q)
c = q->comp;
/* Now the actual matching for the current path section. */
if (!(c->next || c->left) && !haswilds(c->str)
- && (!(c->stat & (C_LCMATCHUC|C_IGNCASE))
+ && (!((c->stat & (C_LCMATCHUC|C_IGNCASE)) || c->errsmax)
|| !strcmp(".", c->str) || !strcmp("..", c->str))) {
/*
* We always need to match . and .. explicitly, even if we're
@@ -433,6 +466,7 @@ scanner(Complist q)
((glob_pre && !strpfx(glob_pre, fn))
|| (glob_suf && !strsfx(glob_suf, fn))))
continue;
+ errsfound = errssofar;
if (domatch(fn, c, gf_noglobdots)) {
/* if this name matchs the pattern... */
if (pbcwdsav == pathbufcwd &&
@@ -453,7 +487,34 @@ scanner(Complist q)
if (dirs) {
int l;
- /* if not the last component in the path */
+ /*
+ * If not the last component in the path:
+ *
+ * If we made an approximation in the new path segment,
+ * and the pattern includes closures other than a
+ * trailing * (we only test if it is not a string),
+ * then it is possible we made too many errors. For
+ * example, (ab)#(cb)# will match the directory abcb
+ * with one error if allowed to, even though it can
+ * match with none. This will stop later parts of the
+ * path matching, so we need to check by reducing the
+ * maximum number of errors and seeing if the directory
+ * still matches. Luckily, this is not a terribly
+ * common case, since complex patterns typically occur
+ * in the last part of the path which is not affected
+ * by this problem.
+ */
+ if (errsfound > errssofar && (c->next || c->left)) {
+ errsmax = errsfound - 1;
+ while (errsmax >= errssofar) {
+ errsfound = errssofar;
+ if (!domatch(fn, c, gf_noglobdots))
+ break;
+ errsmax = errsfound - 1;
+ }
+ errsfound = errsmax + 1;
+ errsmax = -1;
+ }
if (closure) {
/* if matching multiple directories */
struct stat buf;
@@ -470,9 +531,14 @@ scanner(Complist q)
continue;
}
l = strlen(fn) + 1;
- subdirs = hrealloc(subdirs, subdirlen, subdirlen + l);
+ subdirs = hrealloc(subdirs, subdirlen, subdirlen + l
+ + sizeof(int));
strcpy(subdirs + subdirlen, fn);
subdirlen += l;
+ /* store the count of errors made so far, too */
+ memcpy(subdirs + subdirlen, (char *)&errsfound,
+ sizeof(int));
+ subdirlen += sizeof(int);
} else
/* if the last filename component, just add it */
insert(fn, 1);
@@ -482,8 +548,11 @@ scanner(Complist q)
if (subdirs) {
int oppos = pathpos;
- for (fn = subdirs; fn < subdirs+subdirlen; fn += strlen(fn) + 1) {
+ for (fn = subdirs; fn < subdirs+subdirlen; ) {
addpath(fn);
+ fn += strlen(fn) + 1;
+ memcpy((char *)&errsfound, fn, sizeof(int));
+ fn += sizeof(int);
scanner((q->closure) ? q : q->next); /* scan next level */
pathbuf[pathpos = oppos] = '\0';
}
@@ -502,16 +571,13 @@ scanner(Complist q)
/* Parse a series of path components pointed to by pptr */
-/* Flags to apply to current level of grourping */
-
-static int addflags;
-
/**/
static Comp
compalloc(void)
{
Comp c = (Comp) alloc(sizeof *c);
c->stat |= addflags;
+ c->errsmax = errsmax;
return c;
}
@@ -519,10 +585,19 @@ compalloc(void)
static int
getglobflags()
{
+ char *nptr;
/* (#X): assumes we are still positioned on the initial '(' */
pptr++;
while (*++pptr && *pptr != Outpar) {
switch (*pptr) {
+ case 'a':
+ /* Approximate matching, max no. of errors follows */
+ errsmax = zstrtol(++pptr, &nptr, 10);
+ if (pptr == nptr)
+ return 1;
+ pptr = nptr-1;
+ break;
+
case 'l':
/* Lowercase in pattern matches lower or upper in target */
addflags |= C_LCMATCHUC;
@@ -635,6 +710,7 @@ parsecomp(int gflag)
if (eptr == cstr) {
/* if no string yet, carry on and get one. */
c->stat = addflags;
+ c->errsmax = errsmax;
cstr = pptr;
continue;
}
@@ -817,6 +893,7 @@ parsecompsw(int gflag)
{
Comp c1, c2, c3, excl = NULL, stail = tail;
int oaddflags = addflags;
+ int oerrsmax = errsmax;
char *sptr;
/*
@@ -845,12 +922,14 @@ parsecompsw(int gflag)
return NULL;
if (isset(EXTENDEDGLOB) && *pptr == Tilde) {
/* Matching remainder of pattern excludes the pattern from matching */
- int oldmode = mode;
+ int oldmode = mode, olderrsmax = errsmax;
mode = 1;
+ errsmax = 0;
pptr++;
excl = parsecomp(gflag);
mode = oldmode;
+ errsmax = olderrsmax;
if (!excl)
return NULL;
}
@@ -878,8 +957,10 @@ parsecompsw(int gflag)
c2->stat |= C_PATHADD;
c1 = c2;
}
- if (!(gflag & GF_TOPLEV))
+ if (!(gflag & GF_TOPLEV)) {
addflags = oaddflags;
+ errsmax = oerrsmax;
+ }
return c1;
}
@@ -965,6 +1046,7 @@ parsepat(char *str)
{
mode = 0; /* path components present */
addflags = 0;
+ errsmax = 0;
pptr = str;
tail = NULL;
/*
@@ -1102,7 +1184,7 @@ static int
gmatchcmp(Gmatch a, Gmatch b)
{
int i, *s;
- long r;
+ long r = 0L;
for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) {
switch (*s & ~GS_DESC) {
@@ -1594,10 +1676,14 @@ glob(LinkList list, LinkNode np)
matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) *
sizeof(struct gmatch));
matchct = 0;
+ errsfound = 0;
+ errsmax = -1;
/* The actual processing takes place here: matches go into *
* matchbuf. This is the only top-level call to scanner(). */
+ scanning = 1;
scanner(q);
+ scanning = 0;
/* Deal with failures to match depending on options */
if (matchct)
@@ -2427,6 +2513,14 @@ domatch(char *str, Comp c, int fist)
int ret;
pptr = str;
first = fist;
+ /*
+ * If scanning paths, keep the error count over the whole
+ * filename
+ */
+ if (!scanning) {
+ errsfound = 0;
+ errsmax = -1;
+ }
if (*pptr == Nularg)
pptr++;
PERMALLOC {
@@ -2444,15 +2538,17 @@ static int
excluded(Comp c, char *eptr)
{
char *saves = pptr;
- int savei = first, savel = longest, ret;
+ int savei = first, savel = longest, savee = errsfound, ret;
first = 0;
/*
* Even if we've been told always to match the longest string,
* i.e. not anchored to the end, we want to match the full string
* we've already matched when we're trying to exclude it.
+ * Likewise, approximate matching here is treated separately.
*/
longest = 0;
+ errsfound = 0;
if (PATHADDP(c) && pathpos) {
VARARR(char, buf, pathpos + strlen(eptr) + 1);
@@ -2470,13 +2566,18 @@ excluded(Comp c, char *eptr)
pptr = saves;
first = savei;
longest = savel;
+ errsfound = savee;
return ret;
}
+/*
+ * Structure for storing instances of a pattern in a closured.
+ */
struct gclose {
- char *start;
- char *end;
+ char *start; /* Start of this instance */
+ char *end; /* End of this instance */
+ int errsfound; /* Errors found up to start of instance */
};
typedef struct gclose *Gclose;
@@ -2488,34 +2589,48 @@ typedef struct gclose *Gclose;
* also not bother going any further, since the first time we got to
* that position (when it was marked), we must already have failed on
* and backtracked over any further closure matches beyond that point.
+ * If we are using approximation, the number in the string is incremented
+ * by the current error count; if we come back to that point with a
+ * lower error count, it is worth trying from that point again, but
+ * we must now mark that point in trystring with the new error.
*/
/**/
static void
-addclosures(Comp c, LinkList closlist, int *pdone, char *trystring)
+addclosures(Comp c, LinkList closlist, int *pdone, unsigned char *trystring)
{
Gclose gcnode;
char *opptr = pptr;
+ unsigned char *tpos;
while (*pptr) {
if (STARP(c)) {
- if (trystring[(pptr+1)-opptr])
- break;
+ if (*(tpos = trystring + ((pptr+1) - opptr))) {
+ if ((unsigned)(errsfound+1) >= *tpos)
+ break;
+ *tpos = (unsigned)(errsfound+1);
+ }
gcnode = (Gclose)zalloc(sizeof(struct gclose));
gcnode->start = pptr;
- gcnode->end = ++pptr;
+ gcnode->end = METAINC(pptr);
+ gcnode->errsfound = errsfound;
} else {
char *saves = pptr;
+ int savee = errsfound;
if (OPTIONALP(c) && *pdone >= 1)
return;
if (!matchonce(c) || saves == pptr ||
- trystring[pptr-opptr]) {
+ (*(tpos = trystring + (pptr-opptr)) &&
+ (unsigned)(errsfound+1) >= *tpos)) {
pptr = saves;
break;
}
+ if (*tpos)
+ *tpos = (unsigned)(errsfound+1);
gcnode = (Gclose)zalloc(sizeof(struct gclose));
gcnode->start = saves;
gcnode->end = pptr;
+ gcnode->errsfound = savee;
}
pushnode(closlist, gcnode);
(*pdone)++;
@@ -2523,13 +2638,29 @@ addclosures(Comp c, LinkList closlist, int *pdone, char *trystring)
}
/*
- * Match characters with case-insensitivity.
- * Note CHARMATCH(x,y) != CHARMATCH(y,x)
+ * Match characters with case-insensitivity. Note charmatch(x,y) !=
+ * charmatch(y,x): the first argument comes from the target string, the
+ * second from the pattern. This used to be a macro, and in principle
+ * could be turned back into one.
*/
-#define CHARMATCH(x, y) \
-(x == y || (((c->stat & C_IGNCASE) ? (tulower(x) == tulower(y)) : \
- (c->stat & C_LCMATCHUC) ? (islower(y) && tuupper(y) == x) : 0)))
+/**/
+static int
+charmatch(Comp c, char *x, char *y)
+{
+ /*
+ * This takes care of matching two characters which may be a
+ * two byte sequence, Meta followed by something else.
+ * Here we bypass tulower() and tuupper() for speed.
+ */
+ int xi = (STOUC(UNMETA(x)) & 0xff), yi = (STOUC(UNMETA(y)) & 0xff);
+ return xi == yi ||
+ (((c->stat & C_IGNCASE) ?
+ ((isupper(xi) ? tolower(xi) : xi) ==
+ (isupper(yi) ? tolower(yi) : yi)) :
+ (c->stat & C_LCMATCHUC) ?
+ (islower(yi) && toupper(yi) == xi) : 0));
+}
/* see if current string in pptr matches c */
@@ -2539,14 +2670,16 @@ doesmatch(Comp c)
{
if (CLOSUREP(c)) {
int done, retflag = 0;
- char *saves, *trystring, *opptr;
+ char *saves, *opptr;
+ unsigned char *trystring, *tpos;
+ int savee;
LinkList closlist;
Gclose gcnode;
if (first && *pptr == '.')
return 0;
- if (!inclosure && !c->left) {
+ if (!inclosure && !c->left && !c->errsmax) {
/* We are not inside another closure, and the current
* pattern is a simple string. We handle this very common
* case specially: otherwise, matches like *foo* are
@@ -2561,26 +2694,30 @@ doesmatch(Comp c)
!itok(looka)) {
/* Another simple optimisation for a very common case:
* we are processing a * and there is
- * an ordinary character match next. We look ahead for
- * that character, taking care of Meta bytes.
+ * an ordinary character match (which may not be a Metafied
+ * character, just to make it easier) next. We look ahead
+ * for that character, taking care of Meta bytes.
*/
while (*pptr) {
for (; *pptr; pptr++) {
if (*pptr == Meta)
pptr++;
- else if (CHARMATCH(STOUC(*pptr), STOUC(looka)))
+ else if (charmatch(c, pptr, &looka))
break;
}
if (!*(saves = pptr))
break;
+ savee = errsfound;
if (doesmatch(c->next))
return 1;
pptr = saves+1;
+ errsfound = savee;
}
} else {
/* Standard track-forward code */
for (done = 0; ; done++) {
saves = pptr;
+ savee = errsfound;
if ((done || ONEHASHP(c) || OPTIONALP(c)) &&
((!c->next && (!LASTP(c) || !*pptr || longest)) ||
(c->next && doesmatch(c->next))))
@@ -2588,6 +2725,7 @@ doesmatch(Comp c)
if (done && OPTIONALP(c))
return 0;
pptr = saves;
+ errsfound = savee;
first = 0;
if (STARP(c)) {
if (!*pptr)
@@ -2602,7 +2740,7 @@ doesmatch(Comp c)
/* The full, gory backtracking code is now necessary. */
inclosure++;
closlist = newlinklist();
- trystring = zcalloc(strlen(pptr)+1);
+ trystring = (unsigned char *)zcalloc(strlen(pptr)+1);
opptr = pptr;
/* Start by making a list where each match is as long
@@ -2621,7 +2759,7 @@ doesmatch(Comp c)
retflag = 1;
break;
}
- trystring[saves-opptr] = 1;
+ trystring[saves-opptr] = (unsigned)(errsfound + 1);
/*
* If we failed, the first thing to try is whether we can
* shorten the match using the last pattern in the closure.
@@ -2633,14 +2771,18 @@ doesmatch(Comp c)
char savec = *gcnode->end;
*gcnode->end = '\0';
pptr = gcnode->start;
+ errsfound = gcnode->errsfound;
if (matchonce(c) && pptr != gcnode->start
- && !trystring[pptr-opptr]) {
+ && (!*(tpos = trystring + (pptr-opptr)) ||
+ *tpos > (unsigned)(errsfound+1))) {
+ if (*tpos)
+ *tpos = (unsigned)(errsfound+1);
*gcnode->end = savec;
gcnode->end = pptr;
/* Try again to construct a list based on
* this new position
*/
- addclosures(c, closlist, &done, trystring+(pptr-opptr));
+ addclosures(c, closlist, &done, tpos);
continue;
}
*gcnode->end = savec;
@@ -2650,6 +2792,7 @@ doesmatch(Comp c)
*/
if ((gcnode = (Gclose)getlinknode(closlist))) {
pptr = gcnode->start;
+ errsfound = gcnode->errsfound;
zfree(gcnode, sizeof(struct gclose));
done--;
} else
@@ -2723,8 +2866,7 @@ rangematch(char **patptr, int ch, int rchar)
#define PAT(X) (pat[X] == Meta ? pat[(X)+1] ^ 32 : untok(pat[X]))
#define PPAT(X) (pat[(X)-1] == Meta ? pat[X] ^ 32 : untok(pat[X]))
- for (pat++; *pat != Outbrack && *pat;
- *pat == Meta ? pat += 2 : pat++) {
+ for (pat++; *pat != Outbrack && *pat; METAINC(pat)) {
if (*pat == Inbrack) {
/* Inbrack can only occur inside a range if we found [:...:]. */
pat += 2;
@@ -2748,6 +2890,22 @@ rangematch(char **patptr, int ch, int rchar)
*patptr = pat;
}
+/*
+ * matchonce() is the core of the pattern matching, handling individual
+ * strings and instances of a pattern in a closure.
+ *
+ * Note on approximate matching: The rule is supposed to be
+ * (1) Take the longest possible match without approximation.
+ * (2) At any failure, make the single approximation that results
+ * in the longest match for the remaining part (which may
+ * include further approximations).
+ * (3) If we match the same distance, take the one with fewer
+ * approximations.
+ * If this is wrong, I haven't yet discovered a counterexample. Email
+ * lines are now open.
+ * pws 1999/02/23
+ */
+
/**/
static int
matchonce(Comp c)
@@ -2758,7 +2916,7 @@ matchonce(Comp c)
if (!pat || !*pat) {
/* No current pattern (c->str). */
char *saves;
- int savei;
+ int savee, savei;
if (errflag)
return 0;
@@ -2766,6 +2924,7 @@ matchonce(Comp c)
* check for exclusion of pattern or alternatives. */
saves = pptr;
savei = first;
+ savee = errsfound;
/* Loop over alternatives with exclusions: (foo~bar|...). *
* Exclusions apply to the pattern in c->left. */
if (c->left || c->right) {
@@ -2812,6 +2971,7 @@ matchonce(Comp c)
*/
exclend--;
pptr = saves;
+ errsfound = savee;
}
inclosure--;
if (ret)
@@ -2822,19 +2982,43 @@ matchonce(Comp c)
if (c->right && (!ret || inclosure)) {
/* If in a closure, we always want the longest match. */
char *newpptr = pptr;
+ int newerrsfound = errsfound;
pptr = saves;
first = savei;
+ errsfound = savee;
ret2 = doesmatch(c->right);
- if (ret && (!ret2 || pptr < newpptr))
+ if (ret && (!ret2 || pptr < newpptr)) {
pptr = newpptr;
+ errsfound = newerrsfound;
+ }
+ }
+ if (!ret && !ret2) {
+ pptr = saves;
+ first = savei;
+ errsfound = savee;
+ break;
}
- if (!ret && !ret2)
- return 0;
}
if (CLOSUREP(c))
return 1;
- if (!c->next) /* no more patterns left */
- return (!LASTP(c) || !*pptr || longest);
+ if (!c->next) {
+ /*
+ * No more patterns left, but we may still be in the middle
+ * of a match, in which case alles in Ordnung...
+ */
+ if (!LASTP(c))
+ return 1;
+ /*
+ * ...else we're at the last pattern, so this is our last
+ * ditch attempt at an approximate match: try to omit the
+ * last few characters.
+ */
+ for (; *pptr && errsfound < c->errsmax &&
+ (errsmax == -1 || errsfound < errsmax);
+ METAINC(pptr))
+ errsfound++;
+ return !*pptr || longest;
+ }
/* optimisation when next pattern is not a closure */
if (!CLOSUREP(c->next)) {
c = c->next;
@@ -2843,7 +3027,10 @@ matchonce(Comp c)
}
return doesmatch(c->next);
}
- /* Don't match leading dot if first is set */
+ /*
+ * Don't match leading dot if first is set
+ * (don't even try for an approximate match)
+ */
if (first && *pptr == '.' && *pat != '.')
return 0;
if (*pat == Star) { /* final * is not expanded to ?#; returns success */
@@ -2852,39 +3039,47 @@ matchonce(Comp c)
return 1;
}
first = 0; /* finished checking start of pattern */
- if (*pat == Quest && *pptr) {
+ if (*pat == Quest) {
/* match exactly one character */
- if (*pptr == Meta)
- pptr++;
- pptr++;
+ if (!*pptr)
+ break;
+ METAINC(pptr);
pat++;
continue;
}
if (*pat == Inbrack) {
/* Match groups of characters */
char ch;
+ char *saves, *savep;
if (!*pptr)
break;
- ch = *pptr == Meta ? pptr[1] ^ 32 : *pptr;
+ saves = pptr;
+ savep = pat;
+ ch = UNMETA(pptr);
if (pat[1] == Hat || pat[1] == '^' || pat[1] == '!') {
/* group is negated */
*++pat = Hat;
rangematch(&pat, ch, Hat);
DPUTS(!*pat, "BUG: something is very wrong in doesmatch()");
- if (*pat != Outbrack)
+ if (*pat != Outbrack) {
+ pptr = saves;
+ pat = savep;
break;
+ }
pat++;
- *pptr == Meta ? pptr += 2 : pptr++;
+ METAINC(pptr);
continue;
} else {
/* pattern is not negated (affirmed? asserted?) */
rangematch(&pat, ch, Inbrack);
DPUTS(!pat || !*pat, "BUG: something is very wrong in doesmatch()");
- if (*pat == Outbrack)
+ if (*pat == Outbrack) {
+ pptr = saves;
+ pat = savep;
break;
- for (*pptr == Meta ? pptr += 2 : pptr++;
- *pat != Outbrack; pat++);
+ }
+ for (METAINC(pptr); *pat != Outbrack; pat++);
pat++;
continue;
}
@@ -2892,7 +3087,7 @@ matchonce(Comp c)
if (*pat == Inang) {
/* Numeric globbing. */
unsigned long t1, t2, t3;
- char *ptr;
+ char *ptr, *saves = pptr, *savep = pat;
if (!idigit(*pptr))
break;
@@ -2933,19 +3128,146 @@ matchonce(Comp c)
pptr--;
t1 /= 10;
}
- if (t1 < t2 || (!not3 && t1 > t3))
+ if (t1 < t2 || (!not3 && t1 > t3)) {
+ pptr = saves;
+ pat = savep;
break;
+ }
}
continue;
}
- if (CHARMATCH(STOUC(*pptr), STOUC(*pat))) {
- /* just plain old characters */
- pptr++;
- pat++;
+ /* itok(Meta) is zero */
+ DPUTS(itok(*pat), "BUG: matching tokenized character");
+ if (charmatch(c, pptr, pat)) {
+ /* just plain old characters (or maybe unplain new characters) */
+ METAINC(pptr);
+ METAINC(pat);
continue;
}
+ if (errsfound < c->errsmax &&
+ (errsmax == -1 || errsfound < errsmax)) {
+ /*
+ * We tried to match literal characters and failed. Now it's
+ * time to match approximately. Note this doesn't handle the
+ * case where we *didn't* try to match literal characters,
+ * including the case where we were already at the end of the
+ * pattern, because then we never get here. In that case the
+ * pattern has to be matched exactly, but we could maybe
+ * advance up the target string before trying it, so we have to
+ * handle that case elsewhere.
+ */
+ char *saves = pptr, *savep = c->str;
+ char *maxpptr = pptr, *patnext = METANEXT(pat);
+ int savee, maxerrs = -1;
+
+ /* First try to advance up the pattern. */
+ c->str = patnext;
+ savee = ++errsfound;
+ if (matchonce(c)) {
+ maxpptr = pptr;
+ maxerrs = errsfound;
+ }
+ errsfound = savee;
+
+ if (*saves) {
+ /*
+ * If we have characters on both strings, we have more
+ * choice.
+ *
+ * Try to edge up the target string.
+ */
+ char *strnext = METANEXT(saves);
+ pptr = strnext;
+ c->str = pat;
+ if (matchonce(c) &&
+ (maxerrs == -1 || pptr > maxpptr ||
+ (pptr == maxpptr && errsfound <= maxerrs))) {
+ maxpptr = pptr;
+ maxerrs = errsfound;
+ }
+ errsfound = savee;
+
+ /*
+ * Try edging up both of them at once.
+ * Note this takes precedence in the case of equal
+ * length as we get further up the pattern.
+ */
+ c->str = patnext;
+ pptr = strnext;
+ if (matchonce(c) &&
+ (maxerrs == -1 || pptr > maxpptr ||
+ (pptr == maxpptr && errsfound <= maxerrs))) {
+ maxpptr = pptr;
+ maxerrs = errsfound;
+ }
+ errsfound = savee;
+
+ /*
+ * See if we can transpose: that counts as just one error.
+ *
+ * Note, however, `abanana' and `banana': transposing
+ * is wrong here, as it gets us two errors,
+ * [ab][a]nana vs [ba][]nana, instead of the correct
+ * [a]banana vs []banana, so we still need to try
+ * the other possibilities.
+ */
+ if (strnext && patnext && !itok(*patnext) &&
+ charmatch(c, strnext, pat) &&
+ charmatch(c, saves, patnext)) {
+ c->str = patnext;
+ METAINC(c->str);
+
+ pptr = strnext;
+ METAINC(pptr);
+
+ if (matchonce(c) &&
+ (maxerrs == -1 || pptr > maxpptr ||
+ (pptr == maxpptr && errsfound <= maxerrs))) {
+ maxpptr = pptr;
+ maxerrs = errsfound;
+ }
+ }
+ }
+ /*
+ * We don't usually restore state on failure, but we need
+ * to fix up the Comp structure which we altered to
+ * look at the tail of the pattern.
+ */
+ c->str = savep;
+ /*
+ * If that failed, game over: we don't want to break
+ * and try the other approximate test, because we just did
+ * that.
+ */
+ if (maxerrs == -1)
+ return 0;
+ pptr = maxpptr;
+ errsfound = maxerrs;
+ return 1;
+ }
break;
}
+ if (*pptr && errsfound < c->errsmax &&
+ (errsmax == -1 || errsfound < errsmax)) {
+ /*
+ * The pattern failed, but we can try edging up the target string
+ * and rematching with an error. Note we do this from wherever we
+ * got to in the pattern string c->str, not the start. hence the
+ * need to modify c->str.
+ *
+ * At this point, we don't have a literal character in the pattern
+ * (handled above), so we don't try any funny business on the
+ * pattern itself.
+ */
+ int ret;
+ char *savep = c->str;
+ errsfound++;
+ METAINC(pptr);
+ c->str = pat;
+ ret = matchonce(c);
+ c->str = savep;
+ return ret;
+ }
return 0;
}
@@ -2958,6 +3280,7 @@ parsereg(char *str)
remnulargs(str);
mode = 1; /* no path components */
addflags = 0;
+ errsmax = 0;
pptr = str;
tail = NULL;
return parsecompsw(GF_TOPLEV);
diff --git a/Src/hashtable.c b/Src/hashtable.c
index b816f1892..72e4db21b 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -607,6 +607,9 @@ hashdir(char **dirp)
Cmdnam cn;
DIR *dir;
char *fn;
+#ifdef _WIN32
+ char *exe;
+#endif
if (isrelative(*dirp) || !(dir = opendir(unmeta(*dirp))))
return;
@@ -618,6 +621,23 @@ hashdir(char **dirp)
cn->u.name = dirp;
cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn);
}
+#ifdef _WIN32
+ /* Hash foo.exe as foo, since when no real foo exists, foo.exe
+ will get executed by DOS automatically. This quiets
+ spurious corrections when CORRECT or CORRECT_ALL is set. */
+ if ((exe = strrchr(fn, '.')) &&
+ (exe[1] == 'E' || exe[1] == 'e') &&
+ (exe[2] == 'X' || exe[2] == 'x') &&
+ (exe[3] == 'E' || exe[3] == 'e') && exe[4] == 0) {
+ *exe = 0;
+ if (!cmdnamtab->getnode(cmdnamtab, fn)) {
+ cn = (Cmdnam) zcalloc(sizeof *cn);
+ cn->flags = 0;
+ cn->u.name = dirp;
+ cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn);
+ }
+ }
+#endif /* _WIN32 */
}
closedir(dir);
}
diff --git a/Src/module.c b/Src/module.c
index f91650a7f..1117571a4 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -567,6 +567,37 @@ load_module(char const *name)
return m;
}
+/* This ensures that the module with the name given as the second argument
+ * is loaded.
+ * The third argument should be non-zero if the function should complain
+ * about trying to load a module with a full path name in restricted mode.
+ * The last argument should be non-zero if this function should signal an
+ * error if the module is already loaded.
+ * The return value is the module of NULL if the module couldn't be loaded. */
+
+/**/
+Module
+require_module(char *nam, char *module, int res, int test)
+{
+ Module m = NULL;
+ LinkNode node;
+
+ node = find_module(module);
+ if (node && (m = ((Module) getdata(node)))->handle &&
+ !(m->flags & MOD_UNLOAD)) {
+ if (test) {
+ zwarnnam(nam, "module %s already loaded.", module, 0);
+ return NULL;
+ }
+ } else if (res && isset(RESTRICTED) && strchr(module, '/')) {
+ zwarnnam(nam, "%s: restricted", module, 0);
+ return NULL;
+ } else
+ return load_module(module);
+
+ return m;
+}
+
/**/
void
add_dep(char *name, char *from)
@@ -963,22 +994,10 @@ bin_zmodload_load(char *nam, char **args, char *ops)
return 0;
} else {
/* load modules */
- for (; *args; args++) {
- Module m;
-
- node = find_module(*args);
- if (node && (m = ((Module) getdata(node)))->handle &&
- !(m->flags & MOD_UNLOAD)) {
- if (!ops['i']) {
- zwarnnam(nam, "module %s already loaded.", *args, 0);
- ret = 1;
- }
- } else if (isset(RESTRICTED) && strchr(*args, '/')) {
- zwarnnam(nam, "%s: restricted", *args, 0);
+ for (; *args; args++)
+ if (!require_module(nam, *args, 1, (!ops['i'])))
ret = 1;
- } else if (!load_module(*args))
- ret = 1;
- }
+
return ret;
}
}
diff --git a/Src/params.c b/Src/params.c
index 77166209f..e8182815e 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -679,11 +679,13 @@ static char **garr;
static long
getarg(char **str, int *inv, Value v, int a2, long *w)
{
- int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i;
+ int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt;
long r = 0;
Comp c;
+ ishash = (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED);
+
/* first parse any subscription flags */
if (v->pm && (*s == '(' || *s == Inpar)) {
int escapes = 0;
@@ -771,12 +773,13 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
} else if (rev) {
v->isarr |= SCANPM_WANTVALS;
}
- if (!down && v->pm && PM_TYPE(v->pm->flags) == PM_HASHED)
+ if (!down && ishash)
v->isarr &= ~SCANPM_MATCHMANY;
*inv = ind;
}
- for (t=s, i=0; *t && ((*t != ']' && *t != Outbrack && *t != ',') || i); t++)
+ for (t=s, i=0;
+ *t && ((*t != ']' && *t != Outbrack && (ishash || *t != ',')) || i); t++)
if (*t == '[' || *t == Inbrack)
i++;
else if (*t == ']' || *t == Outbrack)
@@ -791,7 +794,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
singsub(&s);
if (!rev) {
- if (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) {
+ if (ishash) {
HashTable ht = v->pm->gets.hfn(v->pm);
if (!ht) {
ht = newparamtable(17, v->pm->nam);
@@ -867,7 +870,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
if ((c = parsereg(s))) {
if (v->isarr) {
- if (PM_TYPE(v->pm->flags) == PM_HASHED) {
+ if (ishash) {
scancomp = c;
if (ind)
v->isarr |= SCANPM_MATCHKEY;
diff --git a/Src/subst.c b/Src/subst.c
index 66c363145..2e9b84718 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -999,11 +999,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
if (!(v = fetchvalue((subexp ? &ov : &s), (wantt ? -1 :
((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
- hkeys|hvals)))
+ hkeys|hvals)) ||
+ (v->pm && (v->pm->flags & PM_UNSET)))
vunset = 1;
if (wantt) {
- if (v) {
+ if (v && v->pm && !(v->pm->flags & PM_UNSET)) {
int f = v->pm->flags;
switch (PM_TYPE(f)) {
diff --git a/Src/system.h b/Src/system.h
index 292943dd9..650690b51 100644
--- a/Src/system.h
+++ b/Src/system.h
@@ -70,8 +70,15 @@ char *alloca _((size_t));
# endif
#endif
-#ifdef HAVE_LIBC_H /* NeXT */
-# include <libc.h>
+/*
+ * libc.h in an optional package for Debian Linux is broken (it
+ * defines dup() as a synonym for dup2(), which has a different
+ * number of arguments), so just include it for next.
+ */
+#ifdef __NeXT__
+# ifdef HAVE_LIBC_H
+# include <libc.h>
+# endif
#endif
#ifdef HAVE_SYS_TYPES_H
diff --git a/Src/zsh.export b/Src/zsh.export
index bfad7aea2..d31e7902e 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -173,6 +173,7 @@ refreshptr
remlpaths
remnulargs
removehashnode
+require_module
resetneeded
restoredir
reswdtab
diff --git a/acconfig.h b/acconfig.h
index 77110b457..506441635 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -196,10 +196,13 @@
/* Define to 1 if system has working FIFO's */
#undef HAVE_FIFOS
-/* Define to 1 if struct rlimit use quad_t */
+/* Define to 1 if struct rlimit uses quad_t */
#undef RLIM_T_IS_QUAD_T
-/* Define to 1 if rlimit use unsigned */
+/* Define to 1 if struct rlimit uses long long */
+#undef RLIM_T_IS_LONG_LONG
+
+/* Define to 1 if rlimit uses unsigned */
#undef RLIM_T_IS_UNSIGNED
/* Define to the type used in struct rlimit */
diff --git a/configure.in b/configure.in
index e41bf47d6..dd6bfee4b 100644
--- a/configure.in
+++ b/configure.in
@@ -673,7 +673,8 @@ dnl The backslash substitution is to persuade cygwin to cough up
dnl slashes rather than doubled backslashes in the path.
echo "#include <signal.h>" > nametmp.c
sigfile_list="`$CPP nametmp.c |
-sed -n -e 's/^#[ ].*\"\(.*\)\"/\1/p' -e 's/\\\\\\\\/\//g' |
+sed -n 's/^#[ ].*\"\(.*\)\"/\1/p' |
+sed 's/\\\\\\\\/\//g' |
$AWK '{ if (\$1 ~ \"sig\") files[[\$1]] = \$1 }
END { for (var in files) print var }'`"
rm -f nametmp.c
@@ -681,6 +682,7 @@ if test -z "$sigfile_list"; then
dnl In case we don't get the stuff from the preprocesor, use the old
dnl list of standard places.
sigfile_list="/usr/include/bsd/sys/signal.h
+/usr/include/signum.h
/usr/include/asm/signum.h
/usr/include/asm/signal.h
/usr/include/linux/signal.h
@@ -689,9 +691,13 @@ if test -z "$sigfile_list"; then
fi
for SIGNAL_H in $sigfile_list
do
- test -f $SIGNAL_H && \
- grep '#[ ]*define[ ][ ]*SIG[0-9A-Z]*[ ]*[0-9][0-9]*' $SIGNAL_H > /dev/null && \
- break
+ dnl Try to make sure it doesn't get confused by files that don't
+ dnl have real signal definitions in, but do #define SIG* by counting
+ dnl the number of signals. Maybe we could even check for e.g. SIGHUP?
+ nsigs=`test -f $SIGNAL_H && \
+ grep '#[ ]*define[ ][ ]*SIG[0-9A-Z]*[ ]*[0-9][0-9]*' $SIGNAL_H | \
+ wc -l | sed 's/[ ]//g'`
+ test "x$nsigs" != x && test "$nsigs" -ge 7 && break
done
zsh_cv_path_signal_h=$SIGNAL_H
])
@@ -729,20 +735,43 @@ dnl ------------------
dnl rlimit type checks
dnl ------------------
DEFAULT_RLIM_T=long
-AC_CACHE_CHECK(if rlim_t is quad_t,
-zsh_cv_rlim_t_is_quad_t,
+AC_CACHE_CHECK(if rlim_t is longer than a long,
+zsh_cv_rlim_t_is_longer,
[AC_TRY_RUN([
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/resource.h>
main(){struct rlimit r;exit(sizeof(r.rlim_cur) <= sizeof(long));}],
-zsh_cv_rlim_t_is_quad_t=yes,
-zsh_cv_rlim_t_is_quad_t=no,
-zsh_cv_rlim_t_is_quad_t=yes)])
-if test $zsh_cv_rlim_t_is_quad_t = yes; then
- AC_DEFINE(RLIM_T_IS_QUAD_T)
- DEFAULT_RLIM_T=quad_t
+zsh_cv_rlim_t_is_longer=yes,
+zsh_cv_rlim_t_is_longer=no,
+zsh_cv_rlim_t_is_longer=yes)])
+if test $zsh_cv_rlim_t_is_longer = yes; then
+ AC_CACHE_CHECK(if rlim_t is a quad,
+ zsh_cv_rlim_t_is_quad_t,
+ [AC_TRY_RUN([
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <stdio.h>
+#include <sys/resource.h>
+main() {
+ struct rlimit r;
+ char buf[[20]];
+ r.rlim_cur = 0;
+ sprintf(buf, "%qd", r.rlim_cur);
+ exit(strcmp(buf, "0"));
+}],
+ zsh_cv_rlim_t_is_quad_t=yes,
+ zsh_cv_rlim_t_is_quad_t=no,
+ zsh_cv_rlim_t_is_quad_t=no)])
+ if test $zsh_cv_tlim_t_is_quad_t = yes; then
+ AC_DEFINE(RLIM_T_IS_QUAD_T)
+ DEFAULT_RLIM_T=quad_t
+ else
+ AC_DEFINE(RLIM_T_IS_LONG_LONG)
+ DEFAULT_RLIM_T='long long'
+ fi
else
AC_CACHE_CHECK(if the rlim_t is unsigned,
zsh_cv_type_rlim_t_is_unsigned,
@@ -1113,7 +1142,6 @@ main()
elif test "x$zsh_cv_func_dlsym_needs_underscore" != xno; then
dnl Do not cache failed value
unset zsh_cv_func_dlsym_needs_underscore
- dynamic=no
fi
fi
diff --git a/patchlist.txt b/patchlist.txt
index 9ec40d8dd..0497dee80 100644
--- a/patchlist.txt
+++ b/patchlist.txt
@@ -299,7 +299,7 @@ Phil: zless, 5032, simplified by Bart, 5037, also added a `setopt
localoptions' after spending an hour wondering why nothing worked any
more.
-Me: `make install' does not do `make install.info', 5047
+pws: `make install' does not do `make install.info', 5047
Sven: compcall tries old-style completion from new-style function,
compctl -K ' func' handles newstyle completion, 5059; avoid recursion,
@@ -309,7 +309,7 @@ Sven: inserting completion inside brace expansion, 5060
Sven: extra completion context, 5092
-Me: typeset -T MYPATH mypath, 5094, plus fix for MYPATH=(foo),
+pws: typeset -T MYPATH mypath, 5094, plus fix for MYPATH=(foo),
mypath=foo (and also existing PATH=(foo) bug), 5120
Sven: doc fix for glob qualifiers, 5102
@@ -320,18 +320,18 @@ Drazen Kacar, modified by me: workaround for terminal bug on Solaris,
Sven: zle and widget information via variables in new completion
functions, 5104
-Me: remove old zle -C, zle -C now does new completion, 5105
+pws: remove old zle -C, zle -C now does new completion, 5105
Sven: glob qualifier o for modes, 5107
-Me: fix for unsetting special zle variables, 5111
+pws: fix for unsetting special zle variables, 5111
Drazen Kacar, modified by me: unlock terminal device on Solaris, 5118
(5117 was wrong)
pws-7
-Me: patch for zls, 5054 (appeared in pws-6 but not in corresponding
+pws: patch for zls, 5054 (appeared in pws-6 but not in corresponding
patchlist).
Bart: finally added missing hunk from 4965 which allowed unsetting an
@@ -355,7 +355,7 @@ Sven: fix for command completion and pattern completions, 5178
Sven: ${(P)...} 5183, 5199, 5200
-Me: compctl documentation tidy-up, 5185, 5198
+pws: compctl documentation tidy-up, 5185, 5198
Sven: zle commands which use the minibuffer erase completion listings,
5201
@@ -370,13 +370,13 @@ Sven: ${foo:q}, 5208, preliminary
Sven: use ${foo:q} for quoting prefix and suffix in new completion, 5120
-Me: bashautolist option, 5229; Sven's addition, 5234, and doc 5235; 5269
+pws: bashautolist option, 5229; Sven's addition, 5234, and doc 5235; 5269
-Me: .zlogout doc, 5233
+pws: .zlogout doc, 5233
-Me: added note on Linux Alpha with egcs to Etc/MACHINES, not posted
+pws: added note on Linux Alpha with egcs to Etc/MACHINES, not posted
-Me: typeset -T fix, 5247
+pws: typeset -T fix, 5247
Bart: parameter scoping docs, 5258
@@ -390,7 +390,7 @@ Sven: rewrite of $foo:q, 5265, +doc, 5284
Sven: get matcher number in new completion function, 5266
-Me: interrupts in getquery() weren't gracefully handled, 5281
+pws: interrupts in getquery() weren't gracefully handled, 5281
pws-8
@@ -409,14 +409,14 @@ Sven: compctl matcher to use reference counts, 5316
Sven: keys available in zle widget functions, 5320
-Me: compctl -LM, 5321
+pws: compctl -LM, 5321
-Me: revamped signames.c generation, 5326, 5329, plus Matt fix, 5330
+pws: revamped signames.c generation, 5326, 5329, plus Matt fix, 5330
Sweth, Bart, Me: Functions/allopt, basically as in 2121 with the odd
emulate and local.
-Me: emulate -L, 5332
+pws: emulate -L, 5332
Sven: printing of zle condition codes, 5335
@@ -425,9 +425,9 @@ Sven: Modularisation of new completion shell code, 5341
Sven: ignoring ignored prefix in new conditions, 5342; related fixes,
5343
-Me: patch for completion init and __normal, 5344; Sven fix, 5351
+pws: patch for completion init and __normal, 5344; Sven fix, 5351
-Me: "$foo[@]" didn't remove the argument if $foo wasn't set, 5349;
+pws: "$foo[@]" didn't remove the argument if $foo wasn't set, 5349;
Bart's fix so this works on OSes other than AIX, 5361
Sven: change fignore handling, 5352
@@ -437,14 +437,14 @@ failed, 5354
Sven: compadd -R function for suffix removal, 5355
-Me: #key-* completions now allow 0 or more key bindings, 5362
+pws: #key-* completions now allow 0 or more key bindings, 5362
-Me: Moved Misc/Completion to Functions/Completion; added some of my own
+pws: Moved Misc/Completion to Functions/Completion; added some of my own
new-style completions: not posted
-Me: 5281 now works, 5364
+pws: 5281 now works, 5364
-Me: make dependencies for main.o, Makemod, zshpaths.h, 5365
+pws: make dependencies for main.o, Makemod, zshpaths.h, 5365
pws-9
@@ -458,9 +458,9 @@ Andrej: Reliant UNIX configuration, 5377
Sven: Manual for new completion (so far), 5384, 5397
-Me: dump new completion status for fast initialisation, 5393
+pws: dump new completion status for fast initialisation, 5393
-Me: bug fixlet for __path_files, 5398
+pws: bug fixlet for __path_files, 5398
Sven: overhaul of do_ambiguous with some bug-fixage, 5399, 5407
@@ -469,11 +469,11 @@ $COMPDUMP file, 5402
Sven: files -> __files, 5401
-Me: magicequalsubst now affects all arguments ...=~...:~..., 5403
+pws: magicequalsubst now affects all arguments ...=~...:~..., 5403
-Me: set -x output for [[ ... ]], 5408
+pws: set -x output for [[ ... ]], 5408
-Me: IRIX 6.5 problems in Etc/MACHINES (see 5410 from Helmut Jarausch).
+pws: IRIX 6.5 problems in Etc/MACHINES (see 5410 from Helmut Jarausch).
Sven: 5412: better matcher control.
@@ -483,7 +483,7 @@ 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
+pws: 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
@@ -492,14 +492,73 @@ 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
+pws: 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 ((...)).
+pws: 5436: set -x for function calls and ((...)).
-Me: unposted (but see 5440): zftp changes: more return 6's, functions now
+pws: unposted (but see 5440): zftp changes: more return 6's, functions now
do auto-open and avoid subshells.
+
+ pws-10
+
+Martin Buchholz: 5448: libc.h can't be included on Debian Linux, so only
+include it on NeXT where it's necessary.
+
+Matt: 5330: I've put this back the way it original was. I hate sed almost
+as much as awk.
+
+Sven: 5455: keep track of which matcher specification to use
+
+Sven: 5466: compwid manual for -after and -between
+
+Sven: 5467: expn manual typo
+
+Sven: 5469: init fix and Functions/Completion/_comp_parts
+
+Sven: 5470: new completion conditions didn't handle untokenization
+consistently.
+
+Sven: 5471: range code knows not to handle associative arrays
+
+Sven: 5476: quoting of tildes in Functions/Completion/_path_files
+
+Sven: 5483: completeinword fixes
+
+Sven: 5489: control for matching in _path_files and _comp_parts
+
+Sven: 5490: unset test for AA elements when substituting
+
+pws: unposted, see 5503: remove dynamic=no from configure.in when
+underscore is needed.
+
+pws: 5508: init and dump, globbing and printing.
+
+Sven: 5511: make sure compctl is available for new completion
+
+Sven: 5512, 5525: globcomplete fix for new completion
+
+Sven: 5521: improved option handling for _path_files
+
+Sven: 5529: cleanup for Functions/Completion
+
+pws: 5531: small init fix
+
+pws: 5538: approximate pattern matching, (#a1)readme etc.
+
+Sven: 5543: compadd -X, zshcompwid manual
+
+Sven: 5544: another completion cleanup
+
+pws: 5545: silly set -x mistake
+
+Sven: 5548: _path_files, _comp_parts
+
+Matt: 5553: under _WIN32, .exe suffix is optional for commands
+
+pws: unposted: Functions/Completion moved to Completion; subdirectories
+Core, Base, Builtins, User, Commands created; Completion/README created.