about summary refs log tree commit diff
path: root/Misc
diff options
context:
space:
mode:
Diffstat (limited to 'Misc')
-rw-r--r--Misc/.distfiles4
-rw-r--r--Misc/.lastloc2
-rwxr-xr-xMisc/c2z128
-rw-r--r--Misc/compctl-examples716
-rwxr-xr-xMisc/globtests107
-rwxr-xr-xMisc/globtests.ksh91
-rwxr-xr-xMisc/lete2ctl301
7 files changed, 1349 insertions, 0 deletions
diff --git a/Misc/.distfiles b/Misc/.distfiles
new file mode 100644
index 000000000..a02614511
--- /dev/null
+++ b/Misc/.distfiles
@@ -0,0 +1,4 @@
+DISTFILES_SRC='
+    .distfiles
+    c2z compctl-examples globtests globtests.ksh lete2ctl
+'
diff --git a/Misc/.lastloc b/Misc/.lastloc
new file mode 100644
index 000000000..8ba7dd946
--- /dev/null
+++ b/Misc/.lastloc
@@ -0,0 +1,2 @@
+(("/home/user2/pws/src/zsh-3.1.5/Misc/globtests.ksh" . 2763)
+ ("/home/user2/pws/src/zsh-3.1.5/Misc/globtests" . 3123))
diff --git a/Misc/c2z b/Misc/c2z
new file mode 100755
index 000000000..8a90d5fd8
--- /dev/null
+++ b/Misc/c2z
@@ -0,0 +1,128 @@
+#! /bin/sh
+#
+# c2z - environment conversion tool
+# Contributed by Bart Schaefer
+# (Tweaked a bit by Paul Falstad)
+#
+# This is a quick script to convert csh aliases to zsh aliases/functions.
+# It also converts the csh environment and local variables to zsh.  c2z
+# uses the csh to parse its own dot-files, then processes csh output to
+# convert the csh settings to zsh.
+#
+# When run as a zsh fuction, c2z runs csh as if it were an interactive
+# shell whenever the parent zsh is interactive.  When run as a shell
+# script, the -i switch can be used to force this behavior.
+#
+# The -l (login) switch causes csh to run as if it were a login shell.
+# This is done "properly" if c2z is used as a zsh function, otherwise
+# it's faked by explicitly sourcing .login.  Use with caution if your
+# .login initializes an X server or does other one-time-only startup
+# procedures.
+#
+# usage:
+#	c2z [-i | -l | filename]
+#
+# You can use this script in your .zshrc or .zlogin files to load your
+# regular csh environment into zsh; for example, in .zlogin:
+#
+#	. =(c2z -l)
+#
+# This is not perfect, but it gets most common aliases and variables.
+# It's also rather time-consuming to do this every time you log in.
+# However, if you're moving from csh to zsh for the first time, this
+# can get you started with a familiar environment right away.
+#
+# In case your mailer eats tabs, $T is set to expand to a tab.
+#
+T="`echo x | tr x '\011'`"
+
+# If we're zsh, we can run "- csh" to get the complete environment.
+#
+MINUS=""
+LOADFILE=""
+INTERACT=""
+CSH=csh
+case "$ZSH_NAME$ZSH_VERSION$VERSION" in
+zsh*)
+    case $1 in
+    -l*) MINUS="-" ;;
+    -i*) INTERACT="-i" ;;
+    *) LOADFILE="source $1" CSH="csh -f";;
+    esac
+    if [[ -o INTERACTIVE ]]; then INTERACT="-i"; fi
+    setopt nobanghist
+    ;;
+*)
+    case $1 in
+    -l*) LOADFILE="source ~/.login" ;;
+    -i*) INTERACT="-i" ;;
+    *) LOADFILE="source $1" CSH="csh -f";;
+    esac
+    ;;
+esac
+
+( eval $MINUS $CSH $INTERACT ) <<EOF 2>&1 >/dev/null
+$LOADFILE
+alias >! /tmp/cz$$.a
+setenv >! /tmp/cz$$.e
+set >! /tmp/cz$$.v
+EOF
+
+# save stdin
+exec 9<&0
+
+# First convert aliases
+exec < /tmp/cz$$.a
+
+# Taken straight from ctoz except for $T and "alias --"
+sed -e 's/'"$T"'(\(.*\))/'"$T"'\1/' >/tmp/cz$$.1
+grep ! /tmp/cz$$.1 >/tmp/cz$$.2
+grep -v ! /tmp/cz$$.1 >/tmp/cz$$.3
+sed -e "s/'/'"\\\\"''"/g \
+    -e 's/^\([^'"$T"']*\)'"$T"'\(.*\)$/alias -- \1='"'\2'/" \
+    /tmp/cz$$.3
+sed -e 's/![:#]*/$/g' \
+    -e 's/\$cwd/$PWD/' \
+    -e 's/^\([^'"$T"']*\)'"$T"'\(.*\)$/\1 () { \2 }/' \
+    /tmp/cz$$.2
+
+# Next, convert environment variables
+exec < /tmp/cz$$.e
+
+# Would be nice to deal with embedded newlines, e.g. in TERMCAP, but ...
+sed -e '/^SHLVL/d' \
+    -e '/^PWD/d' \
+    -e "s/'/'"\\\\"''"/g \
+    -e "s/^\([A-Za-z0-9_]*=\)/export \1'/" \
+    -e "s/$/'/"
+
+# Finally, convert local variables
+exec < /tmp/cz$$.v
+
+sed -e 's/'"$T"'/=/' \
+    -e "s/'/'"\\\\"''"/g \
+    -e '/^[A-Za-z0-9_]*=[^(]/{
+	s/=/='"'/"'
+	s/$/'"'/"'
+	}' |
+sed -e '/^argv=/d' -e '/^cwd=/d' -e '/^filec=/d' -e '/^status=/d' \
+	 -e '/^autolist=/s/.*/setopt autolist/' \
+	 -e '/^correct=all/s//setopt correctall/' \
+	 -e '/^correct=/s//setopt correct/' \
+	 -e '/^histchars=/s//HISTCHARS=/' \
+	 -e '/^history=/s//HISTSIZE=/' \
+	 -e '/^home=/s//HOME=/' \
+	 -e '/^ignoreeof=/s/.*/setopt ignoreeof/' \
+	 -e '/^noclobber=/s/.*/setopt noclobber/' \
+	 -e '/^notify=/d' \
+	 -e '/^prompt=/s/!/%h/' \
+	 -e 's/^prompt/PROMPT/' \
+	 -e '/^showdots=/s/.*/setopt globdots/' \
+	 -e '/^savehist=/s//HISTFILE=\~\/.zhistory SAVEHIST=/' \
+	 -e '/^who=/s//WATCHFMT=/'
+
+
+exec 0<&9
+
+rm /tmp/cz$$.?
+exit
diff --git a/Misc/compctl-examples b/Misc/compctl-examples
new file mode 100644
index 000000000..e84a37fbb
--- /dev/null
+++ b/Misc/compctl-examples
@@ -0,0 +1,716 @@
+#
+# This file gives examples of possible programmable completions (compctl).
+# You can either put the compctl commands in your .zshrc file, or put them
+# in a separate file (say .zcompctl) and source it from your .zshrc file.
+#
+# These are just examples. Use and modify to personal taste.  Copying
+# this file without thought will needlessly increase zsh's memory usage
+# and startup time.
+#
+# For a detailed description of how these work, check the zshcompctl man
+# page.
+#
+#------------------------------------------------------------------------------
+hosts=("${${(s: :)${(s:	:)${${(f)$(</etc/hosts)}%%\#*}#*[ 	]*}}:#}")
+ports=( "${${${(f)$(</etc/services)}:#\#*}%%[ 	]*}" )
+
+# groups=( $(cut -d: -f1 /etc/group) )
+# groups=( $(ypcat group.byname | cut -d: -f1) ) # if you use NIS
+
+# It can be done without forking, but it used too much memory in old zsh's:
+groups=( "${${(f)$(</etc/group)}%%:*}" )
+#groups=( "${${(f)$(ypcat groups)}%%:*}" ) # if you use NIS
+
+# Completion for zsh builtins.
+compctl -z -P '%' bg
+compctl -j -P '%' fg jobs disown
+compctl -j -P '%' + -s '`ps -x | tail +2 | cut -c1-5`' wait
+compctl -A shift
+compctl -c type whence where which
+compctl -m -x 'W[1,-*d*]' -n - 'W[1,-*a*]' -a - 'W[1,-*f*]' -F -- unhash
+compctl -m -q -S '=' -x 'W[1,-*d*] n[1,=]' -/ - \
+	'W[1,-*d*]' -n -q -S '=' - 'n[1,=]' -/g '*(*)' -- hash
+compctl -F functions unfunction
+compctl -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)' echotc
+compctl -a unalias
+compctl -v getln getopts read unset vared
+compctl -v -S '=' -q declare export integer local readonly typeset
+compctl -eB -x 'p[1] s[-]' -k '(a f m r)' - \
+	'C[1,-*a*]' -ea - 'C[1,-*f*]' -eF - 'C[-1,-*r*]' -ew -- disable
+compctl -dB -x 'p[1] s[-]' -k '(a f m r)' - \
+	'C[1,-*a*]' -da - 'C[1,-*f*]' -dF - 'C[-1,-*r*]' -dw -- enable
+compctl -k "(${(j: :)${(f)$(limit)}%% *})" limit unlimit
+compctl -l '' -x 'p[1]' -f -- . source
+# Redirection below makes zsh silent when completing unsetopt xtrace
+compctl -s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)' + -o + -x 's[no]' -o -- unsetopt
+compctl -s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)' + -o + -x 's[no]' -o -- setopt
+compctl -s '${^fpath}/*(N:t)' autoload
+compctl -b -x 'W[1,-*[DAN]*],C[-1,-*M]' -s '$(bindkey -l)' -- bindkey
+compctl -c -x 'C[-1,-*k]' -A - 'C[-1,-*K]' -F -- compctl
+compctl -x 'C[-1,-*e]' -c - 'C[-1,-[ARWI]##]' -f -- fc
+compctl -x 'p[1]' - 'p[2,-1]' -l '' -- sched
+compctl -x 'C[-1,[+-]o]' -o - 'c[-1,-A]' -A -- set
+compctl -b -x 'w[1,-N] p[3]' -F -- zle
+compctl -s '${^module_path}/*(N:t:r)' -x \
+	'W[1,-*(a*u|u*a)*],W[1,-*a*]p[3,-1]' -B - \
+	'W[1,-*u*]' -s '$(zmodload)' -- zmodload
+
+# Anything after nohup is a command by itself with its own completion
+compctl -l '' nohup noglob exec nice eval - time rusage
+compctl -l '' -x 'p[1]' -eB -- builtin
+compctl -l '' -x 'p[1]' -em -- command
+compctl -x 'p[1]' -c - 'p[2,-1]' -k signals -- trap
+#------------------------------------------------------------------------------
+# kill takes signal names as the first argument after -, but job names after %
+# or PIDs as a last resort
+compctl -j -P '%' + -s '`ps -x | tail +2 | cut -c1-5`' + \
+	-x 's[-] p[1]' -k "($signals[1,-3])" -- kill
+#------------------------------------------------------------------------------
+compctl -s '$(groups)' + -k groups newgrp
+compctl -f -x 'p[1], p[2] C[-1,-*]' -k groups -- chgrp
+compctl -f -x 'p[1] n[-1,.], p[2] C[-1,-*] n[-1,.]' -k groups - \
+	'p[1], p[2] C[-1,-*]' -u -S '.' -q -- chown
+compctl -/g '*.x' rpcgen
+compctl -u -x 's[+] c[-1,-f],s[-f+]' -W ~/Mail -f - \
+	's[-f],c[-1,-f]' -f -- mail elm
+compctl -x 'c[-1,-f]' -W ~/Mail -f -- pine
+#------------------------------------------------------------------------------
+compctl -s "\$(awk '/^[a-zA-Z0-9][^ 	]+:/ {print \$1}' FS=: [mM]akefile)" -x \
+	'c[-1,-f]' -f -- make gmake pmake
+#------------------------------------------------------------------------------
+# tar
+tarnames () {
+	# Completion function for use with tar:
+	# get the names of files in the tar archive to extract.
+	#
+	# The main claim to fame of this particular function is that it
+	# completes directories in the tar-file in a manner very roughly
+	# consistent with `compctl -f'.  There are two bugs:  first, the whole
+	# path prefix up to the present is listed with ^D, not just the new
+	# part to add; second, after a unique completion a space is always
+	# inserted, even if the completion ends with a slash.  These are
+	# currently limitations of zsh.
+	#
+	# This only works for the (fairly common) tar argument style where
+	# the arguments are bunched into the first argument, and the second
+	# argument is the name of the tarfile.  For example,
+	#  tar xvzf zsh-3.1.2.tar.gz ...
+	# You can only use compressed/gzipped files if tar is GNU tar,
+	# although the correct name for the tar programme will be inferred.
+
+	local line list=tf
+	read -cA line
+	# $line[2] is the first argument:  check for possible compression args.
+	# (This is harmless when used with non-GNU tar, but then the file must
+	# be uncompressed to be able to use it with tar anyway.)
+	[[ $line[2] = *[Zz]* ]] && list=tfz
+	# $line[1] is the command name:  something like tar or gnutar.
+	# $line[3] is the name of the tar archive.
+
+	# cache contents for multiple completion: note tar_cache_name
+	# and tar_cache_list are not local.  Assumes all files with the
+	# same name are the same file, even if in different directories:
+	# you can trick it with $PWD/file on the command line.
+	if [[ $line[3] != $tar_cache_name ]]; then
+	  tar_cache_list=($($line[1] $list $line[3]))
+	  tar_cache_name=$line[3]
+	fi
+
+	# Now prune the list to include only appropriate directories.
+	local file new
+	reply=()
+	if [[ $1 = */* ]]; then
+	  local sofar=${1%/*}/
+	  for file in $tar_cache_list; do
+	    if [[ $file = $sofar* ]]; then
+	      new=${file#$sofar}
+	      if [[ $new = */* ]]; then
+		new=$sofar${new%%/*}/
+	      else
+		new=$file
+	      fi
+	      if [[ $1 != */ || $new != $1 ]]; then
+		reply=($reply $new)
+	      fi
+	    fi
+	  done
+	else
+	  for file in $tar_cache_list; do
+	    if [[ $file = */* ]]; then
+	      new=${file%%/*}/
+	    else
+	      new=$file
+	    fi
+	    reply=($reply $new)
+	  done
+	fi
+}
+
+compctl -f \
+	-x 'p[2] C[-1,*(z*f|f*z)*]' -/g '*.(taz|tar.(gz|z|Z)|tgz)' \
+	- 'p[2] C[-1,*(Z*f|f*Z)*]' -/g '*.(tar.Z|taz)' \
+	- 'p[2] C[-1,*f*]' -/g '*.tar' \
+	- 'p[1] N[-1,ctxvzZ]' -k "(v z f)" \
+	- 'p[1] s[]' -k "(c t x)" -S '' \
+	- 'p[3,-1] W[1,*x*]' -K tarnames \
+	-- tar gtar gnutar
+#------------------------------------------------------------------------------
+# rmdir only real directories
+compctl -/g '*(/)' rmdir dircmp
+#------------------------------------------------------------------------------
+# cd/pushd only directories or symbolic links to directories
+compctl -/ cd chdir dirs pushd
+
+# Another possibility for cd/pushd is to use it in conjunction with the
+# cdmatch function (in the Functions subdirectory of zsh distribution).
+compctl -K cdmatch -S '/' -q -x 'p[2]' -Q -K cdmatch2 - \
+	'S[/][~][./][../]' -g '*(-/)' + -g '*(-/D)' - \
+	'n[-1,/]' -K cdmatch -S '/' -q -- cd chdir pushd
+#------------------------------------------------------------------------------
+# If the command is rsh, make the first argument complete to hosts and treat the
+# rest of the line as a command on its own.
+compctl -k hosts -x 'p[2,-1]' -l '' -- rsh
+
+# rlogin takes hosts and users after `-l'
+compctl -k hosts -x 'c[-1,-l]' -u -- rlogin
+
+# rcp: match users, hosts and files initially.  Match files after a :, or hosts
+# after an @.  If one argument contains a : then the other matches files only.
+# Not quite perfect; compctl just isn't up to it yet.
+compctl -u -k hosts -f -x 'n[1,:]' -f - 'n[1,@]' -k hosts -S ':' - \
+	'p[1] W[2,*:*]' -f - 'p[1] W[2,*?*]' -u -k hosts -S ':' - \
+	'p[2] W[1,*:*]' -f - 'p[2] W[1,*?*]' -u -k hosts -S ':' -- rcp
+
+compctl -k hosts host rup rusers ping
+#------------------------------------------------------------------------------
+# strip, profile, and debug only executables.  The compctls for the
+# debuggers could be better, of course.
+compctl -/g '*(*)' strip gprof adb dbx xdbx ups
+compctl -/g '*.[ao]' -/g '*(*)' nm
+#------------------------------------------------------------------------------
+# shells: compctl needs some more enhancement to do -c properly.
+compctl -f -x 'C[-1,-*c]' -c - 'C[-1,[-+]*o]' -o -- bash ksh sh zsh
+#------------------------------------------------------------------------------
+# su takes a username and args for the shell.
+compctl -u -x 'w[1,-]p[3,-1]' -l sh - 'w[1,-]' -u - 'p[2,-1]' -l sh -- su
+#------------------------------------------------------------------------------
+# Run ghostscript on postscript files, but if no postscript file matches what
+# we already typed, complete directories as the postscript file may not be in
+# the current directory.
+compctl -/g '*.(e|E|)(ps|PS)' \
+	gs ghostview nup psps pstops psmulti psnup psselect
+#------------------------------------------------------------------------------
+# Similar things for tex, texinfo and dvi files.
+compctl -/g '*.tex*' {,la,gla,ams{la,},{g,}sli}tex texi2dvi
+compctl -/g '*.dvi' xdvi dvips
+#------------------------------------------------------------------------------
+# For rcs users, co and rlog from the RCS directory.  We don't want to see
+# the RCS and ,v though.
+compctl -g 'RCS/*(:s@RCS/@@:s/,v//)' co rlog rcs rcsdiff
+#------------------------------------------------------------------------------
+# gzip uncompressed files, but gzip -d only gzipped or compressed files
+compctl -x 'R[-*[dt],^*]' -/g '*.(gz|z|Z|t[agp]z|tarZ|tz)' + -f - \
+    's[]' -/g '^*(.(tz|gz|t[agp]z|tarZ|zip|ZIP|jpg|JPG|gif|GIF|[zZ])|[~#])' \
+    + -f -- gzip
+compctl -/g '*.(gz|z|Z|t[agp]z|tarZ|tz)' gunzip gzcat zcat
+compctl -/g '*.Z' uncompress zmore
+compctl -/g '*.F' melt fcat
+#------------------------------------------------------------------------------
+# ftp takes hostnames
+ftphosts=(prep.ai.mit.edu wuarchive.wustl.edu ftp.uu.net ftp.math.gatech.edu)
+compctl -k ftphosts ftp
+
+# Some systems have directories containing indices of ftp servers.
+# For example: we have the directory /home/ftp/index/INDEX containing
+# files of the form `<name>-INDEX.Z', this leads to:
+#compctl -g '/home/ftp/index/INDEX/*-INDEX.Z(:t:r:s/-INDEX//)' ftp tftp
+#------------------------------------------------------------------------------
+# Change default completion (see the multicomp function in the Function
+# subdirectory of the zsh distribution).
+#compctl -D -f + -U -K multicomp
+# If completion of usernames is slow for you, you may want to add something
+# like
+#    -x 'C[0,*/*]' -f - 's[~]' -S/ -k users + -u
+# where `users' contains the names of the users you want to complete often.
+# If you want to use this and to be able to complete named directories after
+# the `~' you should add `+ -n' at the end
+#------------------------------------------------------------------------------
+# This is to complete all directories under /home, even those that are not
+# yet mounted (if you use the automounter).
+
+# This is for NIS+ (e.g. Solaris 2.x)
+#compctl -Tx 's[/home/] C[0,^/home/*/*]'  -S '/' -s '$(niscat auto_home.org_dir | \
+#	awk '\''/export\/[a-zA-Z]*$/ {print $NF}'\'' FS=/)'
+
+# And this is for YP (e.g. SunOS4.x)
+#compctl -Tx 's[/home/] C[0,^/home/*/*]' -S '/' -s '$(ypcat auto.home | \
+#	awk '\''/export\/[a-zA-Z]*$/ {print $NF}'\'' FS=/)'
+#------------------------------------------------------------------------------
+# Find is very system dependent, this one is for GNU find.
+# Note that 'r[-exec,;]' must come first
+if [[ -r /proc/filesystems ]]; then
+    # Linux
+    filesystems='"${${(f)$(</proc/filesystems)}#*	}"'
+else
+    filesystems='ufs 4.2 4.3 nfs tmp mfs S51K S52K'
+fi
+compctl -x 'r[-exec,;][-ok,;]' -l '' - \
+'s[-]' -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' - \
+	'p[1]' -g '. .. *(-/)' - \
+	'C[-1,-((a|c|)newer|fprint(|0|f))]' -f - \
+	'c[-1,-fstype]' -s $filesystems - \
+	'c[-1,-group]' -k groups - \
+	'c[-1,-user]' -u -- find
+#------------------------------------------------------------------------------
+# Generic completion for C compiler.
+compctl -/g "*.[cCoa]" -x 's[-I]' -/ - \
+	's[-l]' -s '${(s.:.)^LD_LIBRARY_PATH}/lib*.a(:t:r:s/lib//)' -- cc
+#------------------------------------------------------------------------------
+# GCC completion, by Andrew Main
+# completes to filenames (*.c, *.C, *.o, etc.); to miscellaneous options after
+# a -; to various -f options after -f (and similarly -W, -g and -m); and to a
+# couple of other things at different points.
+# The -l completion is nicked from the cc compctl above.
+# The -m completion should be tailored to each system; the one below is i386.
+compctl -/g '*.([cCmisSoak]|cc|cxx|ii|k[ih])' -x \
+	's[-l]' -s '${(s.:.)^LD_LIBRARY_PATH}/lib*.a(:t:r:s/lib//)' - \
+	'c[-1,-x]' -k '(none c objective-c c-header c++ cpp-output
+	assembler assembler-with-cpp)' - \
+	'c[-1,-o]' -f - \
+	'C[-1,-i(nclude|macros)]' -/g '*.h' - \
+	'C[-1,-i(dirafter|prefix)]' -/ - \
+	's[-B][-I][-L]' -/ - \
+	's[-fno-],s[-f]' -k '(all-virtual cond-mismatch dollars-in-identifiers
+	enum-int-equiv external-templates asm builtin strict-prototype
+	signed-bitfields signd-char this-is-variable unsigned-bitfields
+	unsigned-char writable-strings syntax-only pretend-float caller-saves
+	cse-follow-jumps cse-skip-blocks delayed-branch elide-constructors
+	expensive-optimizations fast-math float-store force-addr force-mem
+	inline-functions keep-inline-functions memoize-lookups default-inline
+	defer-pop function-cse inline peephole omit-frame-pointer
+	rerun-cse-after-loop schedule-insns schedule-insns2 strength-reduce
+	thread-jumps unroll-all-loops unroll-loops)' - \
+	's[-g]' -k '(coff xcoff xcoff+ dwarf dwarf+ stabs stabs+ gdb)' - \
+	's[-mno-][-mno][-m]' -k '(486 soft-float fp-ret-in-387)' - \
+	's[-Wno-][-W]' -k '(all aggregate-return cast-align cast-qual
+	char-subscript comment conversion enum-clash error format id-clash-6
+	implicit inline missing-prototypes missing-declarations nested-externs
+	import parentheses pointer-arith redundant-decls return-type shadow
+	strict-prototypes switch template-debugging traditional trigraphs
+	uninitialized unused write-strings)' - \
+	's[-]' -k '(pipe ansi traditional traditional-cpp trigraphs pedantic
+	pedantic-errors nostartfiles nostdlib static shared symbolic include
+	imacros idirafter iprefix iwithprefix nostdinc nostdinc++ undef)' \
+	-X 'Use "-f", "-g", "-m" or "-W" for more options' -- gcc g++
+#------------------------------------------------------------------------------
+# There are (at least) two ways to complete manual pages.  This one is
+# extremely memory expensive if you have lots of man pages
+man_var() {
+    man_pages=( ${^manpath}/man*/*(N:t:r) )
+    compctl -k man_pages -x 'C[-1,-P]' -m - \
+	    'R[-*l*,;]' -/g '*.(man|[0-9](|[a-z]))' -- man
+    reply=( $man_pages )
+}
+compctl -K man_var -x 'C[-1,-P]' -m - \
+	'R[-*l*,;]' -/g '*.(man|[0-9](|[a-z]))' -- man
+
+# This one isn't that expensive but somewhat slower
+man_glob () {
+   local a
+   read -cA a
+   if [[ $a[2] = -s ]] then         # Or [[ $a[2] = [0-9]* ]] for BSD
+     reply=( ${^manpath}/man$a[3]/$1*$2(N:t:r) )
+   else
+     reply=( ${^manpath}/man*/$1*$2(N:t:r) )
+   fi
+}
+#compctl -K man_glob -x 'C[-1,-P]' -m - \
+#	'R[-*l*,;]' -/g '*.(man|[0-9nlpo](|[a-z]))' -- man
+#------------------------------------------------------------------------------
+# xsetroot: gets possible colours, cursors and bitmaps from wherever.
+# Uses two auxiliary functions.  You might need to change the path names.
+Xcolours() {
+  reply=( ${(L)$(awk '{ if (NF = 4) print $4 }' \
+	< /usr/openwin/lib/X11/rgb.txt)} )
+}
+Xcursor() {
+  reply=( $(sed -n 's/^#define[	 ][ 	]*XC_\([^ 	]*\)[ 	].*$/\1/p' \
+	  < /usr/include/X11/cursorfont.h) )
+}
+compctl -k '(-help -def -display -cursor -cursor_name -bitmap -mod -fg -bg
+	-grey -rv -solid -name)' -x \
+	'c[-1,-display]' -s '$DISPLAY' -k hosts -S ':0' - \
+	'c[-1,-cursor]' -f -  'c[-2,-cursor]' -f - \
+	'c[-1,-bitmap]' -g '/usr/include/X11/bitmaps/*' - \
+	'c[-1,-cursor_name]' -K Xcursor - \
+	'C[-1,-(solid|fg|bg)]' -K Xcolours -- xsetroot
+#------------------------------------------------------------------------------
+# dd
+compctl -k '(if of conv ibs obs bs cbs files skip file seek count)' \
+	-S '=' -x 's[if=], s[of=]' -f - 'C[0,conv=*,*] n[-1,,], s[conv=]' \
+	-k '(ascii ebcdic ibm block unblock lcase ucase swap noerror sync)' \
+	-q -S ',' - 'n[-1,=]' -X '<number>'  -- dd
+#------------------------------------------------------------------------------
+# Various MH completions by Peter Stephenson
+# You may need to edit where it says *Edit Me*.
+
+# The following three functions are best autoloaded.
+# mhcomp completes folders (including subfolders),
+# mhfseq completes sequence names and message numbers,
+# mhfile completes files in standard MH locations.
+
+function mhcomp {
+  # Completion function for MH folders.
+  # Works with both + (rel. to top) and @ (rel. to current).
+  local nword args pref char mhpath
+  read -nc nword
+  read -cA args
+
+  pref=$args[$nword]
+  char=$pref[1]
+  pref=$pref[2,-1]
+
+  # The $(...) here accounts for most of the time spent in this function.
+  if [[ $char = + ]]; then
+  #    mhpath=$(mhpath +)
+  # *Edit Me*: use a hard wired value here: it's faster.
+    mhpath=~/Mail
+  elif [[ $char = @ ]]; then
+    mhpath=$(mhpath)
+  fi
+
+  eval "reply=($mhpath/$pref*(N-/))"
+
+  # I'm frankly amazed that this next step works, but it does.
+  reply=(${reply#$mhpath/})
+}
+
+mhfseq() {
+  # Extract MH message names and numbers for completion.  Use of the
+  # correct folder, if it is not the current one, requires that it
+  # should be the previous command line argument.  If the previous
+  # argument is `-draftmessage', a hard wired draft folder name is used.
+
+  local folder foldpath words pos nums
+  read -cA words
+  read -cn pos
+
+  # Look for a folder name.
+  # First try the previous word.
+  if [[ $words[$pos-1] = [@+]* ]]; then
+    folder=$words[$pos-1]
+  # Next look and see if we're looking for a draftmessage
+  elif [[ $words[$pos-1] = -draftmessage ]]; then
+    # *Edit Me*:  shortcut -- hard-wire draftfolder here
+    # Should really look for a +draftfolder argument.
+    folder=+drafts
+  fi
+  # Else use the current folder ($folder empty)
+
+  if [[ $folder = +* ]]; then
+  # *Edit Me*:  use hard-wired path with + for speed.
+    foldpath=~/Mail/$folder[2,-1]
+  else
+    foldpath=$(mhpath $folder)
+  fi
+
+  # Extract all existing message numbers from the folder.
+  nums=($foldpath/<->(N:t))
+  # If that worked, look for marked sequences.
+  # *Edit Me*: if you never use non-standard sequences, comment out
+  # or delete the next three lines.
+  if (( $#nums )); then
+    nums=($nums $(mark $folder | awk -F: '{print $1}'))
+  fi
+
+  # *Edit Me*:  `unseen' is the value of Unseen-Sequence, if it exists;
+  set -A reply next cur prev first last all unseen $nums
+
+}
+
+mhfile () {
+  # Find an MH file; for use with -form arguments and the like.
+  # Use with compctl -K mhfile.
+
+  local mhfpath file
+  # *Edit Me*:  Array containing all the places MH will look for templates etc.
+  mhfpath=(~/Mail /usr/local/lib/MH)
+
+  # Emulate completeinword behaviour as appropriate
+  local wordstr
+  if [[ -o completeinword ]]; then
+    wordstr='$1*$2'
+  else
+    wordstr='$1$2*'
+  fi
+
+  if [[ $1$2 = */* ]]; then
+    # path given: don't search MH locations
+    eval "reply=($wordstr(.N))"
+  else
+    # no path:  only search MH locations.
+    eval "reply=(\$mhfpath/$wordstr(.N:t))"
+  fi
+}
+
+# Note: you must type the initial + or @ of a folder name to get
+# completion, even in places where only folder names are allowed.
+# Abbreviations for options are not recognised.  Hit tab to complete
+# the option name first.
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(all fast nofast header noheader help list nolist \
+  pack nopack pop push recurse norecurse total nototal)" -- folder
+compctl -K mhfseq -x 's[+][@],c[-1,-draftfolder] s[+][@]' \
+  -K mhcomp -S / -q - 'c[-1,-draftmessage]' -K mhfseq - \
+  'C[-1,-(editor|whatnowproc)]' -c - \
+  's[-]' -k "(draftfolder draftmessage nodraftfolder editor noedit \
+  file form use nouse whatnowproc nowhatnowproc help)" - \
+  'c[-1,-form]' -K mhfile -- comp
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(audit noaudit changecur nochangecur form format \
+  file silent nosilent truncate notruncate width help)" - \
+  'C[-1,-(audit|form)]' -K mhfile - 'c[-1,-file]' -f + -- inc
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(sequence add delete list public nopublic zero nozero help)" -- \
+  mark
+compctl -K mhfseq -x 's[+][@]' \
+  -K mhcomp -S / -q - 'c[-1,-file]' -f - 'c[-1,-rmmprov]' -c - \
+  's[-]' -k "(draft link nolink preserve nopreserve src file \
+  rmmproc normmproc help)" -- refile
+compctl -K mhfseq -x 's[+][@]' \
+  -K mhcomp -S / -q - 'c[-1,-draftmessage]'  -K mhfseq -\
+  's[-]' -k "(annotate noannotate cc nocc draftfolder nodraftfolder \
+  draftmessage editor noedit fcc filter form inplace noinplace query \
+  noquery width whatnowproc nowhatnowproc help)" - 'c[-1,(cc|nocc)]' \
+  -k "(all to cc me)" - 'C[-1,-(filter|form)]' -K mhfile - \
+  'C[-1,-(editor|whatnowproc)]' -c -- repl
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(clear noclear form format header noheader reverse noreverse \
+  file help width)" - 'c[-1,-file]' -f - 'c[-1,-form]' -K mhfile -- scan
+compctl -K mhfseq -x 's[+][@]'  -K mhcomp -S / -q - \
+  's[-]' -k "(draft form moreproc nomoreproc header noheader \
+   showproc noshowproc length width help)" - 'C[-1,-(show|more)proc]' -c - \
+   'c[-1,-file]' -f - 'c[-1,-form]' -K mhfile - \
+   'c[-1,-length]' -s '$LINES' - 'c[-1,-width]' -s '$COLUMNS' -- show next prev
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - 's[-]' \
+  -k "(help)" -- rmm
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - \
+  's[-]' -k "(after before cc date datefield from help list nolist \
+  public nopublic search sequence subject to zero nozero not or and \
+  lbrace rbrace)" -- pick
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - 's[-]' \
+  -k "(alias check draft draftfolder draftmessage help nocheck \
+  nodraftfolder)" -- whom
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - 's[-]' \
+  -k "(file part type list headers noheaders realsize norealsize nolist \
+  show serialonly noserialonly form pause nopause noshow store auto noauto \
+  nostore cache nocache rcache wcache check nocheck ebcdicsafe noebcdicsafe \
+  rfc934mode norfc934mode verbose noverbose help)" - \
+  'c[-1,-file]' -f - 'c[-1,-form]' -K mhfile - \
+  'C[-1,-[rw]cache]' -k '(public private never ask)' -- mhn
+compctl -K mhfseq -x 's[+][@]' -K mhcomp -S / -q - 's[-]' -k '(help)' -- mhpath
+#------------------------------------------------------------------------------
+# CVS
+#
+cvscmds=(add admin rcs checkout commit diff rdiff export history import log rlog
+         release remove status tag rtag update annotate)
+cvsignore="*~ *# .#* *.o *.a CVS . .."
+
+compctl -k cvscmds \
+    -x "c[-1,-D]" -k '(today yesterday 1\ week\ ago)' \
+    - "c[-1,-m]" -k '(bugfix cosmetic\ fix ... added\ functionality foo)' \
+    - "c[-1,-F]" -f \
+    - "c[-1,-r]" -K cvsrevisions \
+    - "c[-1,-I]" -f \
+    - "R[add,;]" -K cvsaddp \
+    - "R[(admin|rcs),;]" -/K cvstargets \
+    - "R[(checkout|co),;]" -K cvsrepositories \
+    - "R[(commit|ci),;]" -/K cvstargets \
+    - "R[(r|)diff,;]" -/K cvstargets \
+    - "R[export,;]" -f \
+    - "R[history,;]" -/K cvstargets \
+    - "R[history,;] c[-1,-u]" -u \
+    - "R[import,;]" -K cvsrepositories \
+    - "R[(r|)log,;]" -/K cvstargets \
+    - 'R[(r|)log,;] s[-w] n[-1,,],s[-w]' -u -S , -q \
+    - "R[rel(|ease),;]" -f \
+    - "R[(remove|rm),;] R[-f,;]" -/K cvstargets \
+    - "R[(remove|rm),;]" -K cvsremovep \
+    - "R[status,;]" -/K cvstargets \
+    - "R[(r|)tag,;]" -/K cvstargets \
+    - "R[up(|date),;]" -/K cvstargets \
+    - "R[annotate,;]" -/K cvstargets \
+    -- cvs 
+
+compctl -/K cvstargets cvstest 
+
+cvsprefix() {
+    local nword args f
+    read -nc nword; read -Ac args
+    pref=$args[$nword]
+    if [[ -d $pref:h && ! -d $pref ]]; then
+	pref=$pref:h
+    elif [[ $pref != */* ]]; then
+	pref=
+    fi
+    [[ -n "$pref" && "$pref" != */ ]] && pref=$pref/
+}
+
+cvsentries() {
+    setopt localoptions nullglob unset
+    if [[ -f ${pref}CVS/Entries ]]; then
+	reply=( "${pref}${^${${${(f@)$(<${pref}CVS/Entries)}:#D*}#/}%%/*}" )
+    fi
+}
+
+cvstargets() {
+    local pref
+    cvsprefix
+    cvsentries
+}
+
+cvsrevisions() {
+    reply=( "${${${(M)${(f)$(cvs -q status -vl .)}:#	*}##[ 	]##}%%[ 	]*}" )
+}
+
+cvsrepositories() {
+    local root=$CVSROOT
+    [[ -f CVS/Root ]] && root=$(<CVS/Root)
+    reply=(
+	$root/^CVSROOT(:t)
+	"${${(M)${(f)$(<$root/CVSROOT/modules)}:#[^#]*}%%[ 	]*}"
+    )
+}
+
+cvsremovep() {
+    local pref
+    cvsprefix
+    cvsentries
+    setopt localoptions unset
+    local omit
+    omit=( ${pref}*(D) )
+    eval 'reply=( ${reply:#('${(j:|:)omit}')} )'
+}
+
+cvsaddp() {
+    local pref
+    cvsprefix
+    cvsentries
+    setopt localoptions unset
+    local all omit
+    all=( ${pref}*(D) )
+    omit=( $reply ${pref}${^${=cvsignore}} )
+    [[ -r ~/.cvsignore ]] && omit=( $omit ${pref}${^$(<~/.cvsignore)} )
+    [[ -r ${pref}.cvsignore ]] && omit=( $omit ${pref}${^$(<${pref}.cvsignore)} )
+    eval 'reply=( ${all:#('${(j:|:)omit}')} )' 
+}
+
+#------------------------------------------------------------------------------
+# RedHat Linux rpm utility
+#
+compctl -s '$(rpm -qa)' \
+	-x 's[--]' -s 'oldpackage percent replacefiles replacepkgs noscripts
+		       root excludedocs includedocs test upgrade test clean
+		       short-circuit sign recompile rebuild resign querytags
+		       queryformat version help quiet rcfile force hash' - \
+	's[ftp:]' -P '//' -s '$(</u/zsh/ftphosts)' -S '/' - \
+	'c[-1,--root]' -/ - \
+	'c[-1,--rcfile]' -f - \
+	'p[1] s[-b]' -k '(p l c i b a)' - \
+	'c[-1,--queryformat] N[-1,{]' \
+		-s '"${${(f)$(rpm --querytags)}#RPMTAG_}"' -S '}' - \
+	'W[1,-q*] C[-1,-([^-]*|)f*]' -f - \
+	'W[1,-i*], W[1,-q*] C[-1,-([^-]*|)p*]' \
+		-/g '*.rpm' + -f -- rpm
+#------------------------------------------------------------------------------
+compctl -u -x 'c[-1,-w]' -f -- ac
+compctl -/g '*.m(.)' mira
+#------------------------------------------------------------------------------
+# talk completion: complete local users, or users at hosts listed via rwho
+compctl -K talkmatch talk ytalk ytalk3
+function talkmatch {
+    local u
+    reply=($(users))
+    for u in "${${(f)$(rwho 2>/dev/null)}%%:*}"; do
+	reply=($reply ${u%% *}@${u##* })
+    done
+}
+#------------------------------------------------------------------------------
+# Linux mount
+comp_fsmount () {
+    local tmp; if [[ $UID = 0 ]]; then tmp=""; else tmp="user"; fi
+    sed -n -e "s|^[^# 	][^ 	]*[ 	][ 	]*\(/[^ 	]*\)[ 	].*$tmp.*|\1|p" /etc/fstab
+}
+comp_nfsmount () {
+    local cmd args host
+    read -Ac cmd; read -cn where
+    host=${${cmd[$where]}%%:*}
+    reply=("${(@f)$(showmount -e $host | sed -n -e "s|^/\([^ ]*\) .*|$host:/\1|p")}")
+}
+compctl -s '$(mount | \
+	      sed -e "s/^[^ ]* on \\([^ ]*\\) type.*/\\1/"'"$(
+	      if [[ ! $UID = 0 ]]; then
+		  echo ' | egrep "^${(j:|:)$(comp_fsmount)}\$"'
+	      fi)"')' umount
+compctl -s '$(comp_fsmount)' + \
+	-x 'S[/]' -f -- + \
+	-x 'C[0,*:*]' -K comp_nfsmount -- + \
+	-s '$(< /etc/hosts)' \
+	mount
+#------------------------------------------------------------------------------
+# Lynx (web browser)
+compctl -k '(http:// file: ftp:// gopher:// news://)' -S '' \
+        -x 's[ftp://]' -k ftphosts -S/ \
+        - 'n[1,://]' -k hosts -S/ \
+        - 's[file:/]' -/g '*.html' -W/ \
+	- 's[file:]' -s '~+' -S '/' \
+        - 's[-]' -k '(anonymous auth base book buried_news cache case
+		cfg child cookies crawl display dump editor emacskeys
+		enable_scrollback error_file fileversions force_html
+		from ftp get_data head help historical homepage
+		image_links index link localhost locexec mime_header
+		minimal newschunksize newsmaxchunk nobrowse noexec
+		nofilereferer nofilereferer nolist nolog nopause
+		noprint noredir noreferer nosocks nostatus number_links
+		popup post_data print pseudo_inlines raw realm reload
+		restrictions resubmit_posts rlogin selective show_cursor
+		source startfile_ok telnet term trace traversal underscore
+		validate version vikeys)' \
+        -- lynx
+#------------------------------------------------------------------------------
+# ssh (secure shell)
+compctl -k hosts \
+	-x "c[-1,-l]" -u \
+	- "c[-1,-i]" -f \
+	- "c[-1,-e]" -k "(~ none)" \
+	- "c[-1,-c]" -k "(idea des 3des tss arcfour none)" \
+	- "c[-1,-p]" -k ports \
+	- "c[-1,-L] c[-1,-R] c[-1,-o]" -k "()" \
+	-- ssh
+#------------------------------------------------------------------------------
+# network stuff
+compctl -k hosts \
+	-x "s[-class=]" -k "(any in chaos hesiod)" \
+	- "s[-query=]" -k "(a cname hinfo md mx mb mg minfo ns ptr soa txt uinfo wks any)" \
+	- "s[-]" -Q -S '' -k "(query= all\  class= d2\  nod2\  debug\  nodebug\  defname\  nodefname\  domain= ignoretc\  noignoretc\ )" \
+	-- nslookup
+
+compctl -k hosts \
+	-x "C[-1,[^-]*] p[2,-1]" -k ports \
+	-- telnet
+
+compctl -x 'N[-1,@]' -k hosts - 's[]' -u -qS '@' -- finger
+#------------------------------------------------------------------------------
+# gdb
+compctl -/g "*(*)" \
+	-x "s[-]" -k "(help nx q batch cd f b tty exec se core symbols c x d)" \
+	- "C[-1,(-cd|-directory)]" -/ \
+	- "C[-1,(-core|-c)]" -/g 'core*' \
+	- "C[-1,(-se|-exec)]" -f \
+	- "C[-1,(-symbols|-command|-x)]" -f \
+	- "p[2,-1] C[-1,[^-]*]" -/g "core*" \
+	-- gdb
diff --git a/Misc/globtests b/Misc/globtests
new file mode 100755
index 000000000..728aee5ae
--- /dev/null
+++ b/Misc/globtests
@@ -0,0 +1,107 @@
+#!/usr/local/bin/zsh -f
+
+setopt extendedglob badpattern
+unsetopt kshglob
+
+failed=0
+while read res str pat; do
+  [[ $res = '#' ]] && continue
+  [[ $str = ${~pat} ]]
+  ts=$?
+  [[ $1 = -q ]] || print "$ts:  [[ $str = $pat ]]"
+  if [[ ( $ts -gt 0 && $res = t) || ($ts -eq 0 && $res = f) ]]; then
+    print "Test failed:  [[ $str = $pat ]]"
+    (( failed++ ))
+  fi
+done <<EOT
+t fofo                (fo#)#
+t ffo                 (fo#)#
+t foooofo             (fo#)#
+t foooofof            (fo#)#
+t fooofoofofooo       (fo#)#
+f foooofof            (fo##)#
+f xfoooofof           (fo#)#
+f foooofofx           (fo#)#
+t ofxoofxo            ((ofo#x)#o)#
+f ofooofoofofooo      (fo#)#
+t foooxfooxfoxfooox   (fo#x)#
+f foooxfooxofoxfooox  (fo#x)#
+t foooxfooxfxfooox    (fo#x)#
+t ofxoofxo            ((ofo#x)#o)#
+t ofoooxoofxo         ((ofo#x)#o)#
+t ofoooxoofxoofoooxoofxo            ((ofo#x)#o)#
+t ofoooxoofxoofoooxoofxoo           ((ofo#x)#o)#
+f ofoooxoofxoofoooxoofxofo          ((ofo#x)#o)#
+t ofoooxoofxoofoooxoofxooofxofxo    ((ofo#x)#o)#
+t aac    ((a))#a(c)
+t ac     ((a))#a(c)
+f c      ((a))#a(c)
+t aaac   ((a))#a(c)
+f baaac  ((a))#a(c)
+t abcd   ?(a|b)c#d
+t abcd   (ab|ab#)c#d
+t acd    (ab|ab#)c#d
+t abbcd  (ab|ab#)c#d
+t effgz  (bc##d|ef#g?|(h|)i(j|k))
+t efgz   (bc##d|ef#g?|(h|)i(j|k))
+t egz    (bc##d|ef#g?|(h|)i(j|k))
+t egzefffgzbcdij    (bc##d|ef#g?|(h|)i(j|k))#
+f egz    (bc##d|ef##g?|(h|)i(j|k))
+t ofoofo (ofo##)#
+t oxfoxoxfox    (oxf(ox)##)#
+f oxfoxfox      (oxf(ox)##)#
+t ofoofo        (ofo##|f)#
+# The following is supposed to match only as fo+ofo+ofo
+t foofoofo      (foo|f|fo)(f|ofo##)#
+t oofooofo      (of|oofo##)#
+t fffooofoooooffoofffooofff     (f#o#)#
+# If the following is really slow, that's a bug.
+f fffooofoooooffoofffooofffx     (f#o#)#
+# The following tests backtracking in alternation matches
+t fofoofoofofoo (fo|foo)#
+# Exclusion: test both types
+t foo           ((^x))
+t foo           ((^x)*)
+f foo           ((^foo))
+t foo           ((^foo)*)
+t foobar        ((^foo))
+t foobar        ((^foo)*)
+f foot          z*~*x
+t zoot          z*~*x
+f foox          z*~*x
+f zoox          z*~*x
+t moo.cow       (*~*.*).(*~*.*)
+f mad.moo.cow   (*~*.*).(*~*.*)
+t moo.cow       (^*.*).(^*.*)
+f sane.moo.cow  (^*.*).(^*.*)
+f mucca.pazza   mu(^c#)?.pa(^z#)?
+t fff           ((^f))
+t fff           ((^f)#)
+t fff           ((^f)##)
+t ooo           ((^f))
+t ooo           ((^f)#)
+t ooo           ((^f)##)
+t foo           ((^f))
+t foo           ((^f)#)
+t foo           ((^f)##)
+f f             ((^f))
+f f             ((^f)#)
+f f             ((^f)##)
+t foot          (^z*|*x)
+f zoot          (^z*|*x)
+t foox          (^z*|*x)
+t zoox          (^z*|*x)
+t foo           (^foo)#
+f foob          (^foo)b*
+t foobb         (^foo)b*
+f zsh           ^z*
+t a%1X          [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]]
+f a%1           [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]]
+t [:            [[:]#
+t :]            []:]#
+t :]            [:]]#
+t [             [[]
+t ]             []]
+t []            [^]]]
+EOT
+print "$failed tests failed."
diff --git a/Misc/globtests.ksh b/Misc/globtests.ksh
new file mode 100755
index 000000000..b03cc488e
--- /dev/null
+++ b/Misc/globtests.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/zsh -f
+
+setopt kshglob
+
+failed=0
+while read res str pat; do
+  [[ $res = '#' ]] && continue
+  [[ $str = ${~pat} ]]
+  ts=$?
+  [[ $1 = -q ]] || print "$ts:  [[ $str = $pat ]]"
+  if [[ ( $ts -gt 0 && $res = t) || ($ts -eq 0 && $res = f) ]]; then
+    print "Test failed:  [[ $str = $pat ]]"
+    (( failed++ ))
+  fi
+done <<EOT
+t fofo                *(f*(o))
+t ffo                 *(f*(o))
+t foooofo             *(f*(o))
+t foooofof            *(f*(o))
+t fooofoofofooo       *(f*(o))
+f foooofof            *(f+(o))
+f xfoooofof           *(f*(o))
+f foooofofx           *(f*(o))
+t ofxoofxo            *(*(of*(o)x)o)
+f ofooofoofofooo      *(f*(o))
+t foooxfooxfoxfooox   *(f*(o)x)
+f foooxfooxofoxfooox  *(f*(o)x)
+t foooxfooxfxfooox    *(f*(o)x)
+t ofxoofxo            *(*(of*(o)x)o)
+t ofoooxoofxo         *(*(of*(o)x)o)
+t ofoooxoofxoofoooxoofxo            *(*(of*(o)x)o)
+t ofoooxoofxoofoooxoofxoo           *(*(of*(o)x)o)
+f ofoooxoofxoofoooxoofxofo          *(*(of*(o)x)o)
+t ofoooxoofxoofoooxoofxooofxofxo    *(*(of*(o)x)o)
+t aac    *(@(a))a@(c)
+t ac     *(@(a))a@(c)
+f c      *(@(a))a@(c)
+t aaac   *(@(a))a@(c)
+f baaac  *(@(a))a@(c)
+t abcd   ?@(a|b)*@(c)d
+t abcd   @(ab|a*@(b))*(c)d
+t acd    @(ab|a*(b))*(c)d
+t abbcd  @(ab|a*(b))*(c)d
+t effgz  @(b+(c)d|e*(f)g?|?(h)i@(j|k))
+t efgz   @(b+(c)d|e*(f)g?|?(h)i@(j|k))
+t egz    @(b+(c)d|e*(f)g?|?(h)i@(j|k))
+t egzefffgzbcdij    *(b+(c)d|e*(f)g?|?(h)i@(j|k))
+f egz    @(b+(c)d|e+(f)g?|?(h)i@(j|k))
+t ofoofo *(of+(o))
+t oxfoxoxfox    *(oxf+(ox))
+f oxfoxfox      *(oxf+(ox))
+t ofoofo        *(of+(o)|f)
+# The following is supposed to match only as fo+ofo+ofo
+t foofoofo      @(foo|f|fo)*(f|of+(o))
+t oofooofo      *(of|oof+(o))
+t fffooofoooooffoofffooofff      *(*(f)*(o))
+# If the following is really slow, that's a bug.
+f fffooofoooooffoofffooofffx     *(*(f)*(o))
+# The following tests backtracking in alternation matches
+t fofoofoofofoo *(fo|foo)
+# Exclusion
+t foo           !(x)
+t foo           !(x)*
+f foo           !(foo)
+t foo           !(foo)*
+t foobar        !(foo)
+t foobar        !(foo)*
+t moo.cow       !(*.*).!(*.*)
+f mad.moo.cow   !(*.*).!(*.*)
+f mucca.pazza   mu!(*(c))?.pa!(*(z))?
+t fff           !(f)
+t fff           *(!(f))
+t fff           +(!(f))
+t ooo           !(f)
+t ooo           *(!(f))
+t ooo           +(!(f))
+t foo           !(f)
+t foo           *(!(f))
+t foo           +(!(f))
+f f             !(f)
+f f             *(!(f))
+f f             +(!(f))
+t foot          @(!(z*)|*x)
+f zoot          @(!(z*)|*x)
+t foox          @(!(z*)|*x)
+t zoox          @(!(z*)|*x)
+t foo           *(!(foo))
+f foob          !(foo)b*
+t foobb         !(foo)b*
+EOT
+print "$failed tests failed."
diff --git a/Misc/lete2ctl b/Misc/lete2ctl
new file mode 100755
index 000000000..ca00b8aee
--- /dev/null
+++ b/Misc/lete2ctl
@@ -0,0 +1,301 @@
+#!/usr/local/bin/perl -w
+#
+#   ``Wee have also Shelles, thee Lyke of whych you knowe not, wherein
+#     thee User may with thee merest Presse of thee Tabbe-Keye expande
+#     or compleat al Maner of Wordes and such-like Diversities.''
+#            - Francis Bacon, `New Atlantis' (or not).
+#
+# Convert tcsh "complete" statements to zsh "compctl" statements.
+# Runs as a filter.  Should ignore anything which isn't a "complete".
+# It expects each "complete" statement to be the first thing on a line.
+# All the examples in the tcsh manual give sensible results.
+#
+# Option:
+# -x (exact): only applies in the case of command disambiguation (is
+#    that really a word?)  If you have lines like
+#       complete '-co*' 'p/0/(compress)'
+#    (which makes co<TAB> always complete to `compress') then the
+#    resulting "compctl" statements will produce one of two behaviours:
+#    (1) By default (like tcsh), com<TAB> etc. will also complete to
+#        "compress" and nothing else.
+#    (2) With -x, com<TAB> does ordinary command completion: this is
+#        more flexible.
+#    I don't understand what the hyphen in complete does and I've ignored it.
+#
+# Notes:
+# (1) The -s option is the way to do backquote expansion.  In zsh,
+#     "compctl -s '`users`' talk" works (duplicates are removed).
+# (2) Complicated backquote completions should definitely be rewritten as
+#     shell functions (compctl's "-K func" option).  Although most of
+#     these will be translated correctly, differences in shell syntax
+#     are not handled.
+# (3) Replacement of $:n with the n'th word on the current line with
+#     backquote expansion now works; it is not necessarily the most
+#     efficient way of doing it in any given case, however.
+# (4) I have made use of zsh's more sophisticated globbing to change
+#     things like ^foo.{a,b,c,d} to ^foo.(a|b|c|d), which works better.
+#     It's just possible in some cases you may want to change it back.
+# (5) Make sure all command names with wildcards are processed together --
+#     they need to be lumped into one "compctl -C" or "compctl -D"
+#     statement for zsh.
+
+# Handle options
+if (@ARGV) {
+    ($ARGV[0] eq '-x') && shift && ($opt_x = 1);
+    ($ARGV[0] =~ /^-+$/) && shift;
+}
+
+# Function names used (via magic autoincrement) when cmdline words are needed
+$funcnam = 'compfn001';
+
+# Read next word on command line
+sub getword {
+    local($word, $word2, $ret);
+    ($_) = /^\s*(.*)$/;
+    while ($_ =~ /^\S/) {
+	if (/^[\']/) {
+	    ($word, $_) = /^\'([^\']*).(.*)$/;
+	} elsif (/^[\"]/) {
+	    ($word, $_) = /^\"([^\"]*).(.*)$/;
+	    while ($word =~ /\\$/) {
+		chop($word);
+		($word2, $_) = /^([^\"]*).(.*)$/;
+		$word .= '"' . $word2;
+	    }
+	} elsif (/\S/) {
+	    ($word, $_) = /^([^\s\\\'\"\#;]*)(.*)$/;
+	    # Backslash: literal next character
+	    /^\\(.)/ && (($word .= substr($_,1,1)),
+			 ($_ = substr($_,2)));
+	    # Rest of line quoted or end of command
+	    /^[\#;]/ && ($_ = '');
+	} else {
+	    return undef;
+	}
+	length($word) && ($ret = defined($ret) ? $ret . $word : $word);
+    }
+    $ret;
+}
+
+# Interpret the x and arg in 'x/arg/type/'
+sub getpat {
+    local($pat,$arg) = @_;
+    local($ret,$i);
+    if ($pat eq 'p') {
+	$ret = "p[$arg]";
+    } elsif ($pat eq 'n' || $pat eq 'N') {
+	$let = ($arg =~ /[*?|]/) ? 'C' : 'c';
+	$num = ($pat eq 'N') ? 2 : 1;
+	$ret = "${let}[-${num},$arg]";
+    } elsif ($pat eq 'c' || $pat eq 'C') {
+	# A few tricks to get zsh to ignore up to the end of
+	# any matched pattern.
+	if (($pat eq 'c' && $arg =~ /^\*([^*?]*)$/)) {
+	    $ret = "n[-1,$1]";
+	} elsif ($arg =~ /[*?]([^*?]*)$/) {
+	    length($1) && ($ret = " n[-1,$1]");
+	    $ret = "C[0,$arg] $ret";
+	} else {
+	    $let = ($pat eq 'c') ? 's' : 'S';
+	    $ret = "${let}[$arg]";
+	}
+    }
+    $ret =~ s/'/'\\''/g;
+    $ret;
+}
+
+# Interpret the type in 'x/arg/type/'
+sub gettype {
+    local ($_) = @_;
+    local($qual,$c,$glob,$ret,$b,$m,$e,@m);
+    $c = substr($_,0,1);
+    ($c =~ /\w/) && (substr($_,1,1) eq ':') && ($glob = substr($_,2));
+# Nothing (n) can be handled by returning nothing.  (C.f. King Lear, I.i.)
+    if ($c =~ /[abcjuv]/) {
+	$ret = "-$c";
+    } elsif ($c eq 'S') {
+	$ret = '-k signals';
+    } elsif ($c eq 'd') {
+	if (defined($glob)) {
+	    $qual = '-/';
+	} else {
+	    $ret = '-/';
+	}
+    } elsif ($c eq 'e') {
+	$ret = '-E';
+    } elsif ($c eq 'f' && !$glob) {
+	$ret = '-f';
+    } elsif ($c eq 'l') {
+	$ret = q!-k "(`limit | awk '{print $1}'`)"!;
+    } elsif ($c eq 'p') {
+	$ret = "-W $glob -f", undef($glob) if defined($glob);
+    } elsif ($c eq 's') {
+	$ret = '-p';
+    } elsif ($c eq 't') {
+	$qual = '.';
+    } elsif ($c eq 'x') {
+	$glob =~ s/'/'\\''/g;
+	$ret = "-X '$glob'";
+	undef($glob);
+    } elsif ($c eq '$') {     # '){
+	$ret = "-k " . substr($_,1);
+    } elsif ($c eq '(') {
+	s/'/'\\''/g;
+	$ret = "-k '$_'";
+    } elsif ($c eq '`') {
+	# this took some working out...
+	if (s/\$:(\d+)/$foo=$1+1,"\${word[$foo]}"/ge) {
+	    $ret = "-K $funcnam";
+	    $genfunc .= <<"HERE";
+function $funcnam {
+    local word
+    read -cA word
+    reply=($_)
+}
+HERE
+	    $funcnam++;
+	} else {
+	    s/'/'\\''/g;
+	    $ret = "-s '$_'";
+	}
+    }
+
+    # foo{bar,ba,blak,sheap} -> foo(bar|ba|blak|sheap).
+    # This saves a lot of mess, since in zsh brace expansion occurs
+    # before globbing.  I'm sorry, but I don't trust $` and $'.
+    while (defined($glob) && (($b,$m,$e) = ($glob =~ /^(.*)\{(.*)\}(.*)$/))
+	   && $m =~ /,/) {
+	@m = split(/,/, $m);
+	for ($i = 0; $i < @m; $i++) {
+	    while ($m[$i] =~ /\\$/) {
+		substr($m[$i],-1,1) = "";
+		splice(@m,$i,2,"$m[$i]\\,$m[$i+1]");
+	    }
+	}
+	$glob = $b . "(" . join('|',@m) . ")" . $e;
+    }
+
+    if ($qual) {
+	$glob || ($glob = '*');
+	$glob .= "($qual)";
+    }
+    $glob && (($glob =~ s/'/'\\''/g),($glob = "-g '$glob'"));
+
+    defined($ret) && defined($glob) && ($ret .= " $glob");
+    defined($ret) ? $ret : $glob;
+}
+
+# Quoted array separator for extended completions
+$" = " - ";
+
+while (<>) {
+    if (/^\s*complete\s/) {
+	undef(@stuff); 
+	$default = '';
+	$_ = $';
+	while (/\\$/) {
+	    # Remove backslashed newlines: in principle these should become
+	    # real newlines inside quotes, but what the hell.
+	    ($_) = /^(.*)\\$/;
+	    $_ .= <>;
+	}
+	$command = &getword;
+	if ($command =~ /^-/ || $command =~ /[*?]/) {
+	    # E.g. complete -co* ...
+	    $defmatch = $command;
+	    ($defmatch =~ /^-/) && ($defmatch = substr($defmatch,1));
+	} else {
+	    undef($defmatch);
+	}
+	while (defined($word = &getword)) {
+	    # Loop over remaining arguments to "complete".
+	    $sep = substr($word,1,1);
+	    $sep =~ s/(\W)/\\$1/g;
+	    @split = split(/$sep/,$word);
+	    for ($i = 0; $i < 3; $i++) {
+		while ($split[$i] =~ /\\$/) {
+		    substr($split[$i],-1,1) = "";
+		    splice(@split,$i,2,"$split[$i]\\$sep$split[$i+1]");
+		}
+	    }
+	    ($pat,$arg,$type,$suffix) = @split;
+	    defined($suffix) && ($suffix =~ /^\s*$/) && undef($suffix);
+	    if (($word =~ /^n$sep\*$sep/) &&
+		 (!defined($defmatch))) {
+		 # The "complete" catch-all:  treat this as compctl\'s
+		 # default (requiring no pattern matching).
+		$default .= &gettype($type) . ' ';
+		defined($suffix) && ($defsuf .= $suffix);
+	    } else {
+		$pat = &getpat($pat,$arg);
+		$type = &gettype($type);
+		if (defined($defmatch)) {
+		    # The command is a pattern: use either -C or -D option.
+		    if ($pat eq 'p[0]') {
+			# Command word (-C): 'p[0]' is redundant.
+			if ($defmatch eq '*') {
+			    $defcommand = $type;
+			} else {
+			    ($defmatch =~ /\*$/) && chop($defmatch);
+			    if ($opt_x) {
+				$c = ($defmatch =~ /[*?]/) ? 'C' : 'c';
+				$pat = $c . "[0,${defmatch}]";
+			    } else {
+				$pat = ($defmatch =~ /[*?]/) ?
+				    "C[0,${defmatch}]" : "S[${defmatch}]";
+			    }
+			    push(@commandword,defined($suffix) ?
+				 "'$pat' $type -S '$suffix'" : "'$pat' $type");
+			}
+		    } elsif ($pat eq "C[-1,*]") {
+			# Not command word completion, but match
+			# command word (only)
+			if ($defmatch eq "*") {
+			    # any word of any command
+			    $defaultdefault .= " $type";
+			} else {
+			    $pat = "W[0,$defmatch]";
+			    push(@defaultword,defined($suffix) ?
+				 "'$pat' $type -S '$suffix'" : "'$pat' $type");
+			}
+		    } else {
+		        # Not command word completion, but still command
+			# word with pattern
+			($defmatch eq '*') || ($pat = "W[0,$defmatch] $pat");
+			push(@defaultword,defined($suffix) ?
+			     "'$pat' $type -S '$suffix'" : "'$pat' $type");
+		    }
+		} else {
+		    # Ordinary command
+		    push(@stuff,defined($suffix) ?
+			 "'$pat' $type -S '$suffix'" : "'$pat' $type");
+		}
+	    }
+	}
+        if (!defined($defmatch)) {
+	    # Ordinary commands with no pattern
+	    print("compctl $default");
+	    defined($defsuf) && print("-S '$defsuf' ") && undef($defsuf);
+	    defined(@stuff) && print("-x @stuff -- ");
+	    print("$command\n");
+	}
+	if (defined($genfunc)) {
+	    print $genfunc;
+	    undef($genfunc);
+	}
+    }
+}
+
+(defined(@commandword) || defined($defcommand)) &&
+    print("compctl -C ",
+	  defined($defcommand) ? $defcommand : '-c',
+	  defined(@commandword) ? " -x @commandword\n" : "\n");
+
+if (defined($defaultdefault) || defined(@defaultword)) {
+    defined($defaultdefault) || ($defaultdefault = "-f");
+    print "compctl -D $defaultdefault";
+    defined(@defaultword) && print(" -x @defaultword");
+    print "\n";
+}
+
+__END__