about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Zsh/builtins.yo7
-rw-r--r--Doc/Zsh/compctl.yo24
-rw-r--r--Doc/Zsh/mod_zftp.yo21
-rw-r--r--Etc/MACHINES6
-rw-r--r--Misc/.distfiles1
-rw-r--r--Misc/new-completion-examples453
-rw-r--r--Src/Builtins/rlimits.c14
-rw-r--r--Src/Modules/.distfiles1
-rw-r--r--Src/Modules/example.mdd3
-rw-r--r--Src/Modules/files.c6
-rw-r--r--Src/Modules/stat.c11
-rw-r--r--Src/Modules/zftp.c142
-rw-r--r--Src/Zle/comp1.c44
-rw-r--r--Src/Zle/comp1.export13
-rw-r--r--Src/Zle/compctl.c582
-rw-r--r--Src/Zle/compctl.mdd4
-rw-r--r--Src/Zle/iwidgets.list16
-rw-r--r--Src/Zle/zle.h18
-rw-r--r--Src/Zle/zle_keymap.c4
-rw-r--r--Src/Zle/zle_main.c28
-rw-r--r--Src/Zle/zle_refresh.c105
-rw-r--r--Src/Zle/zle_thingy.c50
-rw-r--r--Src/Zle/zle_tricky.c448
-rw-r--r--Src/builtin.c296
-rw-r--r--Src/cond.c6
-rw-r--r--Src/exec.c18
-rw-r--r--Src/glob.c4
-rw-r--r--Src/hashtable.c6
-rw-r--r--Src/hist.c6
-rw-r--r--Src/input.c3
-rw-r--r--Src/jobs.c17
-rw-r--r--Src/lex.c25
-rw-r--r--Src/loop.c2
-rw-r--r--Src/mem.c54
-rw-r--r--Src/mkbltnmlst.sh8
-rw-r--r--Src/mkmakemod.sh23
-rw-r--r--Src/module.c2
-rw-r--r--Src/params.c12
-rw-r--r--Src/subst.c5
-rw-r--r--Src/watch.c3
-rw-r--r--Src/zsh.export9
-rw-r--r--Src/zsh.h34
-rw-r--r--Util/zsh-development-guide21
-rw-r--r--aczsh.m42
-rw-r--r--configure.in19
-rw-r--r--patchlist.txt53
47 files changed, 2210 insertions, 423 deletions
diff --git a/Config/version.mk b/Config/version.mk
index 309c19772..05e8483fd 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-4
-VERSION_DATE='December 17, 1998'
+VERSION=3.1.5-pws-5
+VERSION_DATE='January 19, 1998'
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 2df70cd98..82562fcbe 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -947,9 +947,10 @@ otherwise it is determined by the width of the value of the
 first assignment.
 )
 item(tt(-a))(
-The names refer to array parameters.  For historical reasons, scalar
-parameters are created even when this flag is specified, but the
-output is restricted to arrays (including associative arrays).
+The names refer to array parameters.  An array parameter may be
+created this way, but it may not be assigned to in the tt(typeset)
+statement.  When displaying, both normal and associative arrays are
+shown.
 )
 item(tt(-f))(
 The names refer to functions rather than parameters.  No assignments
diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo
index 37296b716..e9634a26f 100644
--- a/Doc/Zsh/compctl.yo
+++ b/Doc/Zsh/compctl.yo
@@ -85,7 +85,7 @@ the standard behavior for all commands.  For example, if your access
 to the user database is too slow and/or it contains too many users (so
 that completion after `tt(~)' is too slow to be usable), you can use
 
-nofill(tt(compctl -Tx  'C[0,*/*]' -f - 's[~]' -k friends -S/))
+nofill(tt(compctl -Tx  'C[0,*/*]' -f - 's[~]' -k friends -S/ -tn))
 
 to complete the strings in the array tt(friends) after a `tt(~)'.
 The first argument is necessary so that this form of ~-completion is
@@ -432,16 +432,18 @@ the sorted ones. I.e. it is possible to have a sorted and a unsorted group
 with the same name and the matches in those groups will not be mixed.
 )
 item(tt(-t) var(continue))(
-The var(continue)-string contains a set of characters that specify if 
-and when completion should continue to produce matches where it normally
-would not do that. The character tt(c) means that completion continues
-with the next suitable compctl (i.e. if you don't specify this in a
-tt(compctl -T), compctls for commands are never used). The character
-tt(PLUS()) is used to continue with the matches for the next alternative
-completion (see below). The characters tt(-) and tt(x) may be used in
-sub-lists for extended completion (see below). They will make the completion
-code use the flag list after the next tt(-) (if the corresponding pattern
-matches) and the default flag list (those before the tt(-x)), respectively.
+The var(continue)-string contains a character that specifies which set 
+of completion flags should be used next. Normally those of the next
+matching compctl are used, i.e. pattern compctls and normal compctls
+after tt(-T) and after a pattern compctl. If var(continue) is the
+character tt(PLUS()) the flags for the next alternative completion
+(see below) are used. The characters tt(-) and tt(x) can be used in
+sub-lists for extended completion (see below). They will make the
+completion code use the flag list after the next tt(-) (if the
+corresponding pattern matches) and the default flag list (those before
+the tt(-x)), respectively. if var(continue) is the character tt(n) no
+other flag lists are used, i.e. the generation of matches stops
+immediately.
 )
 item(tt(-M) var(match-spec))(
 This defines additional matching control specifications that should be used
diff --git a/Doc/Zsh/mod_zftp.yo b/Doc/Zsh/mod_zftp.yo
index a15be60d6..c08130bf5 100644
--- a/Doc/Zsh/mod_zftp.yo
+++ b/Doc/Zsh/mod_zftp.yo
@@ -92,6 +92,27 @@ nofill(tt(LPAR()zftp open; zftp get foo >bar; zftp close)tt(RPAR() &))
 --- here, the connection is restricted to a background subshell and
 you are free to open a simultaneous connection in the foreground.
 )
+item(tt(test))(
+Test the connection; if the server has reported
+that it has closed the connection (maybe due to a timeout), return
+status 2; if no connection was open anyway, return status 1; else
+return status 0.  The tt(test) subcommand is
+silent, apart from messages printed by the tt($ZFTP_VERBOSE)
+mechanism, or error messages if the connection closes.  There is no
+network overhead for this test.
+
+The test is only supported on systems with either the tt(select(2)) or
+tt(poll(2)) system calls; otherwise the message tt(not
+supported on this system) is printed instead.
+
+It is useful to put the code
+
+nofill(tt([[ -n $ZFTP_HOST ]] && zftp test))
+
+into the shell function tt(precmd) for testing the connection before
+every prompt.  However, tt(zftp) will call tt(test) at the start of any
+other subcommand when a connection is open.
+)
 item(tt(cd) var(directory))(
 Change the remote directory to var(directory).  Also alters the shell
 variable tt(ZFTP_PWD).
diff --git a/Etc/MACHINES b/Etc/MACHINES
index 303508a93..7002f6db4 100644
--- a/Etc/MACHINES
+++ b/Etc/MACHINES
@@ -45,7 +45,8 @@ HP: HP-UX 9, 10.20
 	Should build `out-of-the-box'.
 
 IBM: AIX
-	Should build `out-of-the-box'.
+	Should build `out-of-the-box'.  On AIX 3.x (at least),
+	--enable-zsh-mem will not work.
 
 Linux: Linux (i386) [3.1.4]
 	If you are using an early minor version of libc 5, then a bug
@@ -92,3 +93,6 @@ Sun: Solaris 2.*
 	To avoid this, make sure you compile zsh without any reference
 	to /usr/ucblib in your LD_LIBRARY_PATH.  You can easily do this
 	by just unsetting LD_LIBRARY_PATH before building zsh.
+
+	Under Solaris 2.7, dynamically loaded library support with
+	--enable-dynamic currently does not work.
diff --git a/Misc/.distfiles b/Misc/.distfiles
index a02614511..fab78c0d1 100644
--- a/Misc/.distfiles
+++ b/Misc/.distfiles
@@ -1,4 +1,5 @@
 DISTFILES_SRC='
     .distfiles
     c2z compctl-examples globtests globtests.ksh lete2ctl
+    new-completion-examples zftp-functions
 '
diff --git a/Misc/new-completion-examples b/Misc/new-completion-examples
new file mode 100644
index 000000000..659679891
--- /dev/null
+++ b/Misc/new-completion-examples
@@ -0,0 +1,453 @@
+# Define a new widget behaving like `expand-or-complete' but calling the
+# function `main-complete' to generate matches.
+
+zle -c my-comp expand-or-complete main-complete
+
+bindkey '\C-i' my-comp
+
+
+# Below is a proposed main loop and the things it needs.
+
+# One associative array for normal completions and one for patterns.
+
+typeset -A comps
+
+
+# These may be used to define completion handlers. First argument is the
+# name of the function/variable containing the definition, the other
+# arguments are the command names for which this definition should be used.
+# With only one argument the function/variable-name __$1 is used.
+
+defcomp() {
+  local v
+
+  if [[ $# -eq 1 ]] then
+    comps[$1]="__$1"
+  else
+    v="$1"
+    shift
+    for i; do
+      comps[$i]="$v"
+    done
+  fi
+}
+
+defpatcomp() {
+  if [[ ${+patcomps} == 1 ]] then
+    patcomps=("$patcomps[@]" "$2 $1" )
+  else
+    patcomps=( "$2 $1" )
+  fi
+}
+
+
+# These can be used to easily save and restore the state of the special
+# variables used by the completion code.
+
+alias compsave='local _opre _oipre _oargs _ocur;_opre="$PREFIX";_oipre="$IPREFIX";_oargs=( "$@" );_ocur="$CURRENT"'
+alias compreset='PREFIX="$_opre";IPREFIX="$_oipre";argv=( "$_oargs[@]" );CURRENT="$_ocur"'
+
+# This is an easy way to get completion for sub-commands.
+
+alias compsub='do-complete "$@" || return 1'
+
+# This searches $1 in the array for normal completions and calls the result.
+
+compalso() {
+  1="$comps[$1]"
+  [[ -z "$1" ]] || call-complete "$@"
+}
+
+# This generates matches. The first argument is something we got from one
+# of the associative arrays above. This is expected to be either the name
+# of a variable in which case we use its value as arguments to complist,
+# or it is the name of a function in which case we call this function with
+# the arguments from the command line as its arguments.
+
+call-complete() {
+  local var
+
+  eval var\=\$\{\+$1\}
+  if [[ "$var" == 0 ]] then
+    "$@"
+  else
+    eval complist \$\{${1}\[\@\]\}
+  fi
+}
+
+# The main loop of the competion code. This is what is called when TAB is
+# pressed. The completion code gives us the special variables and the
+# arguments from the command line are gives as positional parameters.
+
+main-complete() {
+  emulate -R zsh
+  local comp
+  setopt localoptions nullglob rcexpandparam globdots
+  unsetopt markdirs globsubst shwordsplit nounset
+
+  # An entry for `--first--' is the replacement for `compctl -T'
+  # The `|| return 1' is used throughout: if a function producing matches
+  # returns non-zero this is interpreted as `do not try to produce more matches'
+  # (this is the replacement for `compctl -t').
+
+  comp="$comps[--first--]"
+  [[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1
+
+  # For arguments we use the `do-complete' function below called via the
+  # convenience alias `compsub'.
+
+  if [[ $CONTEXT == argument || $CONTEXT == command ]] then
+    compsub
+  else
+    # Let's see if we have a special completion definition for the other
+    # possible contexts.
+
+    comp=''
+
+    case $CONTEXT in
+    redirect)  comp="$comps[--redir--]";;
+    math)   comp="$comps[--math--]";;
+    subscript) comp="$comps[--subscr--]";;
+    value)  comp="$comps[--value--]";;
+    condition)   comp="$comps[--cond--]";;
+    esac
+
+    # If not, we use default completion, if any.
+
+    [[ -z "$comp" ]] && comp="$comps[--default--]"
+    [[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1
+  fi
+}
+
+# This does completion for a command (in command position and for the
+# arguments).
+
+do-complete() {
+  local comp cmd1 cmd2 pat val
+
+  # 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" ]] || call-complete "$comp" "$@" || return 1
+    return 0
+  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
+      call-complete "$val" "$@" || return 1
+    fi
+  done
+
+  # Now look up the two names in the normal completion array.
+
+  comp="${comps[$cmd1]:-$comps[$cmd2]}"
+
+  # And generate the matches, probably using default completion.
+
+  [[ -z "$comp" ]] && comp="$comps[--default--]"
+  [[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1
+}
+
+# Do sub-completion for pre-command modifiers.
+
+defcomp __precmd - noglob nocorrect exec command builtin
+__precmd() {
+  COMMAND="$1"
+  shift
+  (( CURRENT-- ))
+  if [[ CURRENT -eq 0 ]] then
+    CONTEXT=command
+  else
+    CONTEXT=argument
+  fi
+  compsub
+}
+
+# Utility function for in-path completion.
+# First argument should be an complist-option (e.g. -f, -/, -g). The other
+# arguments should be glob patterns, one per argument.
+# E.g.: files -g '*.tex' '*.texi'
+# This is intended as a replacement for `complist -f', `complist -/', and
+# `complist -g ...' (but don't use it with other options).
+# This function behaves as if you have a matcher definition like:
+#   compctl -M 'r:|[-.,_/]=* r:|=* m:{a-z}={A-Z} m:-=_ m:.=,'
+# so you may want to modify this.
+
+pfiles() {
+  local nm str pa pre epre a b c s rest
+
+  setopt localoptions nullglob rcexpandparam globdots extendedglob
+  unsetopt markdirs globsubst shwordsplit nounset
+
+  nm=$NMATCHES
+  if [[ $# -eq 0 ]] then
+    complist -f
+  elif [[ "$1" = -g ]] then
+    complist -g "$argv[2,-1]"
+    shift
+  else
+    complist $1
+    shift
+  fi
+  [[ -nmatches nm ]] || return
+
+  str="$PREFIX*$SUFFIX"
+
+  [[ -z "$1" ]] && 1='*'
+  if [[ $str[1] = \~ ]] then
+    pre="${str%%/*}/"
+    eval epre\=$pre
+    str="${str#*/}"
+    pa=''
+  else
+    pre=''
+    epre=''
+    if [[ $str[1] = / ]] then
+      str="$str[2,-1]"
+      pa='/'
+    else
+      pa=''
+    fi
+  fi
+  str="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*."
+  while [[ "$str" = */* ]] do
+    rest="${str#*/}"
+    a="${epre}${pa}(#l)${str%%/*}(-/)"
+    a=( $~a )
+    if [[ $#a -eq 0 ]] then
+      return
+    elif [[ $#a -gt 1 ]] then
+      c=()
+      s=( $rest$@ )
+      s=( "${(@)s:gs.**.*.}" )
+      for i in $a; do
+        b=( $~i/(#l)$~s )
+        eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \)
+        [[ $#b -ne 0 ]] && c=( $c $i )
+      done
+      if [[ $#c -eq 0 ]] then
+        return
+      elif [[ $#c -ne 1 ]] then
+        a="$epre$pa"
+        c=( $~c/(#l)$~s )
+        eval c\=\( \$\{c:/\*\(${(j:|:)fignore}\)\} \)
+	c=( ${c#$a} )
+        for i in $c; do
+          compadd -p "$pre$pa" -W "$a" -s "/${i#*/}" -f "${i%%/*}"
+        done
+	return
+      fi
+      a=( "$c[1]" )
+    fi
+    a="$a[1]"
+    pa="$pa${a##*/}/"
+    str="$rest"
+  done
+  a="$epre$pa"
+  s=( $str$@ )
+  s=( "${(@)s:gs.**.*.}" )
+  b=( $~a(#l)$~s )
+  eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \)
+  compadd -p "$pre$pa" -W "$epre$pa" -f ${b#$a}
+}
+
+# Utility function for completing files of a given type or any file.
+# In many cases you will want to call this one instead of pfiles().
+
+files() {
+  local nm
+
+  nm=$NMATCHES
+  pfiles "$@"
+
+  [[ $# -ne 0 && -nmatches nm ]] && pfiles
+}
+
+# Simple default, command, and math completion defined with variables.
+
+defcomp __default --default--
+__default() {
+  files
+}
+
+defcomp __command --command--
+__command=( -c )
+
+defcomp __math --math--
+__math=( -v )
+
+defcomp __subscr --subscr--
+__subscr() {
+  compalso --math-- "$@"
+  # ...probably other stuff
+}
+
+# A simple pattern completion, just as an example.
+
+defpatcomp __x_options '*/X11/*'
+__x_options() {
+  complist -J options -k '(-display -name -xrm)'
+}
+
+# A better example: completion for `find'.
+
+defcomp find
+__find() {
+  compsave
+
+  if [[ -mbetween -(ok|exec) \\\; ]] then
+    compsub
+  elif [[ -iprefix - ]] then
+    complist -s 'daystart {max,min,}depth follow noleaf version xdev \
+	{a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \
+	{i,}{l,}name {no,}{user,group} path perm regex size true uid used \
+	exec {f,}print{f,0,} ok prune ls'
+    compreset
+  elif [[ -position 1 ]] then
+    complist -g '. ..'
+    files -g '(-/)'
+  elif [[ -mcurrent -1 -((a|c|)newer|fprint(|0|f)) ]] then
+    files
+  elif [[ -current -1 -fstype ]] then
+    complist -k '(ufs 4.2 4.3 nfs tmp mfs S51K S52K)'
+  elif [[ -current -1 -group ]] then
+    complist -k groups
+  elif [[ -current -1 -user ]] then
+    complist -u
+  fi
+}
+
+# Various completions...
+
+defcomp __gunzip gunzip zcat
+__gunzip() {
+  files -g '*.[gG][z]'
+}
+
+defcomp gzip
+__gzip() {
+  files -g '*~*.[gG][zZ]'
+}
+
+defcomp xfig
+__xfig() {
+  files -g '*.fig'
+}
+
+defcomp __make make gmake
+__make() {
+  complist -s "\$(awk '/^[a-zA-Z0-9][^/ 	]+:/ {print \$1}' FS=: [mM]akefile)"
+}
+
+defcomp __ps gs ghostview gview psnup psselect pswrap pstops pstruct lpr
+__ps() {
+  files -g '*([pP][sS]|eps)'
+}
+
+defcomp __which which whence
+__which=( -caF )
+
+defcomp __rlogin rlogin rsh ssh
+__rlogin() {
+  if [[ -position 1 ]] then
+    complist -k hosts
+  elif [[ -position 2 ]] then
+    complist -k '(-l)'
+  elif [[ -position 3 && -word 1 artus ]] then
+    complist -k '(puck root)'
+  fi
+}
+
+defcomp __dvi xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype
+__dvi() {
+  files -g '*.(dvi|DVI)'
+}
+
+defcomp __dirs rmdir df du dircmp cd
+__dirs() {
+  files -/ '*(-/)'
+}
+
+defcomp __jobs fg bg jobs
+__jobs=(-j -P '%?')
+
+defcomp kill
+__kill() {
+  if [[ -iprefix '-' ]] then
+    complist -k signals
+  else
+    complist -P '%?' -j
+  fi
+}
+
+defcomp __uncompress uncompress zmore
+__uncompress() {
+  files -g '*.Z'
+}
+
+defcomp compress
+__compress() {
+  files -g '*~*.Z'
+}
+
+defcomp __tex tex latex glatex slitex gslitex
+__tex() {
+  files -g '*.(tex|TEX|texinfo|texi)'
+}
+
+defcomp __options setopt unsetopt
+__options=(-M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o)
+
+defcomp __funcs unfunction
+__funcs=(-F)
+
+defcomp __aliases unalias
+__aliases=(-a)
+
+defcomp __vars unset
+__vars=(-v)
+
+defcomp __enabled disable
+__enabled=(-FBwa)
+
+defcomp __disabled enable
+__disabled=(-dFBwa)
+
+defcomp __pdf acroread
+__pdf() {
+  files -g '*.(pdf|PDF)'
+}
+
+defcomp tar
+__tar() {
+  local nm tf
+  compsave
+
+  tf="$2"
+  nm=$NMATCHES
+  if [[ ( -mword 1 *t*f* || -mword 1 *x*f* ) && -position 3 100000 ]] then
+    complist -k "( $(tar tf $tf) )"
+    compreset
+  elif [[ -mword 1 *c*f* && -position 3 100000 ]] then
+    files
+    compreset
+  elif [[ -mcurrent -1 *f* && -position 2 ]] then
+    files -g '*.(tar|TAR)'
+  fi
+}
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 31dcb71b1..2af8dc6ac 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -264,7 +264,7 @@ bin_limit(char *nam, char **argv, char *ops, int func)
 	     * together more than one of these.  It's easier to understand from *
 	     * the code:                                                        */
 	    val = zstrtorlimt(s, &s, 10);
-	    if (*s)
+	    if (*s) {
 		if ((*s == 'h' || *s == 'H') && !s[1])
 		    val *= 3600L;
 		else if ((*s == 'm' || *s == 'M') && !s[1])
@@ -275,6 +275,7 @@ bin_limit(char *nam, char **argv, char *ops, int func)
 		    zwarnnam("limit", "unknown scaling factor: %s", s, 0);
 		    return 1;
 		}
+	    }
 	}
 # ifdef RLIMIT_NPROC
 	else if (lim == RLIMIT_NPROC)
@@ -339,12 +340,12 @@ bin_unlimit(char *nam, char **argv, char *ops, int func)
     /* Without arguments, remove all limits. */
     if (!*argv) {
 	for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) {
-	    if (hard)
+	    if (hard) {
 		if (euid && current_limits[limnum].rlim_max != RLIM_INFINITY)
 		    ret++;
 		else
 		    limits[limnum].rlim_max = RLIM_INFINITY;
-	    else
+	    } else
 		limits[limnum].rlim_cur = limits[limnum].rlim_max;
 	}
 	if (ops['s'])
@@ -373,13 +374,13 @@ bin_unlimit(char *nam, char **argv, char *ops, int func)
 		return 1;
 	    }
 	    /* remove specified limit */
-	    if (hard)
+	    if (hard) {
 		if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) {
 		    zwarnnam(nam, "can't remove hard limits", NULL, 0);
 		    ret++;
 		} else
 		    limits[lim].rlim_max = RLIM_INFINITY;
-	    else
+	    } else
 		limits[lim].rlim_cur = limits[lim].rlim_max;
 	    if (ops['s'] && zsetlimit(lim, nam))
 		ret++;
@@ -478,11 +479,12 @@ bin_ulimit(char *name, char **argv, char *ops, int func)
 	    }
 	}
 	if (!*argv || **argv == '-') {
-	    if (res < 0)
+	    if (res < 0) {
 		if (*argv || nres)
 		    continue;
 		else
 		    res = RLIMIT_FSIZE;
+	    }
 	    resmask |= 1 << res;
 	    nres++;
 	    continue;
diff --git a/Src/Modules/.distfiles b/Src/Modules/.distfiles
index 4c98f97ea..23c7eae3f 100644
--- a/Src/Modules/.distfiles
+++ b/Src/Modules/.distfiles
@@ -5,4 +5,5 @@ DISTFILES_SRC='
     example.mdd example.c
     files.mdd files.c
     stat.mdd stat.c
+    zftp.mdd zftp.c
 '
diff --git a/Src/Modules/example.mdd b/Src/Modules/example.mdd
index 89f12097c..f2cd50693 100644
--- a/Src/Modules/example.mdd
+++ b/Src/Modules/example.mdd
@@ -1,3 +1,6 @@
 autobins="example"
 
+autoinfixconds="ex"
+autoprefixconds="len"
+
 objects="example.o"
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index f52c54338..c1948e1fd 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -195,18 +195,18 @@ bin_ln(char *nam, char **args, char *ops, int func)
 
 
     if(func == BIN_MV) {
-	move = rename;
+	move = (MoveFunc) rename;
 	flags = ops['f'] ? 0 : MV_ASKNW;
 	flags |= MV_ATOMIC;
     } else {
 	flags = ops['f'] ? MV_FORCE : 0;
 #ifdef HAVE_LSTAT
 	if(ops['s'])
-	    move = symlink;
+	    move = (MoveFunc) symlink;
 	else
 #endif
 	     {
-	    move = link;
+	    move = (MoveFunc) link;
 	    if(!ops['d'])
 		flags |= MV_NODIRS;
 	}
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 5c56be5c6..6f80e2a96 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -346,7 +346,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 	} else {
 	    for (; *arg; arg++) {
 		if (strchr("glLnNrstT", *arg))
-		    ops[*arg] = 1;
+		    ops[STOUC(*arg)] = 1;
 		else if (*arg == 'A') {
 		    if (arg[1]) {
 			arrnam = arg+1;
@@ -505,7 +505,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 		continue;
 	}
 
-	if (flags & STF_FILE)
+	if (flags & STF_FILE) {
 	    if (arrnam)
 		*arrptr++ = ztrdup(*args);
 	    else if (hashnam) {
@@ -513,6 +513,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 		*hashptr++ = ztrdup(*args);
 	    } else
 		printf("%s%s", *args, (flags & STF_PICK) ? " " : ":\n");
+	}
 	if (iwhich > -1) {
 	    statprint(&statbuf, outbuf, *args, iwhich, flags);
 	    if (arrnam)
@@ -544,7 +545,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 	    putchar('\n');
     }
 
-    if (arrnam)
+    if (arrnam) {
 	if (ret)
 	    freearray(array);
 	else {
@@ -552,8 +553,9 @@ bin_stat(char *name, char **args, char *ops, int func)
 	    if (errflag)
 		return 1;
 	}
+    }
 
-    if (hashnam)
+    if (hashnam) {
     	if (ret)
 	    freearray(hash);
 	else {
@@ -561,6 +563,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 	    if (errflag)
 		return 1;
 	}
+    }
 
     return ret;
 }
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 4bcd80c7f..d71fa642f 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -62,9 +62,19 @@
 /* it's a TELNET based protocol, but don't think I like doing this */
 #include <arpa/telnet.h>
 
+/*
+ * We use poll() in preference to select because some subset of manuals says
+ * that's the thing to do, plus it's a bit less fiddly.  I don't actually
+ * have access to a system with poll but not select, however, though
+ * both bits of the code have been tested on a machine with both.
+ */
+#ifdef HAVE_POLL_H
+# include <poll.h>
+#endif
+
 /* pinch the definition from <netinet/in.h> for deficient headers */
 #ifndef INADDR_NONE
-#define INADDR_NONE 0xffffffff
+# define INADDR_NONE 0xffffffff
 #endif
 
 /*
@@ -124,7 +134,8 @@ enum {
     ZFTP_HERE  = 0x0100,	/* here rather than over there */
     ZFTP_CDUP  = 0x0200,	/* CDUP rather than CWD */
     ZFTP_REST  = 0x0400,	/* restart: set point in remote file */
-    ZFTP_RECV  = 0x0800		/* receive rather than send */
+    ZFTP_RECV  = 0x0800,	/* receive rather than send */
+    ZFTP_TEST  = 0x1000		/* test command, don't test */
 };
 
 typedef struct zftpcmd *Zftpcmd;
@@ -134,6 +145,7 @@ static struct zftpcmd zftpcmdtab[] = {
     { "params", zftp_params, 0, 4, 0 },
     { "login", zftp_login, 0, 3, ZFTP_CONN },
     { "user", zftp_login, 0, 3, ZFTP_CONN },
+    { "test", zftp_test, 0, 0, ZFTP_TEST },
     { "cd", zftp_cd, 1, 1, ZFTP_CONN|ZFTP_LOGI },
     { "cdup", zftp_cd, 0, 0, ZFTP_CONN|ZFTP_LOGI|ZFTP_CDUP },
     { "dir", zftp_dir, 0, -1, ZFTP_CONN|ZFTP_LOGI },
@@ -674,8 +686,8 @@ zfgetmsg()
 
     zfgetline(line, 256, tmout);
     ptr = line;
-    if (zfdrrrring || !isdigit((int)*ptr) || !isdigit((int)ptr[1]) || 
-	!isdigit((int)ptr[2])) {
+    if (zfdrrrring || !isdigit(STOUC(*ptr)) || !isdigit(STOUC(ptr[1])) || 
+	!isdigit(STOUC(ptr[2]))) {
 	/* timeout, or not talking FTP.  not really interested. */
 	zcfinish = 2;
 	if (!zfclosing)
@@ -820,7 +832,7 @@ zfopendata(char *name)
 	zwarnnam(name, "Must set preference S or P to transfer data", NULL, 0);
 	return 1;
     }
-    zdfd = zfmovefd(socket(AF_INET, SOCK_STREAM, 0));
+    zdfd = socket(AF_INET, SOCK_STREAM, 0);
     if (zdfd < 0) {
 	zwarnnam(name, "can't get data socket: %e", NULL, errno);
 	return 1;
@@ -851,7 +863,7 @@ zfopendata(char *name)
 	 * lastmsg already has the reply code expunged.
 	 */
 	for (ptr = lastmsg; *ptr; ptr++)
-	    if (isdigit(*ptr))
+	    if (isdigit(STOUC(*ptr)))
 		break;
 	if (sscanf(ptr, "%d,%d,%d,%d,%d,%d",
 		   nums, nums+1, nums+2, nums+3, nums+4, nums+5) != 6) {
@@ -986,11 +998,11 @@ zfgetdata(char *name, char *rest, char *cmd, int getsize)
 	char *ptr = strstr(lastmsg, "bytes");
 	zfstatus |= ZFST_NOSZ|ZFST_TRSZ;
 	if (ptr) {
-	    while (ptr > lastmsg && !isdigit(*ptr))
+	    while (ptr > lastmsg && !isdigit(STOUC(*ptr)))
 		ptr--;
-	    while (ptr > lastmsg && isdigit(ptr[-1]))
+	    while (ptr > lastmsg && isdigit(STOUC(ptr[-1])))
 		ptr--;
-	    if (isdigit(*ptr)) {
+	    if (isdigit(STOUC(*ptr))) {
 		zfstatus &= ~ZFST_NOSZ;
 		if (getsize) {
 		    long sz = zstrtol(ptr, NULL, 10);
@@ -1017,6 +1029,13 @@ zfgetdata(char *name, char *rest, char *cmd, int getsize)
 	    return 1;
 	}
 	zdfd = newfd;		/* this is now the actual data fd */
+    } else {
+	/*
+	 * We avoided dup'ing zdfd up to this point, to try to keep
+	 * things simple, so we now need to move it out of the way
+	 * of the user-visible fd's.
+	 */
+	zdfd = zfmovefd(zdfd);
     }
 
 
@@ -1659,7 +1678,7 @@ zftp_open(char *name, char **args, int flags)
     }
 
     zsock.sin_port = zservp->s_port;
-    zcfd = zfmovefd(socket(zsock.sin_family, SOCK_STREAM, 0));
+    zcfd = socket(zsock.sin_family, SOCK_STREAM, 0);
     if (zcfd < 0) {
 	zwarnnam(name, "socket failed: %e", NULL, errno);
 	zfunsetparam("ZFTP_HOST");
@@ -1667,12 +1686,6 @@ zftp_open(char *name, char **args, int flags)
 	return 1;
     }
 
-#if defined(F_SETFD) && defined(FD_CLOEXEC)
-    /* If the shell execs a program, we don't want this fd left open. */
-    len = FD_CLOEXEC;
-    fcntl(zcfd, F_SETFD, &len);
-#endif
-
     /*
      * now connect the socket.  manual pages all say things like `this is all
      * explained oh-so-wonderfully in some other manual page'.  not.
@@ -1708,6 +1721,19 @@ zftp_open(char *name, char **args, int flags)
     /* now we can talk to the control connection */
     zcfinish = 0;
 
+
+    /*
+     * Move the fd out of the user-visible range.  We need to do
+     * this after the connect() on some systems.
+     */
+    zcfd = zfmovefd(zcfd);
+
+#if defined(F_SETFD) && defined(FD_CLOEXEC)
+    /* If the shell execs a program, we don't want this fd left open. */
+    len = FD_CLOEXEC;
+    fcntl(zcfd, F_SETFD, &len);
+#endif
+
     len = sizeof(zsock);
     if (getsockname(zcfd, (struct sockaddr *)&zsock, &len) < 0) {
 	zwarnnam(name, "getsockname failed: %e", NULL, errno);
@@ -2022,6 +2048,69 @@ zftp_login(char *name, char **args, int flags)
     return zfgetcwd();
 }
 
+/*
+ * See if the server wants to tell us something.  On a timeout, we usually
+ * have a `421 Timeout' or something such waiting for us, so we read
+ * it here.  As well as being called explicitly by the user
+ * (precmd is a very good place for this, it's cheap since it has
+ * no network overhead), we call it in the bin_zftp front end if we
+ * have a connection and weren't going to call it anyway.
+ *
+ * Poll-free and select-free systems are few and far between these days,
+ * but I'm willing to consider suggestions.
+ */
+
+/**/
+static int
+zftp_test(char *name, char **args, int flags)
+{
+#if defined(HAVE_POLL) || defined(HAVE_SELECT)
+    int ret;
+# ifdef HAVE_POLL
+    struct pollfd pfd;
+# else
+    fd_set f;
+    struct timeval tv;
+# endif /* HAVE_POLL */
+
+    if (zcfd == -1)
+	return 1;
+
+# ifdef HAVE_POLL
+#  ifndef POLLIN
+    /* safety first, though I think POLLIN is more common */
+#   define POLLIN POLLNORM
+#  endif /* HAVE_POLL */
+    pfd.fd = zcfd;
+    pfd.events = POLLIN;
+    if ((ret = poll(&pfd, 1, 0)) < 0 && errno != EINTR && errno != EAGAIN)
+	zfclose();
+    else if (ret > 0 && pfd.revents) {
+	/* handles 421 (maybe a bit noisily?) */
+	zfgetmsg();
+    }
+# else
+    FD_ZERO(&f);
+    FD_SET(zcfd, &f);
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    if ((ret = select(zcfd +1, (SELECT_ARG_2_T) &f, NULL, NULL, &tv)) < 0
+	&& errno != EINTR)
+	zfclose();
+    else if (ret > 0) {
+	/* handles 421 */
+	zfgetmsg();
+    }
+# endif /* HAVE_POLL */
+    /* if we now have zcfd == -1, then we've just been dumped out. */
+    return (zcfd == -1) ? 2 : 0;
+#else
+    zfwarnnam(name, "not supported on this system.", NULL, 0);
+    return 3;
+#endif /* defined(HAVE_POLL) || defined(HAVE_SELECT) */
+}
+
+
 /* do ls or dir on the remote directory */
 
 /**/
@@ -2476,7 +2565,7 @@ bin_zftp(char *name, char **args, char *ops, int func)
     char fullname[11] = "zftp ";
     char *cnam = *args++, *prefs, *ptr;
     Zftpcmd zptr;
-    int n, ret;
+    int n, ret = 0;
 
     for (zptr = zftpcmdtab; zptr->nam; zptr++)
 	if (!strcmp(zptr->nam, cnam))
@@ -2521,8 +2610,25 @@ bin_zftp(char *name, char **args, char *ops, int func)
 				  "B" : "S"), ZFPM_READONLY);
 	}
     }
+#if defined(HAVE_SELECT) || defined (HAVE_POLL)
+    if (zcfd != -1 && !(zptr->flags & ZFTP_TEST)) {
+	/*
+	 * Test the connection for a bad fd or incoming message, but
+	 * only if the connection was last heard of open, and
+	 * if we are not about to call the test command anyway.
+	 * Not worth it unless we have select() or poll().
+	 */
+	ret = zftp_test("zftp test", NULL, 0);
+    }
+#endif
     if ((zptr->flags & ZFTP_CONN) && zcfd == -1) {
-	zwarnnam(fullname, "not connected.", NULL, 0);
+	if (ret != 2) {
+	    /*
+	     * with ret == 2, we just got dumped out in the test,
+	     * so enough messages already.
+	     */	       
+	    zwarnnam(fullname, "not connected.", NULL, 0);
+	}
 	return 1;
     }
 
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index a0c013901..42bc92bb2 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -41,7 +41,7 @@ struct compctl cc_compos, cc_default, cc_first, cc_dummy;
 /**/
 Cmlist cmatcher;
 
-/* pointers to functions required by zle */
+/* pointers to functions required by zle and defined by compctl */
 
 /**/
 void (*printcompctlptr) _((char *, Compctl, int, int));
@@ -49,6 +49,24 @@ void (*printcompctlptr) _((char *, Compctl, int, int));
 /**/
 Compctl (*compctl_widgetptr) _((char *, char **));
 
+/**/
+void (*makecompparamsptr) _((void));
+
+/* pointers to functions required by compctl and defined by zle */
+
+/**/
+void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, int, int, int, int, int, char **));
+
+/**/
+char *(*comp_strptr) _((int*,int*));
+
+/**/
+int (*getcpatptr) _((char *, int, char *, int));
+
+/**/
+void (*makecomplistcallptr) _((Compctl));
+
+
 /* Hash table for completion info for commands */
  
 /**/
@@ -72,6 +90,23 @@ char **clwords;
 /**/
 int incompctlfunc;
 
+/* != 0 if we are in a new style completion function */
+
+/**/
+int incompfunc;
+
+/* global variables for shell parameters in new style completion */
+
+/**/
+long compcurrent,
+     compnmatches;
+
+/**/
+char *compcontext,
+     *compcommand,
+     *compprefix,
+     *compsuffix,
+     *compiprefix;
 
 /* This variable and the functions rembslash() and quotename() came from     *
  * zle_tricky.c, but are now used in compctl.c, too.                         */
@@ -443,6 +478,8 @@ setup_comp1(Module m)
     cc_first.refc = 10000;
     cc_first.mask = 0;
     cc_first.mask2 = CC_CCCONT;
+    compcontext = compcommand = compprefix = compsuffix =
+	compiprefix = NULL;
     return 0;
 }
 
@@ -469,6 +506,11 @@ finish_comp1(Module m)
     deletehashtable(compctltab);
     zfree(clwords, clwsize * sizeof(char *));
     compctlreadptr = fallback_compctlread;
+    zsfree(compcontext);
+    zsfree(compcommand);
+    zsfree(compprefix);
+    zsfree(compiprefix);
+    zsfree(compsuffix);
     return 0;
 }
 
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index 1ac9195df..c90161740 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -1,4 +1,5 @@
 #!
+addmatchesptr
 cc_compos
 cc_default
 cc_dummy
@@ -8,14 +9,26 @@ clwords
 clwpos
 clwsize
 cmatcher
+compcommand
+compcontext
 compctl_widgetptr
 compctltab
+compcurrent
+compiprefix
+compnmatches
+compprefix
+comp_strptr
+compsuffix
 freecmatcher
 freecmlist
 freecompcond
 freecompctl
+getcpatptr
 incompctlfunc
+incompfunc
 instring
+makecomplistcallptr
+makecompparamsptr
 patcomps
 printcompctlptr
 quotename
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 4d192fef8..1913d3828 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -373,7 +373,7 @@ parse_class(Cpattern p, unsigned char *s, unsigned char e)
 
 /**/
 static int
-get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
+get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
 {
     /* Parse the basic flags for completion:
      * first is a flag that we are not in extended completion,
@@ -394,12 +394,17 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 	if(argv[0] && argv[0][0] == '-')
 	    argv++;
 	*av = argv;
-	freecompctl(cc);
- 	cclist = COMP_REMOVE;
-	return 0;
+	if (cl)
+	    return 1;
+	else {
+	    freecompctl(cc);
+	    cclist = COMP_REMOVE;
+	    return 0;
+	}
     }
 
     memset((void *)&cct, 0, sizeof(cct));
+    cct.mask2 = CC_CCCONT;
 
     /* Loop through the flags until we have no more:        *
      * those with arguments are not properly allocated yet, *
@@ -505,6 +510,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		{
 		    char *p;
 
+		    if (cl) {
+			zerrnam(name, "illegal option -%c", NULL, **argv);
+			return 1;
+		    }
 		    if ((*argv)[1]) {
 			p = (*argv) + 1;
 			*argv = "" - 1;
@@ -516,26 +525,28 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 			p = *++argv;
 			*argv = "" - 1;
 		    }
-		    while (*p) {
-			switch (*p) {
-			case '+':
-			    cct.mask2 |= CC_XORCONT;
-			    break;
-			case 'c':
-			    cct.mask2 |= CC_CCCONT;
-			    break;
-			case '-':
-			    cct.mask2 |= CC_PATCONT;
-			    break;
-			case 'x':
-			    cct.mask2 |= CC_DEFCONT;
-			    break;
-			default:
-			    zwarnnam(name, "invalid retry specification character `%c'",
-				     NULL, *p);
-			    return 1;
-			}
-			p++;
+		    switch (*p) {
+		    case '+':
+			cct.mask2 = CC_XORCONT;
+			break;
+		    case 'n':
+			cct.mask2 = 0;
+			break;
+		    case '-':
+			cct.mask2 = CC_PATCONT;
+			break;
+		    case 'x':
+			cct.mask2 = CC_DEFCONT;
+			break;
+		    default:
+			zwarnnam(name, "invalid retry specification character `%c'",
+				 NULL, *p);
+			return 1;
+		    }
+		    if (p[1]) {
+			zwarnnam(name, "too many retry specification characters: `%s'",
+				 p + 1, 0);
+			return 1;
 		    }
 		}
 		break;
@@ -645,7 +656,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		}
 		break;
 	    case 'l':
-		if ((*argv)[1]) {
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		} else if ((*argv)[1]) {
 		    cct.subcmd = (*argv) + 1;
 		    *argv = "" - 1;
 		} else if (!argv[1]) {
@@ -745,6 +759,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		*argv = "" - 1;
 		break;
 	    case 'C':
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		}
 		if (first && !hx) {
 		    cclist |= COMP_COMMAND;
 		} else {
@@ -754,6 +772,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		}
 		break;
 	    case 'D':
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		}
 		if (first && !hx) {
 		    isdef = 1;
 		    cclist |= COMP_DEFAULT;
@@ -764,7 +786,11 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		}
 		break;
  	    case 'T':
-              if (first && !hx) {
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		}
+		if (first && !hx) {
  		    cclist |= COMP_FIRST;
  		} else {
  		    zwarnnam(name, "misplaced first completion (-T) flag",
@@ -773,6 +799,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
  		}
  		break;
 	    case 'L':
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		}
 		if (!first || hx) {
 		    zwarnnam(name, "illegal use of -L flag", NULL, 0);
 		    return 1;
@@ -780,6 +810,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		cclist |= COMP_LIST;
 		break;
 	    case 'x':
+		if (cl) {
+		    zerrnam(name, "extended completion not allowed", NULL, 0);
+		    return 1;
+		}
 		if (!argv[1]) {
 		    zwarnnam(name, "condition expected after -%c", NULL,
 			    **argv);
@@ -811,6 +845,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 
 	if (*++argv && (!ready || ready == 2) &&
 	    **argv == '+' && !argv[0][1]) {
+	    if (cl) {
+		zerrnam(name, "xor'ed completion illegal", NULL, 0);
+		return 1;
+	    }
 	    /* There's an alternative (+) completion:  assign
 	     * what we have so far before moving on to that.
 	     */
@@ -835,6 +873,7 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		cc->xor = (Compctl) zcalloc(sizeof(*cc));
 		cc = cc->xor;
 		memset((void *)&cct, 0, sizeof(cct));
+		cct.mask2 = CC_CCCONT;
 	    }
 	}
     }
@@ -1084,7 +1123,7 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
 	(*next)->cond = m;
 	argv++;
 	/* End of the condition; get the flags that go with it. */
-	if (get_compctl(name, &argv, *next, 0, isdef))
+	if (get_compctl(name, &argv, *next, 0, isdef, 0))
 	    return 1;
  	if ((!argv || !*argv) && (cclist & COMP_SPECIAL))
  	    /* default, first, or command completion finished */
@@ -1362,17 +1401,16 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 	    t >>= 1;
 	}
     }
-    if (flags2 & (CC_XORCONT | CC_CCCONT | CC_PATCONT | CC_DEFCONT)) {
+    if (flags2 & (CC_XORCONT | CC_PATCONT | CC_DEFCONT)) {
 	printf(" -t");
 	if (flags2 & CC_XORCONT)
 	    putchar('+');
-	if (flags2 & CC_CCCONT)
-	    putchar('c');
 	if (flags2 & CC_PATCONT)
 	    putchar('-');
 	if (flags2 & CC_DEFCONT)
 	    putchar('x');
-    }
+    } else if (!(flags2 & CC_CCCONT))
+	printf(" -tn");
     /* now flags with arguments */
     printif(cc->mstr, 'M');
     if (flags2 & CC_NOSORT)
@@ -1518,7 +1556,7 @@ bin_compctl(char *name, char **argv, char *ops, int func)
 	    return ret - 1;
 
 	cc = (Compctl) zcalloc(sizeof(*cc));
-	if (get_compctl(name, &argv, cc, 1, 0)) {
+	if (get_compctl(name, &argv, cc, 1, 0, 0)) {
 	    freecompctl(cc);
 	    return 1;
 	}
@@ -1610,7 +1648,7 @@ compctl_widget(char *name, char **argv)
   cclist = 0;
   showmask = 0;
 
-  if (get_compctl(name, &argv, cc, 1, 0)) {
+  if (get_compctl(name, &argv, cc, 1, 0, 0)) {
       freecompctl(cc);
       return NULL;
   }
@@ -1632,8 +1670,478 @@ compctl_widget(char *name, char **argv)
   return cc;
 }
 
+/**/
+static int
+bin_complist(char *name, char **argv, char *ops, int func)
+{
+    Compctl cc;
+    int ret = 0;
+
+    if (!incompfunc) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    cc = (Compctl) zcalloc(sizeof(*cc));
+    cclist = 0;
+    showmask = 0;
+
+    if (get_compctl(name, &argv, cc, 1, 0, 1))
+	ret = 1;
+    else if (*argv) {
+	zerrnam(name, "command names illegal", NULL, 0);
+	ret = 1;
+    } else
+	makecomplistcallptr(cc);
+
+    freecompctl(cc);
+    return ret;
+}
+
+/**/
+static int
+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;
+    int f = 0, q = 0, m = 0, ns = 0, a = 0;
+
+    if (!incompfunc) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    for (; *argv && **argv ==  '-'; argv++) {
+	for (p = *argv + 1; *p; p++) {
+	    sp = NULL;
+	    switch (*p) {
+	    case 'q':
+		f |= CMF_REMOVE;
+		break;
+	    case 'Q':
+		q = 1;
+		break;
+	    case 'f':
+		f |= CMF_FILE;
+		break;
+	    case 'n':
+		f |= CMF_NOLIST;
+		break;
+	    case 'U':
+		m = 1;
+		break;
+	    case 'P':
+		sp = &pre;
+		e = "string expected after -%c";
+		break;
+	    case 'S':
+		sp = &suf;
+		e = "string expected after -%c";
+		break;
+	    case 'J':
+		sp = &group;
+		e = "group name expected after -%c";
+		break;
+	    case 'V':
+		if (!group)
+		    ns = 1;
+		sp = &group;
+		e = "group name expected after -%c";
+		break;
+	    case 'i':
+		sp = &ipre;
+		e = "string expected after -%c";
+		break;
+	    case 'p':
+		sp = &ppre;
+		e = "string expected after -%c";
+		break;
+	    case 's':
+		sp = &psuf;
+		e = "string expected after -%c";
+		break;
+	    case 'W':
+		sp = &prpre;
+		e = "string expected after -%c";
+		break;
+	    case 'a':
+		a = 1;
+		break;
+	    case '-':
+		argv++;
+		goto ca_args;
+	    default:
+		zerrnam(name, "bad option: -%c", NULL, *p);
+		return 1;
+	    }
+	    if (sp) {
+		if (*sp) {
+		    zerrnam(name, "doubled option: -%c", NULL, *p);
+		    return 1;
+		}
+		if (p[1]) {
+		    *sp = p + 1;
+		    p = "" - 1;
+		} else if (argv[1]) {
+		    *sp = *++argv;
+		    p = "" - 1;
+		} else {
+		    zerrnam(name, e, NULL, *p);
+		    return 1;
+		}
+	    }
+	}
+    }
+ ca_args:
+    if (!*argv) {
+	zerrnam(name, "missing completions", NULL, 0);
+	return 1;
+    }
+    addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
+		  f, q, m, ns, a, argv);
+    return 0;
+}
+
+#define VAR(X) ((void *) (&(X)))
+static struct compparam {
+    char *name;
+    int type;
+    void *var;
+} compparams[] = {
+    { "CURRENT", PM_INTEGER, VAR(compcurrent) },
+    { "CONTEXT", PM_SCALAR, VAR(compcontext) },
+    { "COMMAND", PM_SCALAR, VAR(compcommand) },
+    { "PREFIX", PM_SCALAR, VAR(compprefix) },
+    { "SUFFIX", PM_SCALAR, VAR(compsuffix) },
+    { "IPREFIX", PM_SCALAR, VAR(compiprefix) },
+    { "NMATCHES", PM_INTEGER, VAR(compnmatches) },
+    { NULL, 0, NULL }
+};
+
+/**/
+void makecompparams(void)
+{
+    struct compparam *cp;
+
+    for (cp = compparams; cp->name; cp++) {
+	Param pm = createparam(cp->name, cp->type | PM_SPECIAL);
+	if (!pm)
+	    pm = (Param) paramtab->getnode(paramtab, cp->name);
+	DPUTS(!pm, "param not set in makecompparams");
+
+	pm->level = locallevel;
+	pm->u.data = cp->var;
+	switch(PM_TYPE(cp->type)) {
+	case PM_SCALAR:
+	    pm->sets.cfn = strvarsetfn;
+	    pm->gets.cfn = strvargetfn;
+	    break;
+	case PM_INTEGER:
+	    pm->sets.ifn = intvarsetfn;
+	    pm->gets.ifn = intvargetfn;
+	    break;
+	}
+	pm->unsetfn = compunsetfn;
+    }
+}
+
+/**/
+static void
+compunsetfn(Param pm, int exp)
+{
+    if (exp)
+	stdunsetfn(pm, exp);
+}
+
+/**/
+static int
+comp_wrapper(List list, FuncWrap w, char *name)
+{
+    if (!incompfunc)
+	return 1;
+    else {
+	char *octxt, *ocmd, *opre, *osuf, *oipre;
+	long ocur;
+
+	ocur = compcurrent;
+	octxt = dupstring(compcontext);
+	ocmd = dupstring(compcommand);
+	opre = dupstring(compprefix);
+	osuf = dupstring(compsuffix);
+	oipre = dupstring(compiprefix);
+
+	runshfunc(list, w, name);
+
+	compcurrent = ocur;
+	zsfree(compcontext);
+	compcontext = ztrdup(octxt);
+	zsfree(compcommand);
+	compcommand = ztrdup(ocmd);
+	zsfree(compprefix);
+	compprefix = ztrdup(opre);
+	zsfree(compsuffix);
+	compsuffix = ztrdup(osuf);
+	zsfree(compiprefix);
+	compiprefix = ztrdup(oipre);
+
+	return 0;
+    }
+}
+
+/**/
+static void
+ignore_prefix(int l)
+{
+    char *o, sav = compprefix[l];
+
+    compprefix[l] = '\0';
+    o = compiprefix;
+    compiprefix = tricat(o, compprefix, "");
+    zsfree(o);
+    compprefix[l] = sav;
+    o = compprefix;
+    compprefix = ztrdup(o + l);
+    zsfree(o);
+}
+
+/**/
+static int
+comp_check(void)
+{
+    if (!incompfunc) {
+	zerr("condition can only be used in completion function", NULL, 0);
+	return 0;
+    }
+    return 1;
+}
+
+/**/
+static void
+restrict_range(int b, int e)
+{
+    int i = e - b;
+    char **p = (char **) zcalloc((i + 1) * sizeof(char *)), **q, **pp;
+
+    for (q = p, pp = pparams + b + 1; i; i--, q++, pp++)
+	*q = ztrdup(*pp);
+    zsfree(compcommand);
+    compcommand = ztrdup(pparams[b]);
+    freearray(pparams);
+    pparams = p;
+    zsfree(compcontext);
+    if ((compcurrent -= b + 1))
+	compcontext = ztrdup("arg");
+    else
+	compcontext = ztrdup("cmd");
+}
+
+/**/
+static int
+cond_prefix(char **a, int id)
+{
+    if (comp_check())
+	return strpfx(cond_str(a, 0), compprefix);
+    return 0;
+}
+
+/**/
+static int
+cond_iprefix(char **a, int id)
+{
+    if (comp_check()) {
+	char *s = cond_str(a, 0);
+
+	if (strpfx(s, compprefix)) {
+	    ignore_prefix(strlen(s));
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_position(char **a, int id)
+{
+    if (comp_check()) {
+	int b = cond_val(a, 0), e = (a[1] ? cond_val(a, 1) : b);
+	int l = arrlen(pparams), t, i = compcurrent - 1;
+
+	if (b > 0)
+	    b--;
+	if (e > 0)
+	    e--;
+	if (b < 0)
+	    b += l;
+	if (e < 0)
+	    e += l;
+	t = (b >= 0 && e >= 0 && i >= b && i <= e && b <= e);
+
+	if (t && a[1]) {
+	    if (b > l)
+		b = l;
+	    if (e > l)
+		e = l;
+	    restrict_range(b, e);
+	}
+	return t;
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_word(char **a, int id)
+{
+    if (comp_check()) {
+	int o = ((id & 2) ? compcurrent : 0) + cond_val(a, 0);
+	int l = arrlen(pparams);
+	char *s;
+
+	if (o < 0)
+	    o += l;
+
+	o--;
+	if (o < 0 || o >= l)
+	    return 0;
+
+	s = pparams[o];
+	return ((id & 1) ? cond_match(a, 1, s) : !strcmp(s, cond_str(a, 1)));
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_strcl(char **a, int id)
+{
+    if (comp_check()) {
+	char *s;
+	int i;
+
+	if (a[1]) {
+	    s = cond_str(a, 1);
+	    i = cond_val(a, 0);
+	} else {
+	    s = cond_str(a, 0);
+	    i = -1;
+	}
+	if (!getcpatptr) {
+	    zerr("zle not loaded, zle condition not available", NULL, 0);
+	    return 1;
+	}
+	i = getcpatptr(comp_strptr(NULL, NULL), i, s, id);
+	if (i != -1) {
+	    ignore_prefix(i);
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_words(char **a, int id)
+{
+    if (comp_check()) {
+	int b = cond_val(a, 0), e = (a[1] ? cond_val(a, 1) : -1);
+	int l = arrlen(pparams);
+
+	return (l >= b && l <= e);
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_range(char **a, int id)
+{
+    if (comp_check()) {
+	char *s, **p;
+	int i, l = arrlen(pparams), t = 0, b = 0, e = l - 1;
+	Comp c;
+
+	i = compcurrent - 1;
+	if (i < 0 || i >= l)
+	    return 0;
+
+	if (id & 1) {
+	    s = a[0];
+	    singsub(&s);
+	    c = parsereg(s);
+	} else
+	    s = cond_str(a, 0);
+
+	for (i--, p = pparams + i; i >= 0; p--, i--) {
+	    if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) {
+		b = i + 1;
+		t = 1;
+		break;
+	    }
+	}
+	if (t && (id & 2)) {
+	    int tt = 0;
+
+	    if (id & 1) {
+		s = a[1];
+		singsub(&s);
+		c = parsereg(s);
+	    } else
+		s = cond_str(a, 1);
+
+	    for (i++, p = pparams + i; i < l; p++, i++) {
+		if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) {
+		    e = i - 1;
+		    tt = 1;
+		    break;
+		}
+	    }
+	    if (tt && i < compcurrent)
+		t = 0;
+	}
+	if (e < b)
+	    t = 0;
+	if (t)
+	    restrict_range(b, e);
+	return t;
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_nmatches(char **a, int id)
+{
+    if (comp_check())
+	return compnmatches == cond_val(a, 0);
+    return 0;
+}
+
 static struct builtin bintab[] = {
     BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
+    BUILTIN("complist", 0, bin_complist, 1, -1, 0, NULL, NULL),
+    BUILTIN("compadd", 0, bin_compadd, 1, -1, 0, NULL, NULL),
+};
+
+static struct conddef cotab[] = {
+    CONDDEF("prefix", 0, cond_prefix, 1, 1, 0),
+    CONDDEF("iprefix", 0, cond_iprefix, 1, 1, 0),
+    CONDDEF("position", 0, cond_position, 1, 2, 0),
+    CONDDEF("word", 0, cond_word, 2, 2, 0),
+    CONDDEF("mword", 0, cond_word, 2, 2, 1),
+    CONDDEF("current", 0, cond_word, 2, 2, 2),
+    CONDDEF("mcurrent", 0, cond_word, 2, 2, 3),
+    CONDDEF("string", 0, cond_strcl, 1, 2, 0),
+    CONDDEF("class", 0, cond_strcl, 1, 2, 1),
+    CONDDEF("words", 0, cond_words, 1, 2, 0),
+    CONDDEF("between", 0, cond_range, 2, 2, 2),
+    CONDDEF("mbetween", 0, cond_range, 2, 2, 3),
+    CONDDEF("after", 0, cond_range, 1, 1, 0),
+    CONDDEF("mafter", 0, cond_range, 1, 1, 1),
+    CONDDEF("nmatches", 0, cond_nmatches, 1, 1, 0),
+};
+
+static struct funcwrap wrapper[] = {
+    WRAPDEF(comp_wrapper),
 };
 
 /**/
@@ -1643,6 +2151,7 @@ setup_compctl(Module m)
     compctltab->printnode = printcompctlp;
     printcompctlptr = printcompctl;
     compctl_widgetptr = compctl_widget;
+    makecompparamsptr = makecompparams;
     return 0;
 }
 
@@ -1650,7 +2159,9 @@ setup_compctl(Module m)
 int
 boot_compctl(Module m)
 {
-    if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
+    if(!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
+	 addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
+	 !addwrapper(m, wrapper)))
 	return 1;
     return 0;
 }
@@ -1662,6 +2173,8 @@ int
 cleanup_compctl(Module m)
 {
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    deletewrapper(m, wrapper);
     return 0;
 }
 
@@ -1672,6 +2185,7 @@ finish_compctl(Module m)
     compctltab->printnode = NULL;
     printcompctlptr = NULL;
     compctl_widgetptr = NULL;
+    makecompparamsptr = NULL;
     return 0;
 }
 
diff --git a/Src/Zle/compctl.mdd b/Src/Zle/compctl.mdd
index c83ecda29..48aabe38a 100644
--- a/Src/Zle/compctl.mdd
+++ b/Src/Zle/compctl.mdd
@@ -1,5 +1,7 @@
 moddeps="comp1"
 
-autobins="compctl"
+autobins="compctl complist compadd"
+
+autoprefixconds="prefix iprefix position word mword current mcurrent string class words between mbetween after mafter nmatches"
 
 objects="compctl.o"
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 52e70fad5..12425d872 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -25,11 +25,11 @@
 "beginning-of-line-hist", beginningoflinehist, 0
 "capitalize-word", capitalizeword, 0
 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
-"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "copy-prev-word", copyprevword, 0
 "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
 "delete-char", deletechar, ZLE_KEEPSUFFIX
-"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "delete-word", deleteword, ZLE_KEEPSUFFIX
 "describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
@@ -48,8 +48,8 @@
 "execute-named-cmd", NULL, 0
 "expand-cmd-path", expandcmdpath, 0
 "expand-history", expandhistory, 0
-"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
+"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "expand-word", expandword, 0
 "forward-char", forwardchar, 0
 "forward-word", forwardword, 0
@@ -68,11 +68,11 @@
 "kill-region", killregion, ZLE_KILL | ZLE_KEEPSUFFIX
 "kill-whole-line", killwholeline, ZLE_KILL | ZLE_KEEPSUFFIX
 "kill-word", killword, ZLE_KILL | ZLE_KEEPSUFFIX
-"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_ISCOMP
 "list-expand", listexpand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "magic-space", magicspace, 0
-"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
+"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "overwrite-mode", overwritemode, 0
 "pound-insert", poundinsert, 0
@@ -84,7 +84,7 @@
 "quote-region", quoteregion, 0
 "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "redo", redo, 0
-"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "self-insert", selfinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 71a929f87..f12505bd3 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -47,23 +47,29 @@ struct widget {
 	ZleIntFunc fn;	/* pointer to internally implemented widget */
 	char *fnnam;	/* name of the shell function for user-defined widget */
         Compctl cc;     /* for use with a WIDGET_COMP widget */
+	struct {
+	    ZleIntFunc fn; /* internal widget function to call */
+	    char *wid;     /* name of widget to call */
+	    char *func;    /* name of shell function to call */
+	} comp;
     } u;
 };
 
 #define WIDGET_INT	(1<<0)    /* widget is internally implemented */
 #define WIDGET_COMP	(1<<1)	  /* Special completion widget */
-#define ZLE_MENUCMP	(1<<2)    /* DON'T invalidate completion list */
-#define ZLE_YANK	(1<<3)
-#define ZLE_LINEMOVE	(1<<4)    /* command is a line-oriented movement */
-#define ZLE_LASTCOL     (1<<5)    /* command maintains lastcol correctly */
-#define ZLE_KILL	(1<<6)
+#define WIDGET_NCOMP    (1<<2)    /* new style completion widget */
+#define ZLE_MENUCMP	(1<<3)    /* DON'T invalidate completion list */
+#define ZLE_YANK	(1<<4)
+#define ZLE_LINEMOVE	(1<<5)    /* command is a line-oriented movement */
+#define ZLE_LASTCOL     (1<<6)    /* command maintains lastcol correctly */
+#define ZLE_KILL	(1<<7)
 #define ZLE_KEEPSUFFIX	(1<<9)    /* DON'T remove added suffix */
 #define ZLE_USEMENU	(1<<10)   /* Do    ) use menu completion for   */
 #define ZLE_NOMENU	(1<<11)   /* Don't )  widget, else use default */
 #define ZLE_USEGLOB	(1<<12)   /* Do    ) use glob completion for   */
 #define ZLE_NOGLOB	(1<<13)   /* Don't )  widget, else use default */
 #define ZLE_NOTCOMMAND  (1<<14)   /* widget should not alter lastcmd */
-
+#define ZLE_ISCOMP      (1<<15)   /* usable for new style completion */
 /* thingies */
 
 struct thingy {
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 7de96bd03..1ffe6f156 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -598,10 +598,10 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !ops[op->o]; op++) ;
+    for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(ops[opp->o]) {
+	    if(ops[STOUC(opp->o)]) {
 		zwarnnam(name, "incompatible operation selection options",
 		    NULL, 0);
 		return 1;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 515405a0d..2c0d3655e 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -443,9 +443,9 @@ zleread(char *lp, char *rp, int ha)
     insmode = unset(OVERSTRIKE);
     eofsent = 0;
     resetneeded = 0;
-    lpptbuf = promptexpand(lp, 1, NULL, NULL);
+    lpromptbuf = promptexpand(lp, 1, NULL, NULL);
     pmpt_attr = txtchange;
-    rpptbuf = promptexpand(rp, 1, NULL, NULL);
+    rpromptbuf = promptexpand(rp, 1, NULL, NULL);
     rpmpt_attr = txtchange;
     histallowed = ha;
     PERMALLOC {
@@ -529,8 +529,8 @@ zleread(char *lp, char *rp, int ha)
 	statusline = NULL;
 	invalidatelist();
 	trashzle();
-	free(lpptbuf);
-	free(rpptbuf);
+	free(lpromptbuf);
+	free(rpromptbuf);
 	zleactive = 0;
 	alarm(0);
     } LASTALLOC;
@@ -565,13 +565,14 @@ execzlefunc(Thingy func)
 	showmsg(msg);
 	zsfree(msg);
 	feep();
-    } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_COMP)) {
+    } else if((w = func->widget)->flags &
+	      (WIDGET_INT|WIDGET_COMP | WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
 	if(!(wflags & ZLE_KEEPSUFFIX))
 	    removesuffix();
 	if(!(wflags & ZLE_MENUCMP) ||
-	   ((wflags & WIDGET_COMP) && compwidget != w)) {
+	   ((wflags & (WIDGET_COMP|WIDGET_NCOMP)) && compwidget != w)) {
 	    /* If we are doing a special completion, and the widget
 	     * is not the one currently in use for special completion,
 	     * we are starting a new completion.
@@ -586,6 +587,9 @@ execzlefunc(Thingy func)
 	if (wflags & WIDGET_COMP) {
 	    compwidget = w;
 	    completespecial();
+	} else if (wflags & WIDGET_NCOMP) {
+	    compwidget = w;
+	    completecall();
 	} else
 	    w->u.fn();
 	if (!(wflags & ZLE_NOTCOMMAND))
@@ -855,7 +859,7 @@ trashzle(void)
 static struct builtin bintab[] = {
     BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaMldDANmrsLR", NULL),
     BUILTIN("vared",   0, bin_vared,   1,  7, 0, NULL,             NULL),
-    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANCLmMgG",     NULL),
+    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANCLmMgGc",    NULL),
 };
 
 /**/
@@ -869,6 +873,11 @@ setup_zle(Module m)
     spaceinlineptr = spaceinline;
     zlereadptr = zleread;
 
+    addmatchesptr = addmatches;
+    comp_strptr = comp_str;
+    getcpatptr = getcpat;
+    makecomplistcallptr = makecomplistcall;
+
     /* initialise the thingies */
     init_thingies();
 
@@ -931,6 +940,11 @@ finish_zle(Module m)
     spaceinlineptr = noop_function_int;
     zlereadptr = fallback_zleread;
 
+    addmatchesptr = NULL;
+    comp_strptr = NULL;
+    getcpatptr = NULL;
+    makecomplistcallptr = NULL;
+
     return 0;
 }
 
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 27e9f2071..c8d6c70a7 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -33,7 +33,7 @@
 /* Expanded prompts */
 
 /**/
-char *lpptbuf, *rpptbuf;
+char *lpromptbuf, *rpromptbuf;
 
 /* Text attributes after displaying prompts */
 
@@ -77,17 +77,17 @@ int cost;
 /* Oct/Nov 94: <mason> some code savagely redesigned to fix several bugs -
    refreshline() & tc_rightcurs() majorly rewritten; zrefresh() fixed -
    I've put my fingers into just about every routine in here -
-   any queries about updates to mason@werple.net.au */
+   any queries about updates to mason@primenet.com.au */
 
 static char **nbuf = NULL,	/* new video buffer line-by-line char array */
     **obuf = NULL;		/* old video buffer line-by-line char array */
 static int more_start,		/* more text before start of screen?	    */
     more_end,			/* more stuff after end of screen?	    */
-    lppth,			/* lines taken up by the prompt		    */
     olnct,			/* previous number of lines		    */
     ovln,			/* previous video cursor position line	    */
-    pptw, rpw,                  /* prompt widths on screen                  */
-    rppth,			/* right prompt height                      */
+    lpromptw, rpromptw,		/* prompt widths on screen                  */
+    lprompth,			/* lines taken up by the prompt		    */
+    rprompth,			/* right prompt height                      */
     vcs, vln,			/* video cursor position column & line	    */
     vmaxln,			/* video maximum number of lines	    */
     winw, winh, rwinh,		/* window width & height		    */
@@ -100,7 +100,6 @@ resetvideo(void)
     int ln;
     static int lwinw = -1, lwinh = -1;	/* last window width & height */
  
-    genprompts();
     winw = columns;  /* terminal width */
     if (termflags & TERM_SHORT)
 	winh = 1;
@@ -132,13 +131,16 @@ resetvideo(void)
 	    *obuf[ln] = '\0';
     }
 
-    if (pptw) {
-    	memset(nbuf[0], ' ', pptw);
-	memset(obuf[0], ' ', pptw);
-	nbuf[0][pptw] = obuf[0][pptw] = '\0';
+    countprompt(lpromptbuf, &lpromptw, &lprompth);
+    countprompt(rpromptbuf, &rpromptw, &rprompth);
+
+    if (lpromptw) {
+    	memset(nbuf[0], ' ', lpromptw);
+	memset(obuf[0], ' ', lpromptw);
+	nbuf[0][lpromptw] = obuf[0][lpromptw] = '\0';
     }
 
-    vcs = pptw;
+    vcs = lpromptw;
     olnct = nlnct = 0;
     if (showinglist > 0)
 	showinglist = -2;
@@ -280,21 +282,25 @@ zrefresh(void)
 	tsetcap(TCSTANDOUTEND, 0);
 	tsetcap(TCUNDERLINEEND, 0);
 
-        if (!clearflag)
+        if (!clearflag) {
             if (tccan(TCCLEAREOD))
                 tcout(TCCLEAREOD);
             else
                 cleareol = 1;   /* request: clear to end of line */
+	}
         if (t0 > -1)
             olnct = t0;
         if (termflags & TERM_SHORT)
             vcs = 0;
-        else if (!clearflag && lpptbuf[0])
-            zputs(lpptbuf, shout);
+        else if (!clearflag && lpromptbuf[0]) {
+            zputs(lpromptbuf, shout);
+	    if (lpromptw == 0)
+		zputs("\n", shout);	/* works with both hasam and !hasam */
+	}
 	if (clearflag) {
 	    zputc('\r', shout);
 	    vcs = 0;
-	    moveto(0, pptw);
+	    moveto(0, lpromptw);
 	}
 	fflush(shout);
 	clearf = clearflag;
@@ -326,7 +332,7 @@ zrefresh(void)
     if (!*nbuf)
 	*nbuf = (char *)zalloc(winw + 2);
 
-    s = (unsigned char *)(nbuf[ln = 0] + pptw);
+    s = (unsigned char *)(nbuf[ln = 0] + lpromptw);
     t = line;
     sen = (unsigned char *)(*nbuf + winw);
     for (; t < line+ll; t++) {
@@ -425,15 +431,16 @@ zrefresh(void)
 
 /* determine whether the right-prompt exists and can fit on the screen */
     if (!more_start)
-	put_rpmpt = rppth == 1 && rpptbuf[0] && !strchr(rpptbuf, '\t') &&
-	    (int)strlen(nbuf[0]) + rpw < winw - 1;
+	put_rpmpt = rprompth == 1 && rpromptbuf[0] &&
+	    !strchr(rpromptbuf, '\t') &&
+	    (int)strlen(nbuf[0]) + rpromptw < winw - 1;
     else {
 /* insert >.... on first line if there is more text before start of screen */
-	memset(nbuf[0], ' ', pptw);
-	t0 = winw - pptw;
+	memset(nbuf[0], ' ', lpromptw);
+	t0 = winw - lpromptw;
 	t0 = t0 > 5 ? 5 : t0;
-	strncpy(nbuf[0] + pptw, ">....", t0);
-	memset(nbuf[0] + pptw + t0, ' ', winw - t0 - pptw);
+	strncpy(nbuf[0] + lpromptw, ">....", t0);
+	memset(nbuf[0] + lpromptw + t0, ' ', winw - t0 - lpromptw);
 	nbuf[0][winw] = nbuf[0][winw + 1] = '\0';
     }
 
@@ -477,8 +484,8 @@ zrefresh(void)
 
     /* output the right-prompt if appropriate */
 	if (put_rpmpt && !ln && !oput_rpmpt) {
-	    moveto(0, winw - 1 - rpw);
-	    zputs(rpptbuf, shout);
+	    moveto(0, winw - 1 - rpromptw);
+	    zputs(rpromptbuf, shout);
 	    vcs = winw - 1;
 	/* reset character attributes to that set by the main prompt */
 	    txtchange = pmpt_attr;
@@ -659,12 +666,12 @@ refreshline(int ln)
 /* 2c: if we're on the first line, start checking at the end of the prompt;
    we shouldn't be doing anything within the prompt */
 
-    if (ln == 0 && pptw) {
-	i = pptw - ccs;
+    if (ln == 0 && lpromptw) {
+	i = lpromptw - ccs;
 	j = strlen(ol);
 	nl += i;
 	ol += (i > j ? j : i);	/* if ol is too short, point it to '\0' */
-	ccs = pptw;
+	ccs = lpromptw;
     }
 
 /* 3: main display loop - write out the buffer using whatever tricks we can */
@@ -815,7 +822,7 @@ moveto(int ln, int cl)
    instead of TCDOWN */
 
     while (ln > vln) {
-	if (vln < vmaxln - 1)
+	if (vln < vmaxln - 1) {
 	    if (ln > vmaxln - 1) {
 		if (tc_downcurs(vmaxln - 1 - vln))
 		    vcs = 0;
@@ -826,6 +833,7 @@ moveto(int ln, int cl)
 		vln = ln;
 		continue;
 	    }
+	}
 	zputc('\r', shout), vcs = 0; /* safety precaution */
 	while (ln > vln) {
 	    zputc('\n', shout);
@@ -893,21 +901,23 @@ tc_rightcurs(int cl)
 
 /* otherwise _carefully_ write the contents of the video buffer.
    if we're anywhere in the prompt, goto the left column and write the whole
-   prompt out unless ztrlen(lpptbuf) == pptw : we can cheat then */
-    if (vln == 0 && i < pptw) {
-	if (strlen(lpptbuf) == pptw)
-	    fputs(lpptbuf + i, shout);
-	else if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpptbuf)))
+   prompt out unless ztrlen(lpromptbuf) == lpromptw : we can cheat then */
+    if (vln == 0 && i < lpromptw) {
+	if (strlen(lpromptbuf) == lpromptw)
+	    fputs(lpromptbuf + i, shout);
+	else if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= ztrlen(lpromptbuf)))
 	    /* it is cheaper to send TCRIGHT than reprint the whole prompt */
-	    for (ct = pptw - i; ct--; )
+	    for (ct = lpromptw - i; ct--; )
 		tcout(TCRIGHT);
         else {
 	    if (i != 0)
 		zputc('\r', shout);
-	    tc_upcurs(lppth - 1);
-	    zputs(lpptbuf, shout);
+	    tc_upcurs(lprompth - 1);
+	    zputs(lpromptbuf, shout);
+	    if (lpromptw == 0)
+		zputs("\n", shout);	/* works with both hasam and !hasam */
 	}
-	i = pptw;
+	i = lpromptw;
 	ct = cl - i;
     }
 
@@ -969,7 +979,7 @@ redisplay(void)
 {
     moveto(0, 0);
     zputc('\r', shout);		/* extra care */
-    tc_upcurs(lppth - 1);
+    tc_upcurs(lprompth - 1);
     resetneeded = 1;
     clearflag = 0;
 }
@@ -987,7 +997,7 @@ singlerefresh(void)
 
     nlnct = 1;
 /* generate the new line buffer completely */
-    for (vsiz = 1 + pptw, t0 = 0; t0 != ll; t0++, vsiz++)
+    for (vsiz = 1 + lpromptw, t0 = 0; t0 != ll; t0++, vsiz++)
 	if (line[t0] == '\t')
 	    vsiz = (vsiz | 7) + 1;
 	else if (icntrl(line[t0]))
@@ -1002,9 +1012,10 @@ singlerefresh(void)
 	cs = 0;
     }
 
-    memcpy(vbuf, strchr(lpptbuf, 0) - pptw, pptw); /* only use last part of prompt */
-    vbuf[pptw] = '\0';
-    vp = vbuf + pptw;
+    /* only use last part of prompt */
+    memcpy(vbuf, strchr(lpromptbuf, 0) - lpromptw, lpromptw);
+    vbuf[lpromptw] = '\0';
+    vp = vbuf + lpromptw;
 
     for (t0 = 0; t0 != ll; t0++) {
 	if (line[t0] == '\t')
@@ -1104,13 +1115,3 @@ singmoveto(int pos)
 	    }
     }
 }
-
-/* recheck size of prompts */
-
-/**/
-static void
-genprompts(void)
-{
-    countprompt(lpptbuf, &pptw, &lppth);
-    countprompt(rpptbuf, &rpw, &rppth);
-}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 68329be65..2e21b5add 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -246,7 +246,10 @@ freewidget(Widget w)
 {
     if ((w->flags & WIDGET_COMP) && w->u.cc)
 	freecompctl(w->u.cc);
-    else if(!(w->flags & WIDGET_INT))
+    else if (w->flags & WIDGET_NCOMP) {
+	zsfree(w->u.comp.wid);
+	zsfree(w->u.comp.func);
+    } else if(!(w->flags & WIDGET_INT))
 	zsfree(w->u.fnnam);
     zfree(w, sizeof(*w));
 }
@@ -337,16 +340,17 @@ bin_zle(char *name, char **args, char *ops, int func)
 	{ 'A', bin_zle_link, 2,  2 },
 	{ 'N', bin_zle_new,  1,  2 },
 	{ 'C', bin_zle_compctl, 1, -1},
+	{ 'c', bin_zle_complete, 3, 3 },
 	{ 0,   bin_zle_call, 0, -1 },
     };
     struct opn const *op, *opp;
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !ops[op->o]; op++) ;
+    for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(ops[opp->o]) {
+	    if(ops[STOUC(opp->o)]) {
 		zerrnam(name, "incompatible operation selection options",
 		    NULL, 0);
 		return 1;
@@ -395,6 +399,11 @@ scanlistwidgets(HashNode hn, int list)
 	if (w->flags & WIDGET_COMP) {
 	    if (printcompctlptr && w->u.cc)
 		printcompctlptr(NULL, w->u.cc, PRINT_LIST, 0);
+	} else if (w->flags & WIDGET_NCOMP) {
+	    fputc(' ', stdout);
+	    quotedzputs(w->u.comp.wid, stdout);
+	    fputc(' ', stdout);
+	    quotedzputs(w->u.comp.func, stdout);
 	} else if(strcmp(t->nam, w->u.fnnam)) {
 	    fputc(' ', stdout);
 	    quotedzputs(w->u.fnnam, stdout);
@@ -405,6 +414,11 @@ scanlistwidgets(HashNode hn, int list)
 	    fputs(" -C", stdout);
 	    if (printcompctlptr && w->u.cc)
 		printcompctlptr(NULL, w->u.cc, PRINT_TYPE, 0);
+	} else if (w->flags & WIDGET_NCOMP) {
+	    fputs(" -c ", stdout);
+	    nicezputs(w->u.comp.wid, stdout);
+	    fputc(' ', stdout);
+	    nicezputs(w->u.comp.func, stdout);
 	} else if(strcmp(t->nam, w->u.fnnam)) {
 	    fputs(" (", stdout);
 	    nicezputs(w->u.fnnam, stdout);
@@ -506,11 +520,39 @@ bin_zle_compctl(char *name, char **args, char *ops, char func)
 
 /**/
 static int
+bin_zle_complete(char *name, char **args, char *ops, char func)
+{
+    Thingy t;
+    Widget w, cw;
+
+    t = rthingy(args[1]);
+    cw = t->widget;
+    unrefthingy(t);
+    if (!(cw->flags & ZLE_ISCOMP)) {
+	zerrnam(name, "invalid widget `%s'", args[1], 0);
+	return 1;
+    }
+    w = zalloc(sizeof(*w));
+    w->flags = WIDGET_NCOMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX;
+    w->first = NULL;
+    w->u.comp.fn = cw->u.fn;
+    w->u.comp.wid = ztrdup(args[1]);
+    w->u.comp.func = ztrdup(args[2]);
+    if (bindwidget(w, rthingy(args[0]))) {
+	freewidget(w);
+	zerrnam(name, "widget name `%s' is protected", args[0], 0);
+	return 1;
+    }
+    return 0;
+}
+
+/**/
+static int
 bin_zle_call(char *name, char **args, char *ops, char func)
 {
     Thingy t;
 
-    if(!zleactive || incompctlfunc) {
+    if(!zleactive || incompctlfunc || incompfunc) {
 	zerrnam(name, "widgets can only be called when ZLE is active",
 	    NULL, 0);
 	return 1;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 8c976449e..a958752ca 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -237,6 +237,15 @@ struct aminfo {
 
 static Aminfo ainfo, fainfo;
 
+/* This contains the name of the function to call if this is for a new  *
+ * style completion. */
+
+static char *compfunc = NULL;
+
+/* The memory heap to use for new style completion generation. */
+
+static Heap compheap;
+
 /* Find out if we have to insert a tab (instead of trying to complete). */
 
 /**/
@@ -274,6 +283,15 @@ completespecial(void)
 
 /**/
 void
+completecall(void)
+{
+    compfunc = compwidget->u.comp.func;
+    compwidget->u.comp.fn();
+    compfunc = NULL;
+}
+
+/**/
+void
 completeword(void)
 {
     usemenu = isset(MENUCOMPLETE);
@@ -408,11 +426,15 @@ reversemenucomplete(void)
 void
 acceptandmenucomplete(void)
 {
+    int sl = suffixlen[' '];
+
     if (!menucmp) {
 	feep();
 	return;
     }
-    cs = menuend + menuinsc;
+    cs = menupos + menulen + menuinsc;
+    if (sl)
+	backdel(sl);
     inststrlen(" ", 1, 1);
     menuinsc = menulen = 0;
     menupos = cs;
@@ -439,6 +461,13 @@ static int lastambig;
 
 static char *cmdstr;
 
+/* This hold the name of the variable we are working on. */
+
+static char *varname;
+
+/* != 0 if we are in a subscript */
+
+static int insubscr;
 
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
@@ -614,16 +643,13 @@ docomplete(int lst)
 			lst = COMP_EXPAND;
 		    else {
 			int t0, n = 0;
-			char *fc;
 			struct hashnode *hn;
 
 			for (t0 = cmdnamtab->hsize - 1; t0 >= 0; t0--)
 			    for (hn = cmdnamtab->nodes[t0]; hn;
 				 hn = hn->next) {
-				if (strpfx(q, hn->nam) && (fc = findcmd(hn->nam))) {
-				    zsfree(fc);
+				if (strpfx(q, hn->nam) && findcmd(hn->nam, 0))
 				    n++;
-				}
 				if (n == 2)
 				    break;
 			    }
@@ -889,7 +915,7 @@ unmetafy_line(void)
 static char *
 get_comp_string(void)
 {
-    int t0, tt0, i, j, k, cp, rd, sl, ocs;
+    int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins;
     char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
 
     zsfree(brbeg);
@@ -943,13 +969,16 @@ get_comp_string(void)
 	linredir = inredir;
 	zsfree(cmdstr);
 	cmdstr = NULL;
+	zsfree(varname);
+	varname = NULL;
+	insubscr = 0;
 	zleparse = 1;
 	clwpos = -1;
 	lexsave();
 	inpush(dupstrspace((char *) linptr), 0, NULL);
 	strinbeg();
 	stophist = 2;
-	i = tt0 = cp = rd = 0;
+	i = tt0 = cp = rd = ins = oins = 0;
 
 	/* This loop is possibly the wrong way to do this.  It goes through *
 	 * the previously massaged command line using the lexer.  It stores *
@@ -963,8 +992,10 @@ get_comp_string(void)
 	 * this would be to pass the command line through the parser too,   *
 	 * and get the arguments that way.  Maybe in 3.1...                 */
 	do {
-	    lincmd = incmdpos;
-	    linredir = inredir;
+	    lincmd = ((incmdpos && !ins) || (oins == 2 && i == 2) ||
+		      (ins == 3 && i == 1));
+	    linredir = (inredir && !ins);
+	    oins = ins;
 	    /* Get the next token. */
 	    ctxtlex();
 	    if (tok == DINPAR)
@@ -973,7 +1004,9 @@ get_comp_string(void)
 	    /* We reached the end. */
 	    if (tok == ENDINPUT)
 		break;
-	    if (tok == BAR    || tok == AMPER     ||
+	    if ((ins && (tok == DO || tok == SEPER)) ||
+		(ins == 2 && i == 2) ||	(ins == 3 && i == 3) ||
+		tok == BAR    || tok == AMPER     ||
 		tok == BARAMP || tok == AMPERBANG ||
 		((tok == DBAR || tok == DAMPER) && !incond)) {
 		/* This is one of the things that separate commands.  If we  *
@@ -982,11 +1015,13 @@ get_comp_string(void)
 		if (tt)
 		    break;
 		/* Otherwise reset the variables we are collecting data in. */
-		i = tt0 = cp = rd = 0;
+		i = tt0 = cp = rd = ins = 0;
 	    }
-	    if (lincmd && tok == STRING) {
+	    if (lincmd && (tok == STRING || tok == FOR || tok == FOREACH ||
+			   tok == SELECT || tok == REPEAT || tok == CASE)) {
 		/* The lexer says, this token is in command position, so *
 		 * store the token string (to find the right compctl).   */
+		ins = (tok == REPEAT ? 2 : (tok != STRING));
 		zsfree(cmdstr);
 		cmdstr = ztrdup(tokstr);
 		i = 0;
@@ -1004,9 +1039,13 @@ get_comp_string(void)
 		rd = linredir;
 		if (inwhat == IN_NOTHING && incond)
 		    inwhat = IN_COND;
-	    }
+	    } else if (linredir)
+		continue;
 	    if (!tokstr)
 		continue;
+	    /* Hack to allow completion after `repeat n do'. */
+	    if (oins == 2 && !i && !strcmp(tokstr, "do"))
+		ins = 3;
 	    /* We need to store the token strings of all words (for some of *
 	     * the more complicated compctl -x things).  They are stored in *
 	     * the clwords array.  Make this array big enough.              */
@@ -1069,10 +1108,16 @@ get_comp_string(void)
 	    /* We found a simple string. */
 	    s = ztrdup(clwords[clwpos]);
 	} else if (t0 == ENVSTRING) {
+	    char sav;
 	    /* The cursor was inside a parameter assignment. */
 	    for (s = tt; iident(*s); s++);
+	    sav = *s;
+	    *s = '\0';
+	    zsfree(varname);
+	    varname = ztrdup(tt);
+	    *s = sav;
 	    if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb)
-		s = NULL, inwhat = IN_MATH;
+		s = NULL, inwhat = IN_MATH, insubscr = 1;
 	    else if (*s == '=') {
 		s++;
 		wb += s - tt;
@@ -1110,14 +1155,32 @@ get_comp_string(void)
 	 * foo[_ wrong (note no $).  If we are in a subscript, treat it   *
 	 * as being in math.                                              */
 	if (inwhat != IN_MATH) {
-	    int i = 0;
+	    int i = 0, hn = 0;
+	    char *nb = (*s == String ? s + 1 : NULL), *ne = NULL;
+
 	    for (tt = s; ++tt < s + cs - wb;)
-		if (*tt == Inbrack)
+		if (*tt == String) {
+		    hn = 0;
+		    nb = tt + 1;
+		} else if (*tt == Inbrack) {
 		    i++;
-		else if (i && *tt == Outbrack)
+		    if (nb && !hn) {
+			hn = 1;
+			ne = tt;
+		    }
+		} else if (i && *tt == Outbrack)
 		    i--;
-	    if (i)
+	    if (i) {
 		inwhat = IN_MATH;
+		insubscr = 1;
+		if (hn && nb && ne) {
+		    char sav = *ne;
+		    *ne = '\0';
+		    zsfree(varname);
+		    varname = ztrdup(nb);
+		    *ne = sav;
+		}
+	    }
 	}
 	if (inwhat == IN_MATH) {
 	    /* In mathematical expression, we complete parameter names (even *
@@ -2336,6 +2399,119 @@ instmatch(Cmatch m)
     return r;
 }
 
+/**/
+void
+addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
+	   char *suf, char *group,
+	   int flags, int quote, int menu, int nosort, int alt, char **argv)
+{
+    char *s, *t;
+    int lpl, lsl, i;
+    Aminfo ai = (alt ? fainfo : ainfo);
+    Cmatch cm;
+
+    if (menu && isset(AUTOMENU))
+	usemenu = 1;
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    if (ipre)
+		ipre = dupstring(ipre);
+	    if (ppre) {
+		ppre = dupstring(ppre);
+		lpl = strlen(ppre);
+	    } else
+		lpl = 0;
+	    if (psuf) {
+		psuf = dupstring(psuf);
+		lsl = strlen(psuf);
+	    } else
+		lsl = 0;
+	    if (pre)
+		pre = dupstring(pre);
+	    if (suf)
+		suf = dupstring(suf);
+	    if (!prpre && (prpre = ppre)) {
+		singsub(&prpre);
+		untokenize(prpre);
+	    } else
+		prpre = dupstring(prpre);
+	    if (group) {
+		endcmgroup(NULL);
+		begcmgroup(group, nosort);
+		if (nosort)
+		    mgroup->flags |= CGF_NOSORT;
+	    }
+    	    if (ai->pprefix) {
+		if (pre)
+		    ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0';
+		else
+		    ai->pprefix[0] = '\0';
+	    } else
+		ai->pprefix = dupstring(pre ? pre : "");
+
+	    for (; (s = *argv); argv++) {
+		if (ai->firstm) {
+		    if ((i = pfxlen(ai->firstm->str, s)) < ai->prerest)
+			ai->prerest = i;
+		    if ((i = sfxlen(ai->firstm->str, s)) < ai->suflen)
+			ai->suflen = i;
+		}
+		t = s;
+		if (ppre)
+		    t = dyncat(ppre, t);
+		if (ipre && *ipre) {
+		    ai->noipre = 0;
+		    if (ai->icpl > lpl)
+			ai->icpl = lpl;
+		    if (ai->icsl > lsl)
+			ai->icsl = lsl;
+		    if (ai->iaprefix)
+			ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0';
+		    else
+			ai->iaprefix = dupstring(t);
+		    if (ai->iprefix) {
+			if (strcmp(ipre, ai->iprefix))
+			    ai->iprefix = "";
+		    } else
+			ai->iprefix = dupstring(ipre);
+
+		    t = dyncat(ipre, t);
+		} else
+		    ai->iprefix = "";
+		if (ai->cpl > lpl)
+		    ai->cpl = lpl;
+		if (ai->csl > lsl)
+		    ai->csl = lsl;
+		if (ai->aprefix)
+		    ai->aprefix[pfxlen(ai->aprefix, t)] = '\0';
+		else
+		    ai->aprefix = dupstring(t);
+		mnum++;
+		ai->count++;
+
+		cm = (Cmatch) halloc(sizeof(struct cmatch));
+		cm->ppre = ppre;
+		cm->psuf = psuf;
+		cm->prpre = prpre;
+		if (!quote)
+		    s = quotename(s, NULL, NULL, NULL);
+		cm->str = dupstring(s);
+		cm->ipre = cm->ripre = ipre;
+		cm->pre = pre;
+		cm->suf = suf;
+		cm->flags = flags;
+		cm->brpl = brpl;
+		cm->brsl = brsl;
+		addlinknode((alt ? fmatches : matches), cm);
+
+		if (expl)
+		    expl->fcount++;
+		if (!ai->firstm)
+		    ai->firstm = cm;
+	    }
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+}
 
 /* This adds a match to the list of matches.  The string to add is given   *
  * in s, the type of match is given in the global variable addwhat and     *
@@ -2351,7 +2527,7 @@ addmatch(char *s, char *t)
 {
     int test = 0, sl = strlen(s), pl = rpl, cc = 0, isf = 0;
     int mpl = 0, msl = 0, bpl = brpl, bsl = brsl;
-    char *e = NULL, *tt, *te, *fc, *ms = NULL;
+    char *e = NULL, *tt, *te, *ms = NULL;
     Comp cp = patcomp;
     HashNode hn;
     Param pm;
@@ -2377,6 +2553,8 @@ addmatch(char *s, char *t)
     hn = (HashNode) t;
     pm = (Param) t;
 
+    if (incompfunc)
+	s = dupstring(s);
     if (!addwhat) {
 	test = 1;
     } else if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
@@ -2427,11 +2605,8 @@ addmatch(char *s, char *t)
 	    }
 	}
 	if (test) {
-	    fc = NULL;
-	    if (addwhat == -7 && !(fc = findcmd(s)))
+	    if (addwhat == -7 && !findcmd(s, 0))
 		return;
-	    if (fc)
-		zsfree(fc);
 	    isf = CMF_FILE;
 
 	    if (addwhat == CC_FILES || addwhat == -6 ||
@@ -2515,7 +2690,6 @@ addmatch(char *s, char *t)
     }
     if (!test)
 	return;
-
     if (!ms && !ispattern && ai->firstm) {
 	if ((test = sl - pfxlen(ai->firstm->str, s)) < ai->prerest)
 	    ai->prerest = test;
@@ -2602,8 +2776,13 @@ addmatch(char *s, char *t)
     cm->str = (ms ? ms : s);
     cm->ipre = (ipre && *ipre ? ipre : NULL);
     cm->ripre = (ripre && *ripre ? ripre : NULL);
-    cm->pre = curcc->prefix;
-    cm->suf = curcc->suffix;
+    if (incompfunc) {
+	cm->pre = dupstring(curcc->prefix);
+	cm->suf = dupstring(curcc->suffix);
+    } else {
+	cm->pre = curcc->prefix;
+	cm->suf = curcc->suffix;
+    }
     cm->flags = mflags | isf;
     cm->brpl = bpl;
     cm->brsl = bsl;
@@ -2729,7 +2908,7 @@ maketildelist(void)
 /* This does the check for compctl -x `n' and `N' patterns. */
 
 /**/
-static int
+int
 getcpat(char *str, int cpatindex, char *cpat, int class)
 {
     char *s, *t, *p;
@@ -2951,6 +3130,8 @@ docompletion(char *s, int lst, int incmd)
 
 	    else if (nmatches == 1) {
 		/* Only one match. */
+		while (!amatches->mcount)
+		    amatches = amatches->next;
 		do_single(amatches->matches[0]);
 		invalidatelist();
 	    }
@@ -3051,7 +3232,98 @@ makecomplist(char *s, int incmd, int lst)
 	ccused = newlinklist();
 	ccstack = newlinklist();
 
-	makecomplistglobal(s, incmd, lst);
+	if (compfunc) {
+	    List list;
+	    int lv = lastval;
+
+	    if ((list = getshfunc(compfunc)) != &dummy_list) {
+		LinkList args = newlinklist();
+		char **p, *tmp;
+		int aadd = 0, usea = 1;
+
+		addlinknode(args, compfunc);
+
+		zsfree(compcontext);
+		zsfree(compcommand);
+		compcommand = "";
+		if (inwhat == IN_MATH) {
+		    if (insubscr) {
+			compcontext = "subscript";
+			compcommand = varname ? varname : "";
+		    } else
+			compcontext = "math";
+		    usea = 0;
+		} else if (lincmd)
+		    compcontext = (insubscr ? "subscript" : "command");
+		else if (linredir)
+		    compcontext = "redirect";
+		else
+		    switch (inwhat) {
+		    case IN_ENV:
+			compcontext = "value";
+			compcommand = varname;
+			usea = 0;
+			break;
+		    case IN_COND:
+			compcontext = "condition";
+			break;
+		    default:
+			if (cmdstr) {
+			    compcontext = "argument";
+			    compcommand = cmdstr;
+			} else {
+			    compcontext = "value";
+			    if (clwords[0])
+				compcommand = clwords[0];
+			}
+			aadd = 1;
+		    }
+		compcontext = ztrdup(compcontext);
+		tmp = quotename(compcommand, NULL, NULL, NULL);
+		untokenize(tmp);
+		compcommand = ztrdup(tmp);
+		if (usea && (!aadd || clwords[0]))
+		    for (p = clwords + aadd; *p; p++) {
+			tmp = dupstring(*p);
+			untokenize(tmp);
+			addlinknode(args, tmp);
+		    }
+		zsfree(compprefix);
+		zsfree(compsuffix);
+		if (unset(COMPLETEINWORD)) {
+		    tmp = quotename(s, NULL, NULL, NULL);
+		    untokenize(tmp);
+		    compprefix = ztrdup(tmp);
+		    compsuffix = ztrdup("");
+		} else {
+		    char *ss = s + offs, sav;
+
+		    tmp = quotename(s, &ss, NULL, NULL);
+		    sav = *ss;
+		    *ss = '\0';
+		    untokenize(tmp);
+		    compprefix = ztrdup(tmp);
+		    *ss = sav;
+		    untokenize(ss);
+		    compsuffix = ztrdup(ss);
+		}
+		zsfree(compiprefix);
+		compiprefix = ztrdup("");
+		compcurrent = (usea ? (clwpos + 1 - aadd) : 1);
+		compnmatches = mnum;
+		incompfunc = 1;
+		startparamscope();
+		makecompparamsptr();
+		NEWHEAPS(compheap) {
+		    doshfunc(compfunc, list, args, 0, 1);
+		} OLDHEAPS;
+		endparamscope();
+		lastcmd = 9;
+		incompfunc = 0;
+	    }
+	    lastval = lv;
+	} else
+	    makecomplistglobal(s, incmd, lst);
 
 	endcmgroup(NULL);
 
@@ -3082,6 +3354,83 @@ makecomplist(char *s, int incmd, int lst)
     return 1;
 }
 
+/* This should probably be moved into tokenize(). */
+
+static char *
+ctokenize(char *p)
+{
+    char *r = p;
+    int bslash = 0;
+
+    tokenize(p);
+
+    for (p = r; *p; p++) {
+	if (*p == '\\')
+	    bslash = 1;
+	else {
+	    if (*p == '$') {
+		if (bslash)
+		    p[-1] = Bnull;
+		else
+		    *p = String;
+	    }
+	    bslash = 0;
+	}
+    }
+    return r;
+}
+
+/**/
+char *
+comp_str(int *ipl, int *pl)
+{
+    char *p = dupstring(compprefix);
+    char *s = dupstring(compsuffix);
+    char *ip = dupstring(compiprefix);
+    char *str;
+    int lp, ls, lip;
+
+    ctokenize(p);
+    remnulargs(p);
+    ctokenize(s);
+    remnulargs(s);
+    ctokenize(ip);
+    remnulargs(ip);
+    ls = strlen(s);
+    lip = strlen(ip);
+    lp = strlen(p);
+    str = halloc(lip + lp + ls + 1);
+    strcpy(str, ip);
+    strcat(str, p);
+    strcat(str, s);
+
+    if (ipl)
+	*ipl = lip;
+    if (pl)
+	*pl = lp;
+
+    return str;
+}
+
+/**/
+void
+makecomplistcall(Compctl cc)
+{
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    int ooffs = offs, lip, lp;
+	    char *str = comp_str(&lip, &lp);
+
+	    offs = lip + lp;
+	    cc->refc++;
+	    ccont = 0;
+	    makecomplistor(cc, str, lincmd, lip, 0);
+	    offs = ooffs;
+	    compnmatches = mnum;
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+}
+
 /* This function gets the compctls for the given command line and *
  * adds all completions for them. */
 
@@ -3159,7 +3508,7 @@ makecomplistcmd(char *os, int incmd)
     /* If the command string starts with `=', try the path name of the *
      * command. */
     if (cmdstr && cmdstr[0] == Equals) {
-	char *c = findcmd(cmdstr + 1);
+	char *c = findcmd(cmdstr + 1, 1);
 
 	if (c) {
 	    zsfree(cmdstr);
@@ -3191,7 +3540,7 @@ makecomplistpc(char *os, int incmd)
 {
     Patcomp pc;
     Comp pat;
-    char *s = findcmd(cmdstr);
+    char *s = findcmd(cmdstr, 1);
 
     for (pc = patcomps; pc; pc = pc->next) {
 	if ((pat = parsereg(pc->pat)) &&
@@ -3468,12 +3817,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
     ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
 
-    if (findnode(ccstack, cc))
+    if (!incompfunc && findnode(ccstack, cc))
 	return;
 
     addlinknode(ccstack, cc);
 
-    if (allccs) {
+    if (!incompfunc && allccs) {
 	if (findnode(allccs, cc)) {
 	    uremnode(ccstack, firstnode(ccstack));
 	    return;
@@ -4107,7 +4456,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    }
 
 	    /* This flag allows us to use read -l and -c. */
-	    incompctlfunc = 1;
+	    if (!incompfunc)
+		incompctlfunc = 1;
 	    sfcontext = SFC_COMPLETE;
 	    /* Call the function. */
 	    doshfunc(cc->func, list, args, 0, 1);
@@ -4126,7 +4476,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	char *j, *jj;
 
 	for (i = 0; i < MAXJOB; i++)
-	    if (jobtab[i].stat & STAT_INUSE) {
+	    if ((jobtab[i].stat & STAT_INUSE) &&
+		jobtab[i].procs && jobtab[i].procs->text) {
 		int stopped = jobtab[i].stat & STAT_STOPPED;
 
 		j = jj = dupstring(jobtab[i].procs->text);
@@ -4274,7 +4625,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    }
 
 	    /* No harm in allowing read -l and -c here, too */
-	    incompctlfunc = 1;
+	    if (!incompfunc)
+		incompctlfunc = 1;
 	    sfcontext = SFC_COMPLETE;
 	    doshfunc(cc->ylist, list, args, 0, 1);
 	    sfcontext = osc;
@@ -4528,10 +4880,12 @@ makearray(LinkList l, int s, int *np, int *nlp)
 	    for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--);
 	    ap = bp;
 	    /* Mark those, that would show the same string in the list. */
-	    for (; bp[1] && !strcmp((*ap)->str, (bp[1])->str); bp++) {
-		(bp[1])->flags |= CMF_NOLIST; nl++;
-	    }
+	    for (; bp[1] && !strcmp((*ap)->str, (bp[1])->str); bp++)
+		(bp[1])->flags |= CMF_NOLIST;
 	}
+	for (ap = rp; *ap; ap++)
+	    if ((*ap)->flags & CMF_NOLIST)
+		nl++;
 	*cp = NULL;
     }
     if (np)
@@ -4991,10 +5345,14 @@ do_single(Cmatch m)
     if (m->suf) {
 	havesuff = 1;
 	menuinsc = ztrlen(m->suf);
-	if (menuwe && (m->flags & CMF_REMOVE)) {
-	    makesuffix(menuinsc);
-	    if (menuinsc == 1)
-		suffixlen[m->suf[0]] = 1;
+	menulen -= menuinsc;
+	if (menuwe) {
+	    menuend += menuinsc;
+	    if (m->flags & CMF_REMOVE) {
+		makesuffix(menuinsc);
+		if (menuinsc == 1)
+		    suffixlen[STOUC(m->suf[0])] = 1;
+	    }
 	}
     } else {
 	/* There is no user-specified suffix, *
@@ -5463,7 +5821,8 @@ listmatches(void)
 		}
 		if (n) {
 		    putc('\n', shout);
-		    p = skipnolist(p + 1);
+		    if (n && nl)
+			p = skipnolist(p + 1);
 		}
 	    }
 	}
@@ -5671,7 +6030,7 @@ expandcmdpath(void)
 	feep();
 	return;
     }
-    str = findcmd(s);
+    str = findcmd(s, 1);
     zsfree(s);
     if (!str) {
 	feep();
@@ -5686,7 +6045,6 @@ expandcmdpath(void)
 	cs += cmdwe - cmdwb + strlen(str);
     if (cs > ll)
 	cs = ll;
-    zsfree(str);
 }
 
 /* Extra function added by AR Iano-Fletcher. */
diff --git a/Src/builtin.c b/Src/builtin.c
index 6c41ce2fd..f6941286d 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -680,7 +680,7 @@ bin_cd(char *nam, char **argv, char *ops, int func)
 		goto brk;
 	    }
 	} while (*++s);
-	for (s = *argv; *++s; ops[*s] = 1);
+	for (s = *argv; *++s; ops[STOUC(*s)] = 1);
     }
   brk:
     chaselinks = ops['P'] || (isset(CHASELINKS) && !ops['L']);
@@ -790,7 +790,7 @@ cd_get_dest(char *nam, char **argv, char *ops, int func)
 	    zsfree(remnode(dirstack, dir));
 	return NULL;
     }
-    if (dest != getdata(dir)) {
+    if (dest != (char *)getdata(dir)) {
 	zsfree(getdata(dir));
 	setdata(dir, dest);
     }
@@ -1224,13 +1224,14 @@ bin_fc(char *nam, char **argv, char *ops, int func)
 		if (!editor)
 		    editor = DEFAULT_FCEDIT;
 
-		if (fcedit(editor, fil))
+		if (fcedit(editor, fil)) {
 		    if (stuff(fil))
 			zwarnnam("fc", "%e: %s", s, errno);
 		    else {
 			loop(0,1);
 			retval = lastval;
 		    }
+		}
 	    }
 	}
 	unlink(fil);
@@ -1464,6 +1465,117 @@ getasg(char *s)
     return &asg;
 }
 
+/* function to set a single parameter */
+
+/**/
+int
+typeset_single(char *cname, char *pname, Param pm, int func,
+	       int on, int off, int roff, char *value)
+{
+    int usepm, tc;
+
+    /* use the existing pm? */
+    usepm = pm && !(pm->flags & PM_UNSET);
+
+    /* Always use an existing pm if special at current locallevel */
+    if (pm && (pm->flags & PM_SPECIAL) && pm->level == locallevel)
+	usepm = 1;
+
+    /*
+     * Don't use a non-special existing param if
+     *   - the local level has changed, and
+     *   - the function is not `export'.
+     */
+    if (usepm && !(pm->flags & PM_SPECIAL) &&
+	locallevel != pm->level && func != BIN_EXPORT)
+	usepm = 0;
+
+    /* attempting a type conversion? */
+    if ((tc = usepm && (((off & pm->flags) | (on & ~pm->flags)) &
+			(PM_INTEGER|PM_HASHED|PM_ARRAY))))
+	usepm = 0;
+    if (tc && (pm->flags & PM_SPECIAL)) {
+	zerrnam(cname, "%s: can't change type of a special parameter",
+		pname, 0);
+	return 1;
+    }
+
+    if (usepm) {
+	if (!on && !roff && !value) {
+	    paramtab->printnode((HashNode)pm, 0);
+	    return 0;
+	}
+	if ((pm->flags & PM_RESTRICTED && isset(RESTRICTED))) {
+	    zerrnam(cname, "%s: restricted", pname, 0);
+	    return 1;
+	}
+	if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
+	    !(pm->flags & PM_READONLY & ~off))
+	    uniqarray((*pm->gets.afn) (pm));
+	pm->flags = (pm->flags | on) & ~off;
+	/* This auxlen/pm->ct stuff is a nasty hack. */
+	if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) &&
+	    auxlen)
+	    pm->ct = auxlen;
+	if (!(pm->flags & (PM_ARRAY|PM_HASHED))) {
+	    if (pm->flags & PM_EXPORTED) {
+		if (!(pm->flags & PM_UNSET) && !pm->env && !value)
+		    pm->env = addenv(pname, getsparam(pname));
+	    } else if (pm->env) {
+		delenv(pm->env);
+		zsfree(pm->env);
+		pm->env = NULL;
+	    }
+	    if (value)
+		setsparam(pname, ztrdup(value));
+	} else if (value) {
+	    zwarnnam(cname, "can't assign new value for array %s", pname, 0);
+	    return 1;
+	}
+	return 0;
+    }
+
+    /*
+     * We're here either because we're creating a new parameter,
+     * or we're adding a parameter at a different local level,
+     * or we're converting the type of a parameter.  In the
+     * last case only, we need to delete the old parameter.
+     */
+    if (tc) {
+	if (pm->flags & PM_READONLY) {
+	    on |= ~off & PM_READONLY;
+	    pm->flags &= ~PM_READONLY;
+	}
+	/*
+	 * Try to carry over a value, but not when changing from,
+	 * to, or between non-scalar types.
+	 */
+	if (!value && !((pm->flags|on) & (PM_ARRAY|PM_HASHED)))
+	    value = dupstring(getsparam(pname));
+	/* pname may point to pm->nam which is about to disappear */
+	pname = dupstring(pname);
+	unsetparam_pm(pm, 0, 1);
+    }
+    /*
+     * Create a new node for a parameter with the flags in `on' minus the
+     * readonly flag
+     */
+    pm = createparam(pname, on & ~PM_READONLY);
+    DPUTS(!pm, "BUG: parameter not created");
+    pm->ct = auxlen;
+    if (func != BIN_EXPORT)
+	pm->level = locallevel;
+    if (value && !(pm->flags & (PM_ARRAY|PM_HASHED)))
+	setsparam(pname, ztrdup(value));
+    pm->flags |= (on & PM_READONLY);
+    if (value && (pm->flags & (PM_ARRAY|PM_HASHED))) {
+	zerrnam(cname, "%s: can't assign initial value for array", pname, 0);
+	return 1;
+    }
+
+    return 0;
+}
+
 /* declare, export, integer, local, readonly, typeset */
 
 /**/
@@ -1475,7 +1587,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     Comp com;
     char *optstr = "aiALRZlurtxU";
     int on = 0, off = 0, roff, bit = PM_ARRAY;
-    int initon, initoff, of, i;
+    int i;
     int returnval = 0, printflags = 0;
 
     /* hash -f is really the builtin `functions' */
@@ -1486,9 +1598,9 @@ bin_typeset(char *name, char **argv, char *ops, int func)
      * Unfortunately, this depends on the order *
      * these flags are defined in zsh.h         */
     for (; *optstr; optstr++, bit <<= 1)
-	if (ops[*optstr] == 1)
+	if (ops[STOUC(*optstr)] == 1)
 	    on |= bit;
-	else if (ops[*optstr] == 2)
+	else if (ops[STOUC(*optstr)] == 2)
 	    off |= bit;
     roff = off;
 
@@ -1521,7 +1633,11 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 
     /* With the -m option, treat arguments as glob patterns */
     if (ops['m']) {
+	MUSTUSEHEAP("typeset -m");
 	while ((asg = getasg(*argv++))) {
+	    LinkList pmlist = newlinklist();
+	    LinkNode pmnode;
+
 	    tokenize(asg->name);   /* expand argument */
 	    if (!(com = parsereg(asg->name))) {
 		untokenize(asg->name);
@@ -1529,143 +1645,45 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 		returnval = 1;
 		continue;
 	    }
-	    /* If no options or values are given, display all *
-	     * parameters matching the glob pattern.          */
-	    if (!(on || roff || asg->value)) {
-		scanmatchtable(paramtab, com, 0, 0, paramtab->printnode, 0);
-		continue;
-	    }
-	    /* Since either options or values are given, we search   *
-	     * through the parameter table and change all parameters *
-	     * matching the glob pattern to have these flags and/or  *
-	     * value.                                                */
+	    /*
+	     * Search through the parameter table and change all parameters
+	     * matching the glob pattern to have these flags and/or value.
+	     * Bad news:  if the parameter gets altered, e.g. by
+	     * a type conversion, then paramtab can be shifted around,
+	     * so we need to store the parameters to alter on a separate
+	     * list for later use.	     
+	     */
 	    for (i = 0; i < paramtab->hsize; i++) {
-		for (pm = (Param) paramtab->nodes[i]; pm; pm = (Param) pm->next) {
+		for (pm = (Param) paramtab->nodes[i]; pm;
+		     pm = (Param) pm->next) {
 		    if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED))
 			continue;
-		    if (domatch(pm->nam, com, 0)) {
-			/* set up flags if we have any */
-			if (on || roff) {
-			    if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
-				!(pm->flags & PM_READONLY & ~off))
-				uniqarray((*pm->gets.afn) (pm));
-			    if ((on & ~pm->flags) & PM_HASHED) {
-				char *nam = ztrdup(pm->nam);
-				unsetparam(nam);
-				pm = createparam(nam, on & ~PM_READONLY);
-				DPUTS(!pm, "BUG: parameter not created");
-			    }
-			    pm->flags = (pm->flags | on) & ~off;
-			    if (PM_TYPE(pm->flags) != PM_ARRAY &&
-				PM_TYPE(pm->flags) != PM_HASHED) {
-				if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && auxlen)
-				    pm->ct = auxlen;
-				/* did we just export this? */
-				if ((pm->flags & PM_EXPORTED) && !pm->env) {
-				    pm->env = addenv(pm->nam, (asg->value) ? asg->value : getsparam(pm->nam));
-				} else if (!(pm->flags & PM_EXPORTED) && pm->env) {
-				/* did we just unexport this? */
-				    delenv(pm->env);
-				    zsfree(pm->env);
-				    pm->env = NULL;
-				}
-			    }
-			}
-			/* set up a new value if given */
-			if (asg->value) {
-			    setsparam(pm->nam, ztrdup(asg->value));
-			}
-		    }
+		    if (domatch(pm->nam, com, 0))
+			addlinknode(pmlist, pm);
 		}
 	    }
+	    for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
+		pm = (Param) getdata(pmnode);
+		if (typeset_single(name, pm->nam, pm, func, on, off, roff,
+				   asg->value))
+		    returnval = 1;
+	    }
 	}
 	return returnval;
     }
 
-    /* Save the values of on, off, and func */
-    initon = on;
-    initoff = off;
-    of = func;
-
     /* Take arguments literally.  Don't glob */
     while ((asg = getasg(*argv++))) {
-	/* restore the original values of on, off, and func */
-	on = initon;
-	off = initoff;
-	func = of;
-	on &= ~PM_ARRAY;
-
 	/* check if argument is a valid identifier */
 	if (!isident(asg->name)) {
 	    zerr("not an identifier: %s", asg->name, 0);
 	    returnval = 1;
 	    continue;
 	}
-	bit = 0;    /* flag for switching int<->not-int */
-	if ((pm = (Param)paramtab->getnode(paramtab, asg->name)) &&
-	    (((pm->flags & PM_SPECIAL) && pm->level == locallevel) ||
-	     (!(pm->flags & PM_UNSET) &&
-	      ((locallevel == pm->level) || func == BIN_EXPORT) &&
-	      !(bit = (((off & pm->flags) | (on & ~pm->flags)) &
-		       (PM_INTEGER|PM_HASHED)))))) {
-	    /* if no flags or values are given, just print this parameter */
-	    if (!on && !roff && !asg->value) {
-		paramtab->printnode((HashNode) pm, 0);
-		continue;
-	    }
-	    if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
-		zerrnam(name, "%s: restricted", pm->nam, 0);
-		returnval = 1;
-		continue;
-	    }
-	    if((pm->flags & PM_SPECIAL) &&
-	       PM_TYPE((pm->flags | on) & ~off) != PM_TYPE(pm->flags)) {
-		zerrnam(name, "%s: cannot change type of a special parameter",
-		    pm->nam, 0);
-		returnval = 1;
-		continue;
-	    }
-	    if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
-		!(pm->flags & PM_READONLY & ~off))
-		uniqarray((*pm->gets.afn) (pm));
-	    pm->flags = (pm->flags | on) & ~off;
-	    if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) &&
-		auxlen)
-		pm->ct = auxlen;
-	    if (PM_TYPE(pm->flags) != PM_ARRAY &&
-		PM_TYPE(pm->flags) != PM_HASHED) {
-		if (pm->flags & PM_EXPORTED) {
-		    if (!(pm->flags & PM_UNSET) && !pm->env && !asg->value)
-			pm->env = addenv(asg->name, getsparam(asg->name));
-		} else if (pm->env) {
-		    delenv(pm->env);
-		    zsfree(pm->env);
-		    pm->env = NULL;
-		}
-		if (asg->value)
-		    setsparam(asg->name, ztrdup(asg->value));
-	    }
-	} else {
-	    if (bit) {
-		if (pm->flags & PM_READONLY) {
-		    on |= ~off & PM_READONLY;
-		    pm->flags &= ~PM_READONLY;
-		}
-		if (!asg->value)
-		    asg->value = dupstring(getsparam(asg->name));
-		unsetparam(asg->name);
-	    }
-	    /* create a new node for a parameter with the *
-	     * flags in `on' minus the readonly flag      */
-	    pm = createparam(ztrdup(asg->name), on & ~PM_READONLY);
-	    DPUTS(!pm, "BUG: parameter not created");
-	    pm->ct = auxlen;
-	    if (func != BIN_EXPORT)
-		pm->level = locallevel;
-	    if (asg->value)
-		setsparam(asg->name, ztrdup(asg->value));
-	    pm->flags |= (on & PM_READONLY);
-	}
+	if (typeset_single(name, asg->name,
+			   (Param)paramtab->getnode(paramtab, asg->name),
+			   func, on, off, roff, asg->value))
+	    returnval = 1;
     }
     return returnval;
 }
@@ -1989,7 +2007,7 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 		puts(wd ? ": none" : " not found");
 		returnval = 1;
 	    }
-	} else if ((cnam = findcmd(*argv))) {
+	} else if ((cnam = findcmd(*argv, 1))) {
 	    /* Found external command. */
 	    if (wd) {
 		printf("%s: command\n", *argv);
@@ -2001,7 +2019,6 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 		    print_if_link(cnam);
 		fputc('\n', stdout);
 	    }
-	    zsfree(cnam);
 	} else {
 	    /* Not found at all. */
 	    if (v || csh || wd)
@@ -2328,9 +2345,17 @@ bin_print(char *name, char **args, char *ops, int func)
 	    args[n] = getkeystring(args[n], &len[n],
 				    func != BIN_ECHO && !ops['e'], &nnl);
 	/* -P option -- interpret as a prompt sequence */
-	if(ops['P'])
-	    args[n] = unmetafy(promptexpand(metafy(args[n], len[n],
-		META_NOALLOC), 0, NULL, NULL), &len[n]);
+	if(ops['P']) {
+	    /*
+	     * promptexpand uses permanent storage: to avoid
+	     * messy memory management, stick it on the heap
+	     * instead.
+	     */
+	    char *str = unmetafy(promptexpand(metafy(args[n], len[n],
+				   META_NOALLOC), 0, NULL, NULL), &len[n]);
+	    args[n] = dupstring(str);
+	    free(str);
+	}
 	/* -D option -- interpret as a directory, and use ~ */
 	if(ops['D']) {
 	    Nameddir d = finddir(args[n]);
@@ -2778,8 +2803,9 @@ zexit(int val, int from_signal)
 		LASTALLOC_RETURN;
 	    }
 	}
-	if (in_exit++ && from_signal)
+	if (in_exit++ && from_signal) {
 	    LASTALLOC_RETURN;
+	}
 	if (isset(MONITOR))
 	    /* send SIGHUP to any jobs left running  */
 	    killrunjobs(from_signal);
@@ -3181,13 +3207,14 @@ bin_read(char *name, char **args, char *ops, int func)
 	    }
 	    if (c == EOF || (c == '\n' && !zbuf))
 		break;
-	    if (!bslash && isep(c) && bptr == buf)
+	    if (!bslash && isep(c) && bptr == buf) {
 		if (iwsep(c))
 		    continue;
 		else if (!first) {
 		    first = 1;
 		    continue;
 		}
+	    }
 	    bslash = c == '\\' && !bslash && !ops['r'];
 	    if (bslash)
 		continue;
@@ -3240,7 +3267,7 @@ zread(void)
     char cc, retry = 0;
 
     /* use zbuf if possible */
-    if (zbuf)
+    if (zbuf) {
 	/* If zbuf points to anything, it points to the next character in the
 	buffer.  This may be a null byte to indicate EOF.  If reading from the
 	buffer, move on the buffer pointer. */
@@ -3248,6 +3275,7 @@ zread(void)
 	    return zbuf++, STOUC(*zbuf++ ^ 32);
 	else
 	    return (*zbuf) ? STOUC(*zbuf++) : EOF;
+    }
     for (;;) {
 	/* read a character from readfd */
 	switch (read(readfd, &cc, 1)) {
diff --git a/Src/cond.c b/Src/cond.c
index 906e81938..a46833f66 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -54,7 +54,7 @@ evalcond(Cond c)
 		    int l = arrlen((char **) c->right);
 
 		    if (l < cd->min || (cd->max >= 0 && l > cd->max)) {
-			zerr("unrecognized condition: `-%s'", (char *) c->left, 0);
+			zerr("unrecognized condition: `%s'", (char *) c->left, 0);
 			return 0;
 		    }
 		}
@@ -68,13 +68,13 @@ evalcond(Cond c)
 		    int l = arrlen(a);
 
 		    if (l < cd->min || (cd->max >= 0 && l > cd->max)) {
-			zerr("unrecognized condition: `-%s'", (char *) c->left, 0);
+			zerr("unrecognized condition: `%s'", (char *) c->left, 0);
 			return 0;
 		    }
 		    a[0] = (char *) c->left;
 		    return cd->handler(a, cd->condid);
 		} else
-		    zerr("unrecognized condition: `-%s'", (char *) c->left, 0);
+		    zerr("unrecognized condition: `%s'", (char *) c->left, 0);
 	    }
 	    return 0;
 	}
diff --git a/Src/exec.c b/Src/exec.c
index 911559a02..e563db6a4 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -457,13 +457,17 @@ execute(Cmdnam not_used_yet, int dash)
     _exit(1);
 }
 
-#define try(X) { if (iscom(X)) return ztrdup(X); }
+#define RET_IF_COM(X) { if (iscom(X)) return docopy ? dupstring(X) : arg0; }
 
-/* get the full pathname of an external command */
+/*
+ * Get the full pathname of an external command.
+ * If the second argument is zero, return the first argument if found;
+ * if non-zero, return the path using heap memory.  (RET_IF_COM(X), above).
+ */
 
 /**/
 char *
-findcmd(char *arg0)
+findcmd(char *arg0, int docopy)
 {
     char **pp;
     char *z, *s, buf[MAXCMDLEN];
@@ -476,7 +480,7 @@ findcmd(char *arg0)
 	return NULL;
     for (s = arg0; *s; s++)
 	if (*s == '/') {
-	    try(arg0);
+	    RET_IF_COM(arg0);
 	    if (arg0 == s || unset(PATHDIRS)) {
 		return NULL;
 	    }
@@ -496,13 +500,13 @@ findcmd(char *arg0)
 			*z++ = '/';
 		    }
 		    strcpy(z, arg0);
-		    try(buf);
+		    RET_IF_COM(buf);
 		}
 	    strcpy(nn, cn->u.name ? *(cn->u.name) : "");
 	    strcat(nn, "/");
 	    strcat(nn, cn->nam);
 	}
-	try(nn);
+	RET_IF_COM(nn);
     }
     for (pp = path; *pp; pp++) {
 	z = buf;
@@ -511,7 +515,7 @@ findcmd(char *arg0)
 	    *z++ = '/';
 	}
 	strcpy(z, arg0);
-	try(buf);
+	RET_IF_COM(buf);
     }
     return NULL;
 }
diff --git a/Src/glob.c b/Src/glob.c
index 9c1d08aa4..6536e0f06 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -2318,7 +2318,7 @@ doesmatch(Comp c)
 		    for (; *pptr; pptr++) {
 			if (*pptr == Meta)
 			    pptr++;
-			else if (CHARMATCH(*pptr, looka))
+			else if (CHARMATCH(STOUC(*pptr), STOUC(looka)))
 			    break;
 		    }
 		    if (!*(saves = pptr))
@@ -2688,7 +2688,7 @@ matchonce(Comp c)
 	    }
 	    continue;
 	}
-	if (CHARMATCH(*pptr, *pat)) {
+	if (CHARMATCH(STOUC(*pptr), STOUC(*pat))) {
 	    /* just plain old characters */
 	    pptr++;
 	    pat++;
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 60fb3df80..b816f1892 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -172,7 +172,7 @@ addhashnode(HashTable ht, char *nam, void *nodeptr)
 	ht->nodes[hashval] = hn;
 	replacing:
 	hn->next = hp->next;
-	if(ht->scan)
+	if(ht->scan) {
 	    if(ht->scan->sorted) {
 		HashNode *tab = ht->scan->u.s.tab;
 		int i;
@@ -181,6 +181,7 @@ addhashnode(HashTable ht, char *nam, void *nodeptr)
 			tab[i] = hn;
 	    } else if(ht->scan->u.u == hp)
 		ht->scan->u.u = hn;
+	}
 	ht->freenode(hp);
 	return;
     }
@@ -270,7 +271,7 @@ removehashnode(HashTable ht, char *nam)
 	ht->nodes[hashval] = hp->next;
 	gotit:
 	ht->ct--;
-	if(ht->scan)
+	if(ht->scan) {
 	    if(ht->scan->sorted) {
 		HashNode *tab = ht->scan->u.s.tab;
 		int i;
@@ -279,6 +280,7 @@ removehashnode(HashTable ht, char *nam)
 			tab[i] = NULL;
 	    } else if(ht->scan->u.u == hp)
 		ht->scan->u.u = hp->next;
+	}
 	return hp;
     }
 
diff --git a/Src/hist.c b/Src/hist.c
index a4c5735c1..d77b2febd 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -392,7 +392,7 @@ histsubchar(int c)
 		c = ingetc();
 	    }
 	    *ptr = 0;
-	    if (!*buf)
+	    if (!*buf) {
 		if (c != '%') {
 		    if (isset(CSHJUNKIEHISTORY))
 			ev = curhist - 1;
@@ -408,6 +408,7 @@ histsubchar(int c)
 		    else
 			ev = defev;
 		    evset = 0;
+		}
 	    } else if ((t0 = atoi(buf))) {
 		ev = (t0 < 0) ? curhist + t0 : t0;
 		evset = 1;
@@ -749,11 +750,12 @@ hend(void)
 	save = 0;
     else {
 	*hptr = '\0';
-	if (hptr[-1] == '\n')
+	if (hptr[-1] == '\n') {
 	    if (chline[1]) {
 		*--hptr = '\0';
 	    } else
 		save = 0;
+	}
 	if (!*chline || !strcmp(chline, "\n") ||
 	    (isset(HISTIGNORESPACE) && spaceflag))
 	    save = 0;
diff --git a/Src/input.c b/Src/input.c
index 45c65b2bf..66a21000c 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -219,7 +219,7 @@ inputline(void)
     char *ingetcline, *ingetcpmptl = NULL, *ingetcpmptr = NULL;
 
     /* If reading code interactively, work out the prompts. */
-    if (interact && isset(SHINSTDIN))
+    if (interact && isset(SHINSTDIN)) {
 	if (!isfirstln)
 	    ingetcpmptl = prompt2;
 	else {
@@ -227,6 +227,7 @@ inputline(void)
 	    if (rprompt)
 		ingetcpmptr = rprompt;
 	}
+    }
     if (!(interact && isset(SHINSTDIN) && SHTTY != -1 && isset(USEZLE))) {
 	/*
 	 * If not using zle, read the line straight from the input file.
diff --git a/Src/jobs.c b/Src/jobs.c
index 74fa8cda0..e94dd77e2 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -462,7 +462,7 @@ printjob(Job jn, int lng, int synch)
 	if (jn->stat & STAT_SUPERJOB &&
 	    jn->procs->status == SP_RUNNING && !pn->next)
 	    pn->status = SP_RUNNING;
-	if (pn->status != SP_RUNNING)
+	if (pn->status != SP_RUNNING) {
 	    if (WIFSIGNALED(pn->status)) {
 		sig = WTERMSIG(pn->status);
 		llen = strlen(sigmsg[sig]);
@@ -483,6 +483,7 @@ printjob(Job jn, int lng, int synch)
 	    } else if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
 		       WEXITSTATUS(pn->status))
 		sflag = 1;
+	}
     }
 
 /* print if necessary */
@@ -508,7 +509,7 @@ printjob(Job jn, int lng, int synch)
 			break;
 		    len2 += strlen(qn->text) + 2;
 		}
-	    if (job != thisjob)
+	    if (job != thisjob) {
 		if (fline)
 		    fprintf(fout, "[%ld]  %c ",
 			    (long)(jn - jobtab),
@@ -516,7 +517,7 @@ printjob(Job jn, int lng, int synch)
 			    : (job == prevjob) ? '-' : ' ');
 		else
 		    fprintf(fout, (job > 9) ? "        " : "       ");
-	    else
+	    } else
 		fprintf(fout, "zsh: ");
 	    if (lng & 1)
 		fprintf(fout, "%ld ", (long) pn->pid);
@@ -531,18 +532,19 @@ printjob(Job jn, int lng, int synch)
 		lng &= ~3;
 	    } else
 		fprintf(fout, "%*s", skip, "");
-	    if (pn->status == SP_RUNNING)
+	    if (pn->status == SP_RUNNING) {
 		if (!conted)
 		    fprintf(fout, "running%*s", len - 7 + 2, "");
 		else
 		    fprintf(fout, "continued%*s", len - 9 + 2, "");
-	    else if (WIFEXITED(pn->status))
+	    }
+	    else if (WIFEXITED(pn->status)) {
 		if (WEXITSTATUS(pn->status))
 		    fprintf(fout, "exit %-4d%*s", WEXITSTATUS(pn->status),
 			    len - 9 + 2, "");
 		else
 		    fprintf(fout, "done%*s", len - 4 + 2, "");
-	    else if (WIFSTOPPED(pn->status))
+	    } else if (WIFSTOPPED(pn->status))
 		fprintf(fout, "%-*s", len + 2, sigmsg[WSTOPSIG(pn->status)]);
 	    else if (WCOREDUMP(pn->status))
 		fprintf(fout, "%s (core dumped)%*s",
@@ -1071,7 +1073,7 @@ bin_fg(char *name, char **argv, char *ops, int func)
         /* If you immediately type "exit" after "jobs", this      *
          * will prevent zexit from complaining about stopped jobs */
 	stopmsg = 2;
-    if (!*argv)
+    if (!*argv) {
 	/* This block handles all of the default cases (no arguments).  bg,
 	fg and disown act on the current job, and jobs and wait act on all the
 	jobs. */
@@ -1100,6 +1102,7 @@ bin_fg(char *name, char **argv, char *ops, int func)
 		    waitjob(job, SIGINT);
 	    return 0;
 	}
+    }
 
     /* Defaults have been handled.  We now have an argument or two, or three...
     In the default case for bg, fg and disown, the argument will be provided by
diff --git a/Src/lex.c b/Src/lex.c
index 6f4f2dd20..b08dfed5b 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -457,11 +457,11 @@ add(int c)
 
 #define SETPARBEGIN {if (zleparse && !(inbufflags & INP_ALIAS) && cs >= ll+1-inbufct) parbegin = inbufct;}
 #define SETPAREND {\
-	    if (zleparse && !(inbufflags & INP_ALIAS) && parbegin != -1 && parend == -1)\
+	    if (zleparse && !(inbufflags & INP_ALIAS) && parbegin != -1 && parend == -1) {\
 		if (cs >= ll + 1 - inbufct)\
 		    parbegin = -1;\
 		else\
-		    parend = inbufct;}
+		    parend = inbufct;} }
 
 static int
 cmd_or_math(int cs_type)
@@ -823,20 +823,22 @@ gettokstr(int c, int sub)
 	case LX2_OUTPAR:
 	    if ((sub || in_brace_param) && isset(SHGLOB))
 		break;
-	    if (!in_brace_param && !pct--)
+	    if (!in_brace_param && !pct--) {
 		if (sub) {
 		    pct = 0;
 		    break;
 		} else
 		    goto brk;
+	    }
 	    c = Outpar;
 	    break;
 	case LX2_BAR:
-	    if (!pct && !in_brace_param)
+	    if (!pct && !in_brace_param) {
 		if (sub)
 		    break;
 		else
 		    goto brk;
+	    }
 	    if (unset(SHGLOB) || (!sub && !in_brace_param))
 		c = Bar;
 	    break;
@@ -912,8 +914,9 @@ gettokstr(int c, int sub)
 		    *bptr = '\0';
 		    return STRING;
 		}
-		if (in_brace_param)
+		if (in_brace_param) {
 		    cmdpush(CS_BRACE);
+		}
 		bct++;
 	    }
 	    break;
@@ -922,8 +925,9 @@ gettokstr(int c, int sub)
 		break;
 	    if (!bct)
 		break;
-	    if (in_brace_param)
+	    if (in_brace_param) {
 		cmdpop();
+	    }
 	    if (bct-- == in_brace_param)
 		in_brace_param = 0;
 	    c = Outbrace;
@@ -933,11 +937,12 @@ gettokstr(int c, int sub)
 		c = Comma;
 	    break;
 	case LX2_OUTANG:
-	    if (!intpos)
+	    if (!intpos) {
 		if (in_brace_param || sub)
 		    break;
 		else
 		    goto brk;
+	    }
 	    e = hgetc();
 	    if (e != '(') {
 		hungetc(e);
@@ -1101,11 +1106,12 @@ gettokstr(int c, int sub)
 			break;
 		    }
 		    add(c);
-		    if (c == '\'')
+		    if (c == '\'') {
 			if ((inquote = !inquote))
 			    STOPHIST
 			else
 			    ALLOWHIST
+		    }
 		}
 	    if (inquote)
 		ALLOWHIST
@@ -1260,8 +1266,9 @@ dquote_parse(char endchar, int sub)
     }
     if (intick == 2)
 	ALLOWHIST
-    if (intick)
+    if (intick) {
 	cmdpop();
+    }
     while (bct--)
 	cmdpop();
     if (lexstop)
diff --git a/Src/loop.c b/Src/loop.c
index 5fbf2b841..95ec4832a 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -52,7 +52,7 @@ execfor(Cmd cmd)
     List list;
     Forcmd node;
     char *str;
-    int val;
+    int val = 0;
     LinkList args;
 
     node = cmd->u.forcmd;
diff --git a/Src/mem.c b/Src/mem.c
index 32822ab8c..703920215 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -129,25 +129,51 @@ global_permalloc(void)
     return luh;
 }
 
-/* heappush saves the current heap state using this structure */
+/* list of zsh heap */
 
-struct heapstack {
-    struct heapstack *next;	/* next one in list for this heap */
-    size_t used;
-};
+static Heap heaps;
 
-/* A zsh heap. */
+/* Use new heaps from now on. This returns the old heap-list. */
 
-struct heap {
-    struct heap *next;		/* next one                                  */
-    size_t used;		/* bytes used from the heap                  */
-    struct heapstack *sp;	/* used by pushheap() to save the value used */
-#define arena(X)	((char *) (X) + sizeof(struct heap))
-};
+/**/
+Heap
+new_heaps(void)
+{
+    Heap h = heaps;
 
-/* list of zsh heap */
+    heaps = NULL;
 
-static Heap heaps;
+    return h;
+}
+
+/* Re-install the old heaps again, freeing the new ones. */
+
+/**/
+void
+old_heaps(Heap old)
+{
+    Heap h, n;
+
+    for (h = heaps; h; h = n) {
+	n = h->next;
+	DPUTS(h->sp, "BUG: old_heaps() with pushed heaps");
+	zfree(h, sizeof(*h));
+    }
+    heaps = old;
+}
+
+/* Temporarily switch to other heaps (or back again). */
+
+/**/
+Heap
+switch_heaps(Heap new)
+{
+    Heap h = heaps;
+
+    heaps = new;
+
+    return h;
+}
 
 /* save states of zsh heaps */
 
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index 2f988ec8f..efaa803ad 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -24,11 +24,17 @@ for x_mod in $x_mods; do
 	*" $x_mod "*) ;;
 	*)  echo "/* non-linked-in known module \`$x_mod' */"
 	    eval "loc=\$loc_$x_mod"
-	    unset moddeps autobins
+	    unset moddeps autobins autoinfixconds autoprefixconds
 	    . $srcdir/../$loc/${x_mod}.mdd
 	    for bin in $autobins; do
 		echo "    add_autobin(\"$bin\", \"$x_mod\");"
 	    done
+	    for cond in $autoinfixconds; do
+		echo "    add_autocond(\"$cond\", 1, \"$x_mod\");"
+	    done
+	    for cond in $autoprefixconds; do
+		echo "    add_autocond(\"$cond\", 0, \"$x_mod\");"
+	    done
 	    for dep in $moddeps; do
 		case $bin_mods in
 		    *" $dep "*)
diff --git a/Src/mkmakemod.sh b/Src/mkmakemod.sh
index 9c90b75e5..2f79acbc3 100644
--- a/Src/mkmakemod.sh
+++ b/Src/mkmakemod.sh
@@ -17,15 +17,18 @@
 # defines one module.  The .mdd file is actually a shell script, which will
 # be sourced.  It may define the following shell variables:
 #
-#   moddeps       modules on which this module depends (default none)
-#   nozshdep      non-empty indicates no dependence on the `zsh' pseudo-module
-#   alwayslink    if non-empty, always link the module into the executable
-#   autobins      builtins defined by the module, for autoloading
-#   objects       .o files making up this module (*must* be defined)
-#   proto         .pro files for this module (default generated from $objects)
-#   headers       extra headers for this module (default none)
-#   hdrdeps       extra headers on which the .mdh depends (default none)
-#   otherincs     extra headers that are included indirectly (default none)
+#   moddeps         modules on which this module depends (default none)
+#   nozshdep        non-empty indicates no dependence on the `zsh' pseudo-module
+#   alwayslink      if non-empty, always link the module into the executable
+#   autobins        builtins defined by the module, for autoloading
+#   autoinfixconds  infix condition codes defined by the module, for
+#                   autoloading (without the leading `-')
+#   autoprefixconds like autoinfixconds, but for prefix condition codes
+#   objects         .o files making up this module (*must* be defined)
+#   proto           .pro files for this module (default generated from $objects)
+#   headers         extra headers for this module (default none)
+#   hdrdeps         extra headers on which the .mdh depends (default none)
+#   otherincs       extra headers that are included indirectly (default none)
 #
 # The .mdd file may also include a Makefile.in fragment between lines
 # `:<<\Make' and `Make' -- this will be copied into Makemod.in.
@@ -167,7 +170,7 @@ if $first_stage; then
     for module in $here_modules; do
 
 	unset moddeps nozshdep alwayslink hasexport
-	unset autobins
+	unset autobins autoinfixconds autoprefixconds
 	unset objects proto headers hdrdeps otherincs
 	. $top_srcdir/$the_subdir/${module}.mdd
 	test -n "${moddeps+set}" || moddeps=
diff --git a/Src/module.c b/Src/module.c
index 5780eb134..f91650a7f 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -227,7 +227,6 @@ deletewrapper(Module m, FuncWrap w)
 
 static char *dlerrstr[256];
 
-/**/
 static void *
 load_and_bind(const char *fn)
 {
@@ -277,7 +276,6 @@ load_and_bind(const char *fn)
 # define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
 # define dlclose(handle) shl_unload((shl_t)(handle))
 
-/**/
 static
 void *
 hpux_dlsym(void *handle, char *name)
diff --git a/Src/params.c b/Src/params.c
index 89a1ed6a1..3dc7e1a1b 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -765,7 +765,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 	} else if (rev) {
 	    v->isarr |= SCANPM_WANTVALS;
 	}
-	if (!down)
+	if (!down && PM_TYPE(v->pm->flags) == PM_HASHED)
 	    v->isarr &= ~SCANPM_MATCHMANY;
 	*inv = ind;
     }
@@ -1534,6 +1534,12 @@ setaparam(char *s, char **val)
 	if (!(v = getvalue(&s, 1)))
 	    createparam(t, PM_ARRAY);
 	*ss = '[';
+	if (PM_TYPE(v->pm->flags) == PM_HASHED) {
+	    zerr("attempt to set slice of associative array", NULL, 0);
+	    freearray(val);
+	    errflag = 1;
+	    return NULL;
+	}
 	v = NULL;
     } else {
 	if (!(v = getvalue(&s, 1)))
@@ -1571,13 +1577,13 @@ sethparam(char *s, char **val)
     }
     if (strchr(s, '[')) {
 	freearray(val);
-	zerr("attempt to set slice of associative array", NULL, 0);
+	zerr("nested associative arrays not yet supported", NULL, 0);
 	errflag = 1;
 	return NULL;
     } else {
 	if (!(v = getvalue(&s, 1)))
 	    createparam(t, PM_HASHED);
-	else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) &&
+	else if (!(PM_TYPE(v->pm->flags) & PM_HASHED) &&
 		 !(v->pm->flags & PM_SPECIAL)) {
 	    unsetparam(t);
 	    createparam(t, PM_HASHED);
diff --git a/Src/subst.c b/Src/subst.c
index cc7e87d3d..5211ee0ee 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -387,11 +387,11 @@ filesubstr(char **namptr, int assign)
 	for (pp = str + 1; !isend2(*pp); pp++);
 	sav = *pp;
 	*pp = 0;
-	if (!(cnam = findcmd(str + 1))) {
+	if (!(cnam = findcmd(str + 1, 1))) {
 	    Alias a = (Alias) aliastab->getnode(aliastab, str + 1);
 	    
 	    if (a)
-		cnam = ztrdup(a->text);
+		cnam = a->text;
 	    else {
 		if (isset(NOMATCH))
 		    zerr("%s not found", str + 1, 0);
@@ -399,7 +399,6 @@ filesubstr(char **namptr, int assign)
 	    }
 	}
 	*namptr = dupstring(cnam);
-	zsfree(cnam);
 	if (sav) {
 	    *pp = sav;
 	    *namptr = dyncat(*namptr, pp);
diff --git a/Src/watch.c b/Src/watch.c
index 2532e0a63..6fe77afcd 100644
--- a/Src/watch.c
+++ b/Src/watch.c
@@ -235,7 +235,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 # endif /* WATCH_UTMP_UT_HOST */
 
     while (*fmt)
-	if (*fmt == '\\')
+	if (*fmt == '\\') {
 	    if (*++fmt) {
 		if (prnt)
 		    putchar(*fmt);
@@ -244,6 +244,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 		return fmt;
 	    else
 		break;
+	}
 	else if (*fmt == fini)
 	    return ++fmt;
 	else if (*fmt != '%') {
diff --git a/Src/zsh.export b/Src/zsh.export
index d8b0ddf19..c85bfbad4 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -23,6 +23,7 @@ closem
 cmdnamtab
 columns
 compctlreadptr
+cond_match
 cond_str
 cond_val
 coprocin
@@ -104,6 +105,8 @@ inredir
 insertlinknode
 install_handler
 intr
+intvargetfn
+intvarsetfn
 inwhat
 isfirstln
 jobtab
@@ -126,6 +129,7 @@ mypgrp
 mypid
 nameddirtab
 ncalloc
+new_heaps
 newhashtable
 newlinklist
 nicechar
@@ -137,6 +141,7 @@ noerrs
 noholdintr
 noop_function
 noop_function_int
+old_heaps
 optiontab
 opts
 paramtab
@@ -148,6 +153,7 @@ path
 pathchecked
 popheap
 postedit
+pparams
 ppid
 prefork
 prepromptfns
@@ -197,6 +203,9 @@ strpfx
 strsfx
 strucpy
 struncpy
+strvargetfn
+strvarsetfn
+switch_heaps
 tclen
 tcstr
 termflags
diff --git a/Src/zsh.h b/Src/zsh.h
index a31a7469b..dabcd90c8 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1305,6 +1305,22 @@ struct ttyinfo {
  * Memory management *
  *********************/
 
+/* heappush saves the current heap state using this structure */
+
+struct heapstack {
+    struct heapstack *next;	/* next one in list for this heap */
+    size_t used;
+};
+
+/* A zsh heap. */
+
+struct heap {
+    struct heap *next;		/* next one                                  */
+    size_t used;		/* bytes used from the heap                  */
+    struct heapstack *sp;	/* used by pushheap() to save the value used */
+#define arena(X)	((char *) (X) + sizeof(struct heap))
+};
+
 #ifndef DEBUG
 # define HEAPALLOC	do { int nonlocal_useheap = global_heapalloc(); do
 
@@ -1318,6 +1334,13 @@ struct ttyinfo {
 # define LASTALLOC_RETURN \
 			if ((nonlocal_useheap ? global_heapalloc() : \
 			     global_permalloc()), 0) {;} else return
+
+# define NEWHEAPS(h)    do { Heap oldheaps = h = new_heaps(); do
+# define OLDHEAPS       while (0); old_heaps(oldheaps); } while (0);
+
+# define SWITCHHEAPS(h)  do { Heap oldheaps = switch_heaps(h); do
+# define SWITCHBACKHEAPS while (0); switch_heaps(oldheaps); } while (0);
+
 #else
 # define HEAPALLOC	do { int nonlocal_useheap = global_heapalloc(); \
 			alloc_stackp++; do
@@ -1333,6 +1356,17 @@ struct ttyinfo {
 # define LASTALLOC_RETURN \
 			if ((nonlocal_useheap ? global_heapalloc() : \
 			    global_permalloc()),alloc_stackp--,0){;}else return
+
+# define NEWHEAPS(h)    do { Heap oldheaps = h = new_heaps(); \
+                        alloc_stackp++; do
+# define OLDHEAPS       while (0); alloc_stackp--; \
+                        old_heaps(oldheaps); } while (0);
+
+# define SWITCHHEAPS(h)  do { Heap oldheaps = switch_heaps(h); \
+                         alloc_stackp++; do
+# define SWITCHBACKHEAPS while (0); alloc_stackp--; \
+                         switch_heaps(oldheaps); } while (0);
+
 #endif
 
 /****************/
diff --git a/Util/zsh-development-guide b/Util/zsh-development-guide
index 73ac120ab..f779b17b3 100644
--- a/Util/zsh-development-guide
+++ b/Util/zsh-development-guide
@@ -120,15 +120,18 @@ Modules are described by a file named `foo.mdd' for a module
 is build. To describe the module it can/should set the following shell 
 variables:
 
-  - moddeps       modules on which this module depends (default none)
-  - nozshdep      non-empty indicates no dependence on the `zsh' pseudo-module
-  - alwayslink    if non-empty, always link the module into the executable
-  - autobins      builtins defined by the module, for autoloading
-  - objects       .o files making up this module (*must* be defined)
-  - proto         .pro files for this module (default generated from $objects)
-  - headers       extra headers for this module (default none)
-  - hdrdeps       extra headers on which the .mdh depends (default none)
-  - otherincs     extra headers that are included indirectly (default none)
+  - moddeps         modules on which this module depends (default none)
+  - nozshdep        non-empty indicates no dependence on the `zsh' pseudo-module
+  - alwayslink      if non-empty, always link the module into the executable
+  - autobins        builtins defined by the module, for autoloading
+  - autoinfixconds  infix condition codes defined by the module, for
+                    autoloading (without the leading `-')
+  - autoprefixconds like autoinfixconds, but for prefix condition codes
+  - objects         .o files making up this module (*must* be defined)
+  - proto           .pro files for this module (default generated from $objects)
+  - headers         extra headers for this module (default none)
+  - hdrdeps         extra headers on which the .mdh depends (default none)
+  - otherincs       extra headers that are included indirectly (default none)
 
 Be sure to put the values in quotes. For further enlightenment have a
 look at the `mkmakemod.sh' script in the Src directory of the
diff --git a/aczsh.m4 b/aczsh.m4
index dfc40ce6f..08c167d2f 100644
--- a/aczsh.m4
+++ b/aczsh.m4
@@ -45,7 +45,7 @@ else
 fi
 echo '
 	extern char **environ;
-	void *symlist1[] = {
+	void *symlist1[[]] = {
 		(void *)&environ,
 		(void *)0
 	};
diff --git a/configure.in b/configure.in
index 4fc45fe7c..1b2692ea1 100644
--- a/configure.in
+++ b/configure.in
@@ -340,7 +340,7 @@ AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \
 		 termios.h sys/param.h sys/filio.h string.h memory.h \
 		 limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \
 		 locale.h errno.h stdlib.h unistd.h sys/capability.h \
-		 utmp.h utmpx.h sys/types.h pwd.h grp.h)
+		 utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h)
 if test $dynamic = yes; then
   AC_CHECK_HEADERS(dlfcn.h)
   AC_CHECK_HEADERS(dl.h)
@@ -467,9 +467,22 @@ elif test $zsh_cv_decl_ospeed_must_define = yes; then
 fi
 
 dnl  Check if tgetent accepts NULL (and will allocate its own termcap buffer)
+dnl  Some termcaps reportedly accept a zero buffer, but then dump core
+dnl  in tgetstr().
 AC_CACHE_CHECK(if tgetent accepts NULL,
 zsh_cv_func_tgetent_accepts_null,
-[AC_TRY_RUN([main(){int i = tgetent((char*)0,"vt100");exit(!i || i == -1);}],
+[AC_TRY_RUN([
+main()
+{
+    int i = tgetent((char*)0,"vt100");
+    if (i > 0) {
+	char tbuf[1024], *u;
+    	u = tbuf;
+    	tgetstr("cl", &u);
+    }
+    exit(!i || i == -1);
+}
+],
   zsh_cv_func_tgetent_accepts_null=yes,
   zsh_cv_func_tgetent_accepts_null=no,
   zsh_cv_func_tgetent_accepts_null=no)])
@@ -615,7 +628,7 @@ dnl need to integrate this function
 dnl AC_FUNC_STRFTIME
 
 AC_CHECK_FUNCS(memcpy memmove \
-              strftime waitpid select tcsetpgrp tcgetattr strstr lstat \
+              strftime waitpid select poll tcsetpgrp tcgetattr strstr lstat \
               getlogin setpgid gettimeofday gethostname mkfifo wait3 difftime \
               sigblock sigsetmask sigrelse sighold killpg sigaction getrlimit \
               sigprocmask setuid seteuid setreuid setresuid setsid strerror \
diff --git a/patchlist.txt b/patchlist.txt
index 2179665ca..6ec3ec351 100644
--- a/patchlist.txt
+++ b/patchlist.txt
@@ -213,3 +213,56 @@ Sven's ignored character fix, 4828
 More Sven condition patches, 4837, 4842
 
 Final (???) isident() fix from Sven, 4845
+
+  pws-5
+
+Name of top level directory is now zsh-3.1.5-pws-5
+
+Missing part of Bart's sethparam() changes, 4851
+
+zftp test subcommand, 4852
+
+Geoff's refresh fix for a line the same length as the terminal width,
+4855
+
+Bart's fix for array slices, 4874
+
+Sven's accept-and-menu-complete-fix, 4878
+
+Sven's group completion fix, 4879
+
+Sven's module condition fixes, 4880
+
+Oliver Kiddle's autoconf fix, 4887
+
+My zftp fix (actually due to Andrej Borsenkow) for systems which only
+allow dup'ing sockets after they are connected, 4888.
+
+Bart's fix to making setting associative array elements inside
+substitutions consistent, 4893
+
+My typeset neatness and -a and -m fix, 4902
+
+My brief Etc/MACHINES addition, 4912
+
+My modification to findcmd() for memory leaks, 4923, plus comment
+alteration by Bart, 4924
+
+Sven's patch for completion after various reserved words, 4930
+
+My patch for compiler warnings, 4931
+
+My configuration fix for when tgetent() accepts a null argument but
+then tgetstr() dumps core, 4939
+
+Sven's alteration of `-t' behaviour, 4940.  This is slightly
+incompatible with previous patched versions of 3.1.5 since now you don't
+need '-tc' with -T.  However, you now do need '-tn' in cases where you
+don't want normal completion tried after a -T matches.
+
+Sven's new completion functions, 4850, 4881, 4941, 4942, 4943, 4944,
+4946, 4949, plus my addition of function pointers, 4945.  The example
+file is now in Misc/new-completion-examples.
+
+(Effect of) fix from Helmut Jarausch in 4947 partly due to change
+missed in patch.