about summary refs log tree commit diff
path: root/Functions
diff options
context:
space:
mode:
Diffstat (limited to 'Functions')
-rw-r--r--Functions/MIME/zsh-mime-handler2
-rw-r--r--Functions/Math/.distfiles2
-rw-r--r--Functions/Math/zmathfunc34
-rw-r--r--Functions/Misc/add-zle-hook-widget186
-rw-r--r--Functions/Misc/add-zsh-hook2
-rw-r--r--Functions/Misc/run-help-ip4
-rw-r--r--Functions/Misc/zcalc345
-rw-r--r--Functions/Misc/zed4
-rw-r--r--Functions/VCS_Info/Backends/VCS_INFO_get_data_git64
-rw-r--r--Functions/VCS_Info/Backends/VCS_INFO_get_data_hg57
-rw-r--r--Functions/VCS_Info/VCS_INFO_hexdump16
-rw-r--r--Functions/VCS_Info/VCS_INFO_patch2subject67
-rw-r--r--Functions/VCS_Info/VCS_INFO_quilt64
-rw-r--r--Functions/VCS_Info/VCS_INFO_set-patch-format79
-rw-r--r--Functions/VCS_Info/vcs_info3
-rw-r--r--Functions/Zle/bracketed-paste-magic50
-rw-r--r--Functions/Zle/bracketed-paste-url-magic4
-rw-r--r--Functions/Zle/delete-whole-word-match15
-rw-r--r--Functions/Zle/expand-absolute-path2
-rw-r--r--Functions/Zle/history-beginning-search-menu2
-rw-r--r--Functions/Zle/insert-unicode-char4
-rw-r--r--Functions/Zle/match-words-by-style53
-rw-r--r--Functions/Zle/select-bracketed2
-rw-r--r--Functions/Zle/select-word-match120
-rw-r--r--Functions/Zle/surround7
-rw-r--r--Functions/Zle/vi-pipe39
-rw-r--r--Functions/Zle/zcalc-auto-insert3
27 files changed, 953 insertions, 277 deletions
diff --git a/Functions/MIME/zsh-mime-handler b/Functions/MIME/zsh-mime-handler
index 24e5184fc..288a0796d 100644
--- a/Functions/MIME/zsh-mime-handler
+++ b/Functions/MIME/zsh-mime-handler
@@ -127,7 +127,7 @@ for pattern in $exec_asis; do
   files=(${dirpref}${~pattern})
   if [[ -n ${files[(r)$1]} ]]; then
     for pattern in $exec_never; do
-      [[ ${1:A} = ${~pattern} ]] && break 2
+      [[ ${1:P} = ${~pattern} ]] && break 2
     done
     if (( list )); then
       for (( i = 1; i <= $#; i++ )); do
diff --git a/Functions/Math/.distfiles b/Functions/Math/.distfiles
new file mode 100644
index 000000000..f03668b3a
--- /dev/null
+++ b/Functions/Math/.distfiles
@@ -0,0 +1,2 @@
+DISTFILES_SRC='
+'
diff --git a/Functions/Math/zmathfunc b/Functions/Math/zmathfunc
new file mode 100644
index 000000000..4ff40700d
--- /dev/null
+++ b/Functions/Math/zmathfunc
@@ -0,0 +1,34 @@
+#autoload
+
+zsh_math_func_min() {
+  local result=$1
+  shift
+  local arg
+  for arg ; do
+    (( $arg < result )) && result=$arg
+  done
+  (( result )) # return
+}
+functions -M min 1 -1 zsh_math_func_min # at least one argument
+
+zsh_math_func_max() {
+  local result=$1
+  shift
+  local arg
+  for arg ; do
+    (( $arg > result )) && result=$arg
+  done
+  (( result )) # return
+}
+functions -M max 1 -1 zsh_math_func_max # at least one argument
+
+zsh_math_func_sum() {
+  local sum
+  local arg
+  for arg ; do
+    (( sum += $arg ))
+  done
+  (( sum ))
+}
+functions -M sum 0 -1 zsh_math_func_sum
+
diff --git a/Functions/Misc/add-zle-hook-widget b/Functions/Misc/add-zle-hook-widget
new file mode 100644
index 000000000..d8a3950fb
--- /dev/null
+++ b/Functions/Misc/add-zle-hook-widget
@@ -0,0 +1,186 @@
+# Add to HOOK the given WIDGET
+# 
+# HOOK is one of isearch-exit, isearch-update, line-pre-redraw, line-init,
+# line-finish, history-line-set, keymap-select (the zle- prefix is allowed
+# but not required).  If a widget corresponding to HOOK already exists, it
+# is preserved and called first in the new set of HOOK widgets.
+#
+# With -d, remove the WIDGET from the hook instead; deletes the hook
+# linkage if it is empty.
+#
+# -D behaves like -d, but pattern characters are active in WIDGET, so
+# any matching widget will be deleted from the hook.
+#
+# Without -d, if the WIDGET is not already defined, a function having the
+# same name is marked for autoload; -U is passed down to autoload if that
+# is given, as are -z and -k.  (This is harmless if the function is
+# already defined.)  The WIDGET is then created with zle -N.
+#
+# The -L option lists the hooks and their associated widgets.
+
+# This is probably more safeguarding than necessary
+zmodload -e zsh/zle || return 1
+{ zmodload zsh/parameter && zmodload zsh/zleparameter } || {
+    print -u2 "add-zle-hook-widget: Need parameter modules for zle hooks"
+    return 1
+}
+
+() { # Preserve caller global option settings
+
+emulate -L zsh
+
+# Setup - create the base functions for hook widgets that call the others
+
+local -a hooktypes=( zle-isearch-exit zle-isearch-update
+                     zle-line-pre-redraw zle-line-init zle-line-finish
+                     zle-history-line-set zle-keymap-select )
+# Stash in zstyle to make it global
+zstyle zle-hook types ${hooktypes#zle-}
+
+# Relying on multifuncdef option here
+function azhw:${^hooktypes} {
+    local -a hook_widgets
+    local hook
+    # Values of these styles look like number:name
+    # and we run them in number order
+    zstyle -a $WIDGET widgets hook_widgets
+    for hook in "${(@)${(@on)hook_widgets[@]}#<->:}"; do
+	if [[ "$hook" = user:* ]]; then
+	    # Preserve $WIDGET within the renamed widget
+	    zle "$hook" -N -- "$@"
+	else
+	    zle "$hook" -Nw -- "$@"
+	fi || return
+    done
+    return 0
+}
+
+# Redefine ourself with the setup left out
+
+function add-zle-hook-widget {
+    local -a hooktypes
+    zstyle -a zle-hook types hooktypes
+
+    # This part copied from add-zsh-hook
+    local usage="Usage: $funcstack[1] hook widgetname\nValid hooks are:\n  $hooktypes"
+
+    local opt
+    local -a autoopts
+    integer del list help
+
+    while getopts "dDhLUzk" opt; do
+	case $opt in
+	    (d)
+	    del=1
+	    ;;
+
+	    (D)
+	    del=2
+	    ;;
+
+	    (h)
+	    help=1
+	    ;;
+
+	    (L)
+	    list=1
+	    ;;
+
+	    ([Uzk])
+	    autoopts+=(-$opt)
+	    ;;
+
+	    (*)
+	    return 1
+	    ;;
+	esac
+    done
+    shift $(( OPTIND - 1 ))
+
+    1=${1#zle-}	# Strip prefix not stored in zle-hook types style
+
+    if (( list )); then
+	zstyle -L "zle-(${1:-${(@j:|:)hooktypes[@]}})" widgets
+	return $?
+    elif (( help || $# != 2 || ${hooktypes[(I)$1]} == 0 )); then
+	print -u$(( 2 - help )) $usage
+	return $(( 1 - help ))
+    fi
+
+    local -aU extant_hooks
+    local hook="zle-$1"
+    local fn="$2"
+
+    if (( del )); then
+        # delete, if hook is set
+	if zstyle -g extant_hooks "$hook" widgets; then
+	    if (( del == 2 )); then
+		set -A extant_hooks ${extant_hooks[@]:#(<->:|)${~fn}}
+	    else
+		set -A extant_hooks ${extant_hooks[@]:#(<->:|)$fn}
+	    fi
+            # unset if no remaining entries
+	    if (( ${#extant_hooks} )); then
+		zstyle "$hook" widgets "${extant_hooks[@]}"
+	    else
+		zstyle -d "$hook" widgets
+	    fi
+	fi
+    else
+	# Check whether attempting to add a widget named for the hook
+	if [[ "$fn" = "$hook" ]]; then
+	    if [[ -n "${widgets[$fn]}" ]]; then
+		print -u2 "$funcstack[1]: Cannot hook $fn to itself"
+		return 1
+	    fi
+	    # No point in building the array until another is added
+	    autoload "${autoopts[@]}" -- "$fn"
+	    zle -N "$fn"
+	    return 0
+	fi
+	integer i=${#options[ksharrays]}-2
+	zstyle -g extant_hooks "$hook" widgets
+        # Check for an existing widget, add it as the first hook
+	if [[ ${widgets[$hook]} != "user:azhw:$hook" ]]; then
+	    if [[ -n ${widgets[$hook]} ]]; then
+		zle -A "$hook" "${widgets[$hook]}"
+		extant_hooks=(0:"${widgets[$hook]}" "${extant_hooks[@]}")
+	    fi
+	    zle -N "$hook" azhw:"$hook"
+	fi
+	# Add new widget only if not already in the hook list
+	if [[ -z ${(M)extant_hooks[@]:#(<->:|)$fn} ]]; then
+       	    # no index and not already hooked
+            # assign largest existing index plus 1
+	    i=${${(On@)${(@M)extant_hooks[@]#<->:}%:}[i]}+1
+	else
+	    return 0
+	fi
+	extant_hooks+=("${i}:${fn}")
+	zstyle -- "$hook" widgets "${extant_hooks[@]}"
+	if [[ -z "${widgets[$fn]}" ]]; then
+	    autoload "${autoopts[@]}" -- "$fn"
+	    zle -N -- "$fn"
+	fi
+	if [[ -z "${widgets[$hook]}" ]]; then
+	    zle -N "$hook" azhw:"$hook"
+	fi
+    fi
+}
+
+} "$@" # Resume caller global options
+
+# Handle zsh autoloading conventions:
+# - "file" appears last in zsh_eval_context when "source"-ing
+# - "evalautofunc" appears with kshautoload set or autoload -k
+# - "loadautofunc" appears with kshautoload unset or autoload -z
+# - use of autoload +X cannot reliably be detected, use best guess
+case "$zsh_eval_context" in
+*file) ;;
+*evalautofunc) ;;
+*loadautofunc) add-zle-hook-widget "$@";;
+*) [[ -o kshautoload ]] || add-zle-hook-widget "$@";;
+esac
+# Note fallback here is equivalent to the usual best-guess used by
+# functions written for zsh before $zsh_eval_context was available
+# so this case-statement is backward-compatible.
diff --git a/Functions/Misc/add-zsh-hook b/Functions/Misc/add-zsh-hook
index fc39659ae..3bc952e2f 100644
--- a/Functions/Misc/add-zsh-hook
+++ b/Functions/Misc/add-zsh-hook
@@ -19,7 +19,7 @@ hooktypes=(
   chpwd precmd preexec periodic zshaddhistory zshexit
   zsh_directory_name
 )
-local usage="Usage: $0 hook function\nValid hooks are:\n  $hooktypes"
+local usage="Usage: add-zsh-hook hook function\nValid hooks are:\n  $hooktypes"
 
 local opt
 local -a autoopts
diff --git a/Functions/Misc/run-help-ip b/Functions/Misc/run-help-ip
index 740af52b5..8807f9ef1 100644
--- a/Functions/Misc/run-help-ip
+++ b/Functions/Misc/run-help-ip
@@ -19,8 +19,8 @@ while [[ $# != 0 && $1 == -* ]]; do
 done
 
 case $1 in
-    (addr*) man ip-address ;;
-    (addrlabel) man ip-addrlabel ;;
+    (addrl*) man ip-addrlabel ;;
+    (a*) man ip-address ;;
     (l2*) man ip-l2tp ;;
     (li*) man ip-link ;;
     (ma*) man ip-maddress ;;
diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc
index 857007a94..480373345 100644
--- a/Functions/Misc/zcalc
+++ b/Functions/Misc/zcalc
@@ -94,24 +94,53 @@
 # sequentially just as if read automatically.
 
 emulate -L zsh
-setopt extendedglob
+setopt extendedglob typesetsilent
+
+zcalc_show_value() {
+  if [[ -n $_base ]]; then
+    print -- $(( $_base $1 ))
+  elif [[ $1 = *.* ]] || (( _outdigits )); then
+    # With normal output, ensure trailing "." doesn't get lost.
+    if [[ -z $_forms[_outform] || ($_outform -eq 1 && $1 = *.) ]]; then
+      print -- $(( $1 ))
+    else
+      printf "$_forms[_outform]\n" $_outdigits $1
+    fi
+  else
+    printf "%d\n" $1
+  fi
+}
 
 # For testing in ZLE functions.
 local ZCALC_ACTIVE=1
 
 # TODO: make local variables that shouldn't be visible in expressions
 # begin with _.
-local line ans base defbase forms match mbegin mend psvar optlist opt arg
+local _line ans _base _defbase _forms match mbegin mend
+local psvar _optlist _opt _arg _tmp
 local compcontext="-zcalc-line-"
-integer num outdigits outform=1 expression_mode
-local -a expressions
+integer _num _outdigits _outform=1 _expression_mode
+integer _rpn_mode _matched _show_stack _i _n
+integer _max_stack _push
+local -a _expressions stack
 
 # We use our own history file with an automatic pop on exit.
 history -ap "${ZDOTDIR:-$HOME}/.zcalc_history"
 
-forms=( '%2$g' '%.*g' '%.*f' '%.*E' '')
+_forms=( '%2$g' '%.*g' '%.*f' '%.*E' '')
 
-zmodload -i zsh/mathfunc 2>/dev/null
+local _mathfuncs
+if zmodload -i zsh/mathfunc 2>/dev/null; then
+  zmodload -P _mathfuncs -FL zsh/mathfunc
+  _mathfuncs="("${(j.|.)${_mathfuncs##f:}}")"
+fi
+local -A _userfuncs
+for _line in ${(f)"$(functions -M)"}; do
+  match=(${=_line})
+  # get minimum number of arguments
+  _userfuncs[${match[3]}]=${match[4]}
+done
+_line=
 autoload -Uz zmathfuncdef
 
 if (( ! ${+ZCALCPROMPT} )); then
@@ -127,111 +156,119 @@ if [[ -f "${ZDOTDIR:-$HOME}/.zcalcrc" ]]; then
 fi
 
 # Process command line
-while [[ -n $1 && $1 = -(|[#-]*|f|e) ]]; do
-  optlist=${1[2,-1]}
+while [[ -n $1 && $1 = -(|[#-]*|f|e|r(<->|)) ]]; do
+  _optlist=${1[2,-1]}
   shift
-  [[ $optlist = (|-) ]] && break
-  while [[ -n $optlist ]]; do
-    opt=${optlist[1]}
-    optlist=${optlist[2,-1]}
-    case $opt in
+  [[ $_optlist = (|-) ]] && break
+  while [[ -n $_optlist ]]; do
+    _opt=${_optlist[1]}
+    _optlist=${_optlist[2,-1]}
+    case $_opt in
       ('#') # Default base
-            if [[ -n $optlist ]]; then
-	       arg=$optlist
-	       optlist=
+            if [[ -n $_optlist ]]; then
+	       _arg=$_optlist
+	       _optlist=
 	    elif [[ -n $1 ]]; then
-	       arg=$1
+	       _arg=$1
 	       shift
 	    else
 	       print -- "-# requires an argument" >&2
 	       return 1
 	    fi
-	    if [[ $arg != (|\#)[[:digit:]]## ]]; then
+	    if [[ $_arg != (|\#)[[:digit:]]## ]]; then
 	      print -- "-# requires a decimal number as an argument" >&2
 	      return 1
 	    fi
-            defbase="[#${arg}]"
+            _defbase="[#${_arg}]"
 	    ;;
 	(f) # Force floating point operation
 	    setopt forcefloat
 	    ;;
         (e) # Arguments are expressions
-	    (( expression_mode = 1 ));
+	    (( _expression_mode = 1 ));
+	    ;;
+        (r) # RPN mode.
+	    (( _rpn_mode = 1 ))
+	    ZCALC_ACTIVE=rpn
+	    if [[ $_optlist = (#b)(<->)* ]]; then
+	       (( _show_stack = ${match[1]} ))
+               _optlist=${_optlist[${#match[1]}+1,-2]}
+	    fi
 	    ;;
     esac
   done
 done
 
-if (( expression_mode )); then
-  expressions=("$@")
+if (( _expression_mode )); then
+  _expressions=("$@")
   argv=()
 fi
 
-for (( num = 1; num <= $#; num++ )); do
+for (( _num = 1; _num <= $#; _num++ )); do
   # Make sure all arguments have been evaluated.
   # The `$' before the second argv forces string rather than numeric
   # substitution.
-  (( argv[$num] = $argv[$num] ))
-  print "$num> $argv[$num]"
+  (( argv[$_num] = $argv[$_num] ))
+  print "$_num> $argv[$_num]"
 done
 
-psvar[1]=$num
-local prev_line cont_prompt
-while (( expression_mode )) ||
-  vared -cehp "${cont_prompt}${ZCALCPROMPT}" line; do
-  if (( expression_mode )); then
-    (( ${#expressions} )) || break
-    line=$expressions[1]
-    shift expressions
+psvar[1]=$_num
+local _prev_line _cont_prompt
+while (( _expression_mode )) ||
+  vared -cehp "${_cont_prompt}${ZCALCPROMPT}" _line; do
+  if (( _expression_mode )); then
+    (( ${#_expressions} )) || break
+    _line=$_expressions[1]
+    shift _expressions
   fi
-  if [[ $line = (|*[^\\])('\\')#'\' ]]; then
-    prev_line+=$line[1,-2]
-    cont_prompt="..."
-    line=
+  if [[ $_line = (|*[^\\])('\\')#'\' ]]; then
+    _prev_line+=$_line[1,-2]
+    _cont_prompt="..."
+    _line=
     continue
   fi
-  line="$prev_line$line"
-  prev_line=
-  cont_prompt=
+  _line="$_prev_line$_line"
+  _prev_line=
+  _cont_prompt=
   # Test whether there are as many open as close
-  # parentheses in the line so far.
-  if [[ ${#line//[^\(]} -gt ${#line//[^\)]} ]]; then
-      prev_line+=$line
-      cont_prompt="..."
-      line=
+  # parentheses in the _line so far.
+  if [[ ${#_line//[^\(]} -gt ${#_line//[^\)]} ]]; then
+      _prev_line+=$_line
+      _cont_prompt="..."
+      _line=
       continue
   fi
-  [[ -z $line ]] && break
+  [[ -z $_line ]] && break
   # special cases
   # Set default base if `[#16]' or `[##16]' etc. on its own.
   # Unset it if `[#]' or `[##]'.
-  if [[ $line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then
+  if [[ $_line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then
     if [[ -z $match[6] ]]; then
       if [[ -z $match[3] ]]; then
-	defbase=
+	_defbase=
       else
-	defbase=$match[1]
+	_defbase=$match[1]
       fi
-      print -s -- $line
-      print -- $(( ${defbase} ans ))
-      line=
+      print -s -- $_line
+      print -- $(( ${_defbase} ans ))
+      _line=
       continue
     else
-      base=$match[1]
+      _base=$match[1]
     fi
   else
-    base=$defbase
+    _base=$_defbase
   fi
 
-  print -s -- $line
+  print -s -- $_line
 
-  line="${${line##[[:blank:]]#}%%[[:blank:]]#}"
-  case "$line" in
+  _line="${${_line##[[:blank:]]#}%%[[:blank:]]#}"
+  case "$_line" in
     # Escapes begin with a colon
     (:(\\|)\!*)
     # shell escape: handle completion's habit of quoting the !
-    eval ${line##:(\\|)\![[:blank:]]#}
-    line=
+    eval ${_line##:(\\|)\![[:blank:]]#}
+    _line=
     continue
     ;;
 
@@ -241,72 +278,196 @@ while (( expression_mode )) ||
     ;;
 
     ((:|)norm) # restore output format to default
-      outform=1
+      _outform=1
     ;;
 
     ((:|)sci[[:blank:]]#(#b)(<->)(#B))
-      outdigits=$match[1]
-      outform=2
+      _outdigits=$match[1]
+      _outform=2
     ;;
 
     ((:|)fix[[:blank:]]#(#b)(<->)(#B))
-      outdigits=$match[1]
-      outform=3
+      _outdigits=$match[1]
+      _outform=3
     ;;
 
     ((:|)eng[[:blank:]]#(#b)(<->)(#B))
-      outdigits=$match[1]
-      outform=4
+      _outdigits=$match[1]
+      _outform=4
     ;;
 
     (:raw)
-    outform=5
+    _outform=5
     ;;
 
     ((:|)local([[:blank:]]##*|))
-      eval $line
-      line=
+      eval ${_line##:}
+      _line=
       continue
     ;;
 
     ((function|:f(unc(tion|)|))[[:blank:]]##(#b)([^[:blank:]]##)(|[[:blank:]]##([^[:blank:]]*)))
       zmathfuncdef $match[1] $match[3]
-      line=
+      _userfuncs[$match[1]]=${$(functions -Mm $match[1])[4]}
+      _line=
       continue
     ;;
 
     (:*)
     print "Unrecognised escape"
-    line=
+    _line=
+    continue
+    ;;
+
+    (\$[[:IDENT:]]##)
+    # Display only, no calculation
+    _line=${_line##\$}
+    print -r -- ${(P)_line}
+    _line=
     continue
     ;;
 
     (*)
-      # Latest value is stored as a string, because it might be floating
-      # point or integer --- we don't know till after the evaluation, and
-      # arrays always store scalars anyway.
-      #
-      # Since it's a string, we'd better make sure we know which
-      # base it's in, so don't change that until we actually print it.
-      eval "ans=\$(( $line ))"
-      # on error $ans is not set; let user re-edit line
-      [[ -n $ans ]] || continue
-      argv[num++]=$ans
-      psvar[1]=$num
+      _line=${${_line##[[:blank:]]##}%%[[:blank:]]##}
+      if [[ _rpn_mode -ne 0 && $_line != '' ]]; then
+	_push=1
+	_matched=1
+	case $_line in
+	  (\<[[:IDENT:]]##)
+	  ans=${(P)${_line##\<}}
+	  ;;
+
+	  (\=|pop|\>[[:IDENT:]]#)
+	  if (( ${#stack} < 1 )); then
+	    print -r -- "${_line}: not enough values on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  case $_line in
+	    (=)
+	    ans=${stack[1]}
+	    ;;
+	    (pop|\>)
+	    _push=0
+	    shift stack
+	    ;;
+	    (\>[[:IDENT:]]##)
+	    if [[ ${_line##\>} = (_*|stack|ans|PI|E) ]]; then
+	      print "${_line##\>}: reserved variable" >&2
+	      _line=
+	      continue
+	    fi
+	    local ${_line##\>}
+	    (( ${_line##\>} = ${stack[1]} ))
+	    _push=0
+	    shift stack
+	    ;;
+	    (*)
+	    print "BUG in special RPN functions" >&2
+	    _line=
+	    continue
+	    ;;
+	  esac
+	  ;;
+
+	  (+|-|\^|\||\&|\*|/|\*\*|\>\>|\<\</)
+	  # Operators with two arguments
+	  if (( ${#stack} < 2 )); then
+	    print -r -- "${_line}: not enough values on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  eval "(( ans = \${stack[2]} $_line \${stack[1]} ))"
+	  shift 2 stack
+	  ;;
+
+	  (ldexp|jn|yn|scalb|xy|\<\>)
+	  # Functions with two arguments
+	  if (( ${#stack} < 2 )); then
+	    print -r -- "${_line}: not enough values on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  if [[ $_line = (xy|\<\>) ]]; then
+	    _tmp=${stack[1]}
+	    stack[1]=${stack[2]}
+	    stack[2]=$_tmp
+	    _push=0
+	  else
+	    eval "(( ans = ${_line}(\${stack[2]},\${stack[1]}) ))"
+	    shift 2 stack
+	  fi
+	  ;;
+
+	  (${~_mathfuncs})
+	  # Functions with a single argument.
+	  # This is actually a superset, but we should have matched
+	  # any that shouldn't be in it in previous cases.
+	  if (( ${#stack} < 1 )); then
+	    print -r -- "${_line}: not enough values on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  eval "(( ans = ${_line}(\${stack[1]}) ))"
+	  shift stack
+	  ;;
+
+	  (${(kj.|.)~_userfuncs})
+	  # Get minimum number of arguments to user function
+	  _n=${_userfuncs[$_line]}
+	  if (( ${#stack} < n_ )); then
+	    print -r -- "${_line}: not enough values ($_n) on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  _line+="("
+	  # least recent elements on stack are earlier arguments
+	  for (( _i = _n; _i > 0; _i-- )); do
+	    _line+=${stack[_i]}
+	    (( _i > 1 )) && _line+=","
+	  done
+	  _line+=")"
+	  shift $_n stack
+	  eval "(( ans = $_line ))"
+	  ;;
+
+	  (*)
+	  # Treat as expression evaluating to new value to go on stack.
+	  _matched=0
+	  ;;
+	esac
+      else
+	_matched=0
+      fi
+      if (( ! _matched )); then
+	# Latest value is stored` as a string, because it might be floating
+	# point or integer --- we don't know till after the evaluation, and
+	# arrays always store scalars anyway.
+	#
+	# Since it's a string, we'd better make sure we know which
+	# base it's in, so don't change that until we actually print it.
+	if ! eval "ans=\$(( $_line ))"; then
+	  _line=
+	  continue
+	fi
+	# on error $ans is not set; let user re-edit _line
+	[[ -n $ans ]] || continue
+      fi
+      argv[_num++]=$ans
+      psvar[1]=$_num
+      (( _push )) && stack=($ans $stack)
     ;;
   esac
-  if [[ -n $base ]]; then
-    print -- $(( $base $ans ))
-  elif [[ $ans = *.* ]] || (( outdigits )); then
-    if [[ -z $forms[outform] ]]; then
-      print -- $(( $ans ))
-    else
-      printf "$forms[outform]\n" $outdigits $ans
-    fi
+  if (( _show_stack )); then
+    (( _max_stack = (_show_stack > ${#stack}) ? ${#stack} : _show_stack ))
+    for (( _i = _max_stack; _i > 0; _i-- )); do
+      printf "%3d: " $_i
+      zcalc_show_value ${stack[_i]}
+    done
   else
-    printf "%d\n" $ans
+    zcalc_show_value $ans
   fi
-  line=
+  _line=
 done
 
 return 0
diff --git a/Functions/Misc/zed b/Functions/Misc/zed
index eb8f557ea..77d392bc3 100644
--- a/Functions/Misc/zed
+++ b/Functions/Misc/zed
@@ -6,6 +6,8 @@
 # Use ^X^W to save, ^C to abort.
 # Option -f: edit shell functions.  (Also if called as fned.)
 
+setopt localoptions noksharrays
+
 local var opts zed_file_name
 # We do not want timeout while we are editing a file
 integer TMOUT=0 okargs=1 fun bind
@@ -74,7 +76,7 @@ if ((fun)) then
   if [[ $var = *\#\ undefined* ]] then
     var="$(autoload +X $1; functions -- $1)"
   elif [[ -z $var ]] then
-    var="$1() {
+    var="${(q-)1} () {
 }"
   fi
   vared -M zed -m zed-vicmd var && eval function "$var"
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
index 472c10d5d..f3dd95dcb 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
@@ -120,41 +120,15 @@ VCS_INFO_git_getbranch () {
 }
 
 VCS_INFO_git_handle_patches () {
-    local git_applied_s git_unapplied_s gitmsg git_all
+    local git_applied_s git_unapplied_s gitmsg
     git_patches_applied=(${(Oa)git_patches_applied})
     git_patches_unapplied=(${(Oa)git_patches_unapplied})
-    (( git_all = ${#git_patches_applied} + ${#git_patches_unapplied} ))
 
-    if VCS_INFO_hook 'gen-applied-string' "${git_patches_applied[@]}"; then
-        if (( ${#git_patches_applied} )); then
-            git_applied_s=${git_patches_applied[1]}
-        else
-            git_applied_s=""
-        fi
-    else
-        git_applied_s=${hook_com[applied-string]}
-    fi
-    hook_com=()
-    if VCS_INFO_hook 'gen-unapplied-string' "${git_patches_unapplied[@]}"; then
-        git_patches_unapplied=${#git_patches_unapplied}
-    else
-        git_patches_unapplied=${hook_com[unapplied-string]}
-    fi
-
-    if (( ${#git_patches_applied} )); then
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" patch-format gitmsg || gitmsg="%p (%n applied)"
-    else
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" nopatch-format gitmsg || gitmsg="no patch applied"
-    fi
-    hook_com=( applied "${git_applied_s}"     unapplied "${git_patches_unapplied}"
-               applied-n ${#git_patches_applied} unapplied-n ${#git_patches_unapplied} all-n ${git_all} )
-    if VCS_INFO_hook 'set-patch-format' "${gitmsg}"; then
-        zformat -f gitmisc "${gitmsg}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
-                                          "n:${#git_patches_applied}" "c:${#git_patches_unapplied}" "a:${git_all}"
-    else
-        gitmisc=${hook_com[patch-replace]}
-    fi
-    hook_com=()
+    VCS_INFO_set-patch-format 'git_patches_applied' 'git_applied_s' \
+                              'git_patches_unapplied' 'git_unapplied_s' \
+                              ":vcs_info:${vcs}:${usercontext}:${rrn}" gitmsg \
+                              '' ''
+    gitmisc=$REPLY
 }
 
 gitdir=${vcs_comm[gitdir]}
@@ -182,7 +156,7 @@ if (( querystaged || queryunstaged )) && \
    [[ "$(${vcs_comm[cmd]} rev-parse --is-inside-work-tree 2> /dev/null)" == 'true' ]] ; then
     # Default: off - these are potentially expensive on big repositories
     if (( queryunstaged )) ; then
-        ${vcs_comm[cmd]} diff --no-ext-diff --ignore-submodules=dirty --quiet --exit-code ||
+        ${vcs_comm[cmd]} diff --no-ext-diff --ignore-submodules=dirty --quiet --exit-code 2> /dev/null ||
             gitunstaged=1
     fi
     if (( querystaged )) ; then
@@ -205,6 +179,7 @@ local patchdir=${gitdir}/patches/${gitbranch}
 if [[ -d $patchdir ]] && [[ -f $patchdir/applied ]] \
    && [[ -f $patchdir/unapplied ]]
 then
+    # stgit
     git_patches_applied=(${(f)"$(< "${patchdir}/applied")"})
     git_patches_unapplied=(${(f)"$(< "${patchdir}/unapplied")"})
     VCS_INFO_git_handle_patches
@@ -213,11 +188,15 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then
     local p
     [[ -f "${patchdir}/done" ]] &&
     for p in ${(f)"$(< "${patchdir}/done")"}; do
-        # remove action
-        git_patches_applied+=("${${(s: :)p}[2,-1]}")
+        # pick/edit/fixup/squash/reword: Add "$hash $subject" to $git_patches_applied.
+        # exec: Add "exec ${command}" to $git_patches_applied.
+        # (anything else): As 'exec'.
+        p=${p/(#s)(p|pick|e|edit|r|reword|f|fixup|s|squash) /}
+        p=${p/(#s)x /exec }
+        git_patches_applied+=("$p")
     done
     if [[ -f "${patchdir}/git-rebase-todo" ]] ; then
-        git_patches_unapplied=(${(f)"$(grep -v '^$' "${patchdir}/git-rebase-todo" | grep -v '^#')"})
+        git_patches_unapplied=( ${${(f)${"$(<"${patchdir}/git-rebase-todo")"}}:#[#]*} )
     fi
     VCS_INFO_git_handle_patches
 elif [[ -d "${gitdir}/rebase-apply" ]]; then
@@ -228,10 +207,19 @@ elif [[ -d "${gitdir}/rebase-apply" ]]; then
         local cur=$(< $next)
         local p subject
         for ((p = 1; p < cur; p++)); do
-            git_patches_applied+=("$(printf "%04d" $p) ?")
+            printf -v "git_patches_applied[$p]"  "%04d ?" "$p"
         done
         if [[ -f "${patchdir}/msg-clean" ]]; then
             subject="${$(< "${patchdir}/msg-clean")[(f)1]}"
+        elif local this_patch_file
+             printf -v this_patch_file "%s/%04d" "${patchdir}" "${cur}"
+             [[ -f $this_patch_file ]]
+        then
+            () {
+              local REPLY
+              VCS_INFO_patch2subject "${this_patch_file}"
+              subject=$REPLY
+            }
         fi
         if [[ -f "${patchdir}/original-commit" ]]; then
             if [[ -n $subject ]]; then
@@ -257,6 +245,7 @@ elif [[ -f "${gitdir}/MERGE_HEAD" ]]; then
     # This is 'git merge --no-commit'
     local -a heads=( ${(@f)"$(<"${gitdir}/MERGE_HEAD")"} )
     local subject;
+    # TODO: maybe read up to the first blank line
     IFS='' read -r subject < "${gitdir}/MERGE_MSG"
     # $subject is the subject line of the would-be commit
     # Maybe we can get the subject lines of MERGE_HEAD's commits cheaply?
@@ -282,6 +271,7 @@ elif [[ -f "${gitdir}/CHERRY_PICK_HEAD" ]]; then
     # ### be "1".  The %u/%c tuple will assume the values [(1,2), (1,1), (1,0)],
     # ### whereas the correct sequence would be [(1,2), (2,1), (3,0)].
     local subject
+    # TODO: maybe read up to the first blank line
     IFS='' read -r subject < "${gitdir}/MERGE_MSG"
     git_patches_applied=( "$(<${gitdir}/CHERRY_PICK_HEAD) ${subject}" )
     if [[ -f "${gitdir}/sequencer/todo" ]]; then
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
index f35ad5965..d4030125c 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
@@ -13,7 +13,7 @@ local hgbase bmfile branchfile rebasefile dirstatefile mqseriesfile \
     hgbmstring hgmqstring applied_string unapplied_string guards_string
 
 local -a hgid_args defrevformat defbranchformat \
-    hgbmarks mqpatches mqseries mqguards mqunapplied hgmisc \
+    hgbmarks mqpatches mqguards mqunapplied hgmisc \
     i_patchguards i_negguards i_posguards
 
 local -A hook_com
@@ -40,9 +40,10 @@ VCS_INFO_adjust
 # Disabled by default anyway, so no harm done.
 if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
     if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" use-simple \
-            && ( VCS_INFO_check_com hexdump ) && [[ -r ${dirstatefile} ]] ; then
-        # Calling hexdump is (much) faster than hg but doesn't get the local rev
-        r_csetid=$(hexdump -n 20 -e '1/1 "%02x"' ${dirstatefile})
+            && VCS_INFO_hexdump ${dirstatefile} 20 ; then
+        # Calling VCS_INFO_hexdump is (much) faster than hg but doesn't get
+        # the local rev
+        r_csetid=$REPLY
     else
         # Settling for a short (but unique!) hash because getting the full
         # 40-char hash in addition to all the other info we want isn't
@@ -174,9 +175,6 @@ if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \
             # Skip commented lines
             [[ ${i_patch} == [[:space:]]#"#"* ]] && continue
 
-            # Keep list of all patches
-            mqseries+=( $i_patch )
-
             # Separate negative and positive guards to more easily find the
             # intersection of active guards with patch guards
             i_patchguards=( ${(s: :)i_patchguards} )
@@ -202,50 +200,21 @@ if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \
         done < ${mqseriesfile}
     fi
 
-    if VCS_INFO_hook 'gen-applied-string' "${mqpatches[@]}"; then
-        (( ${#mqpatches} )) && applied_string=${mqpatches[1]}
-    else
-        applied_string=${hook_com[applied-string]}
-    fi
-
-    hook_com=()
-
-    if VCS_INFO_hook 'gen-unapplied-string' "${mqunapplied[@]}"; then
-        unapplied_string=${#mqunapplied}
-    else
-        unapplied_string=${hook_com[unapplied-string]}
-    fi
-
-    hook_com=()
-
     if VCS_INFO_hook 'gen-mqguards-string' "${mqguards[@]}"; then
         guards_string=${(j:,:)mqguards}
+        # TODO: %-escape extra_zformats[g:...] value
     else
         guards_string=${hook_com[guards-string]}
     fi
 
-    if (( ${#mqpatches} )); then
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" patch-format \
-            hgmqstring || hgmqstring="%p (%n applied)"
-    else
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" nopatch-format \
-            hgmqstring || hgmqstring="no patch applied"
-    fi
-
-    hook_com=( applied "${applied_string}" unapplied "${unapplied_string}"
-               applied-n ${#mqpatches}     unapplied-n ${#mqunapplied}     all-n ${#mqseries}
-               guards "${guards_string}"   guards-n ${#mqguards} )
+    local -A extra_hook_com=( guards "${guards_string}"   guards-n ${#mqguards} )
+    local -a extra_zformats=( "g:${extra_hook_com[guards]}" "G:${#mqguards}" )
 
-    if VCS_INFO_hook 'set-patch-format' ${qstring}; then
-        zformat -f hgmqstring "${hgmqstring}" \
-            "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
-            "n:${#mqpatches}" "c:${#mqunapplied}" "a:${#mqseries}" \
-            "g:${hook_com[guards]}" "G:${#mqguards}"
-    else
-        hgmqstring=${hook_com[patch-replace]}
-    fi
-
-    hook_com=()
+    VCS_INFO_set-patch-format 'mqpatches' 'applied_string' \
+                              'mqunapplied' 'unapplied_string' \
+                              ":vcs_info:${vcs}:${usercontext}:${rrn}" hgmqstring \
+                              extra_hook_com extra_zformats
+    hgmqstring=$REPLY
 fi
 
 
diff --git a/Functions/VCS_Info/VCS_INFO_hexdump b/Functions/VCS_Info/VCS_INFO_hexdump
new file mode 100644
index 000000000..11f1c1a50
--- /dev/null
+++ b/Functions/VCS_Info/VCS_INFO_hexdump
@@ -0,0 +1,16 @@
+## vim:ft=zsh
+
+# VCS_INFO_hexdump FILENAME BYTECOUNT
+#
+# Return in $REPLY a hexadecimal representation (lowercase, no whitespace)
+# of the first BYTECOUNT bytes of FILENAME.
+
+if [[ -r $1 ]]; then
+  setopt localoptions nomultibyte extendedglob
+  local val
+  read -k $2 -u 0 val <$1
+  REPLY=${(Lj::)${(l:2::0:)${(@s//)val}//(#m)*/$(( [##16] ##$MATCH ))}}
+else
+  return 1
+fi
+
diff --git a/Functions/VCS_Info/VCS_INFO_patch2subject b/Functions/VCS_Info/VCS_INFO_patch2subject
new file mode 100644
index 000000000..e222e8382
--- /dev/null
+++ b/Functions/VCS_Info/VCS_INFO_patch2subject
@@ -0,0 +1,67 @@
+# This function takes as an argument a filename of a patch and sets $REPLY to
+# a single-line "subject", or unsets it if no subject could be extracted.
+{
+    setopt localoptions extendedglob
+    integer i
+    integer -r LIMIT=10
+    local -a lines
+    local needle
+    if [[ -f "$1" ]]; then
+        # Extract the first LIMIT lines, or up to the first empty line or the start of the unidiffs,
+        # whichever comes first.
+        while (( i++ < LIMIT )); do
+            IFS= read -r "lines[$i]"
+            if [[ -z ${lines[$i]} ]] || [[ ${lines[$i]} == (#b)(---|Index:)* ]]; then
+                lines[$i]=()
+                break
+            fi
+        done < "$1"
+        
+        if needle=${lines[(i)Subject:*]}; (( needle <= $#lines )); then
+            # "Subject: foo" line, plus rfc822 whitespace unfolding.
+            #
+            # Example: 'git format-patch' patches.
+            REPLY=${lines[needle]}
+            REPLY=${REPLY#*: }
+            REPLY=${REPLY#\[PATCH\] }
+            while [[ ${${lines[++needle]}[1]} == ' ' ]]; do
+                REPLY+=${lines[needle]}
+            done
+        elif needle=${lines[(r)Description:*]}; [[ -n $needle ]]; then
+            # "Description: foo" line.
+            #
+            # Example: DEP-3 patches.
+            REPLY=${needle#*: }
+        elif [[ ${lines[1]} == '# HG changeset patch' ]] && { needle=${${lines:#([#]*)}[1]}; [[ -n $needle ]] }; then
+            # Mercurial patch
+            REPLY=$needle
+        elif [[ ${lines[1]} == "commit "[0-9a-f](#c40) ]] &&
+             [[ ${lines[2]} == "Author:"* && ${lines[3]} == "Date:"* ]] &&
+             (( ! ${+lines[4]} )); then
+            # `git show` output.
+            #
+            # The log message is after the first blank line, so open() the file
+            # again.  Also check whether the following line (second line of the
+            # log message itself) is empty.
+            {
+              repeat 4 { IFS= read -r }
+              IFS= read -r needle; needle=${needle#'    '}
+              if IFS= read -r; REPLY=${REPLY#'    '}; [[ -n $REPLY ]]; then
+                needle+='...'
+              fi
+            } < "$1"
+            REPLY=$needle
+        elif (( ${+lines[1]} )); then
+            # The first line of the file is not part of the diff.
+            REPLY=${lines[1]}
+        else
+            # The patch has no subject.
+            unset REPLY
+            return 0
+        fi
+    else
+        # The patch cannot be examined, or invalid arguments.
+        unset REPLY
+        return 1
+    fi
+}
diff --git a/Functions/VCS_Info/VCS_INFO_quilt b/Functions/VCS_Info/VCS_INFO_quilt
index c3c3d864d..381b58489 100644
--- a/Functions/VCS_Info/VCS_INFO_quilt
+++ b/Functions/VCS_Info/VCS_INFO_quilt
@@ -80,6 +80,10 @@ function VCS_INFO_quilt-dirfind() {
     return ${ret}
 }
 
+function VCS_INFO_quilt-patch2subject() {
+    VCS_INFO_patch2subject "$@"
+}
+
 function VCS_INFO_quilt() {
     emulate -L zsh
     setopt extendedglob
@@ -87,7 +91,7 @@ function VCS_INFO_quilt() {
     local patches pc tmp qstring root
     local -i ret
     local context
-    local -a applied unapplied all applied_string unapplied_string quiltcommand quilt_env
+    local -a applied unapplied applied_string unapplied_string quiltcommand quilt_env
     local -A hook_com
 
     context=":vcs_info:${vcs}.quilt-${mode}:${usercontext}:${rrn}"
@@ -119,7 +123,7 @@ function VCS_INFO_quilt() {
             applied=()
         fi
         patches=$(<$pc/.quilt_patches)
-        patches=`builtin cd -q "${pc:h}" && print -r - ${patches:A}`
+        patches=`builtin cd -q "${pc:h}" && print -r - ${patches:P}`
     fi
     if zstyle -t "${context}" get-unapplied; then
         # This zstyle call needs to be moved further up if `quilt' needs
@@ -147,27 +151,19 @@ function VCS_INFO_quilt() {
 
     if [[ -n $patches ]]; then
       () {
-        local i line
+        local i
         for ((i=1; i<=$#applied; i++)); do
-          if [[ -f "$patches/$applied[$i]" ]] &&
-             read -r line < "$patches/$applied[$i]" &&
-             [[ $line != (#b)(---|Index:)* ]] &&
-             true
-            ;
+          if VCS_INFO_quilt-patch2subject "$patches/$applied[$i]" && (( $+REPLY ))
           then
-            applied[$i]+=" $line"
+            applied[$i]+=" $REPLY"
           else
             applied[$i]+=" ?"
           fi
         done
         for ((i=1; i<=$#unapplied; i++)); do
-          if [[ -f "$patches/$unapplied[$i]" ]] &&
-             read -r line < "$patches/$unapplied[$i]" &&
-             [[ $line != (#b)(---|Index:)* ]] &&
-             true
-            ;
+          if VCS_INFO_quilt-patch2subject "$patches/$unapplied[$i]" && (( $+REPLY ))
           then
-            unapplied[$i]+=" $line"
+            unapplied[$i]+=" $REPLY"
           else
             unapplied[$i]+=" ?"
           fi
@@ -175,41 +171,15 @@ function VCS_INFO_quilt() {
       }
     fi
 
-    all=( ${(Oa)applied} ${unapplied} )
-
-    if VCS_INFO_hook 'gen-applied-string' "${applied[@]}"; then
-        if (( ${#applied} )); then
-            applied_string=${applied[1]}
-        else
-            applied_string=""
-        fi
-    else
-        applied_string=${hook_com[applied-string]}
-    fi
-    hook_com=()
-    if VCS_INFO_hook 'gen-unapplied-string' "${unapplied[@]}"; then
-        unapplied_string="${#unapplied}"
-    else
-        unapplied_string=${hook_com[unapplied-string]}
-    fi
-
-    if (( ${#applied} )); then
-        zstyle -s "${context}" patch-format qstring || qstring="%p (%n applied)"
-    else
-        zstyle -s "${context}" nopatch-format qstring || qstring="no patch applied"
-    fi
-    hook_com=( applied "${applied_string}" unapplied "${unapplied_string}"
-               applied-n ${#applied}       unapplied-n ${#unapplied}       all-n ${#all} )
-    if VCS_INFO_hook 'set-patch-format' ${qstring}; then
-        zformat -f qstring "${qstring}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
-                                        "n:${#applied}" "c:${#unapplied}" "a:${#all}"
-    else
-        qstring=${hook_com[patch-replace]}
-    fi
-    hook_com=()
+    VCS_INFO_set-patch-format 'applied' 'applied_string' \
+                              'unapplied' 'unapplied_string' \
+                              ${context} qstring \
+                              '' ''
+    qstring=$REPLY
 
     case ${mode} in
     (standalone)
+        backend_misc[patches]=${qstring}
         VCS_INFO_formats '' '' "${root}" '' '' '' "${qstring}"
         VCS_INFO_set
         ;;
diff --git a/Functions/VCS_Info/VCS_INFO_set-patch-format b/Functions/VCS_Info/VCS_INFO_set-patch-format
new file mode 100644
index 000000000..cdf2d303e
--- /dev/null
+++ b/Functions/VCS_Info/VCS_INFO_set-patch-format
@@ -0,0 +1,79 @@
+# This function is the common guts of the gen-applied-string /
+# gen-unapplied-string / set-patch-format dance of several backends.
+#
+# Parameters:
+# $1 - name of an array parameter to be the argument to gen-applied-string
+# $2 - name of a parameter to store the applied-string in
+# $3 - name of an array parameter to be the argument to gen-unapplied-string
+# $4 - name of a parameter to store the unapplied-string in
+# $5 - context argument for use in zstyle getters
+# $6 - name of a parameter to store a patch-format format string in
+# $7 - name of an assoc parameter with extra $hook_com key-value pairs for the
+#      set-patch-format hook invocation, or '' for none
+# $8 - name of an array parameter with extra arguments for the patch-format zformat call, or '' for empty
+#
+# The expanded patch-format string is returned in $REPLY.
+#
+# Output:
+# - $hook_com is overwritten and the keys 'applied', 'applied-n',
+#   'unapplied', 'unapplied-n', 'all-n' are set.
+{
+    local applied_needs_escaping='unknown'
+    local unapplied_needs_escaping='unknown'
+    if VCS_INFO_hook 'gen-applied-string' "${(@P)1}"; then
+        if (( ${(P)#1} )); then
+            REPLY=${(P)1[1]}
+        else
+            REPLY=""
+        fi
+        applied_needs_escaping='yes'
+    else
+        REPLY=${hook_com[applied-string]}
+    fi
+    : ${(P)2::=$REPLY}
+    hook_com=()
+
+    if VCS_INFO_hook 'gen-unapplied-string' "${(@P)3}"; then
+        REPLY=${(P)#3}
+        unapplied_needs_escaping='yes'
+    else
+        REPLY=${hook_com[unapplied-string]}
+    fi
+    : ${(P)4::=$REPLY}
+    hook_com=()
+
+    if (( ${(P)#1} )); then
+        zstyle -s "${5}" patch-format REPLY || REPLY="%p (%n applied)"
+    else
+        zstyle -s "${5}" nopatch-format REPLY || REPLY="no patch applied"
+    fi
+    : ${(P)6::=$REPLY}
+
+    hook_com=(
+      applied-n ${(P)#1}
+      applied "${(P)2}"
+      unapplied-n ${(P)#3}
+      unapplied "${(P)4}"
+    )
+    hook_com[all-n]=$(( ${hook_com[applied-n]} + ${hook_com[unapplied-n]} ))
+    hook_com+=( ${7:+"${(@kvP)7}"} )
+    if VCS_INFO_hook 'set-patch-format' "${(P)6}"; then
+        # Escape the value for use in $PS1
+        if [[ $applied_needs_escaping == 'yes' ]]; then
+          hook_com[applied]=${hook_com[applied]//'%'/%%}
+        fi
+        if [[ $unapplied_needs_escaping == 'yes' ]]; then
+          hook_com[unapplied]=${hook_com[unapplied]//'%'/%%}
+        fi
+
+        zformat -f REPLY "${(P)6}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
+                                        "n:${hook_com[applied-n]}" "c:${hook_com[unapplied-n]}" \
+                                        "a:${hook_com[all-n]}" \
+                                        ${8:+"${(@P)8}"}
+    else
+        unset applied_needs_escaping unapplied_needs_escaping # the hook deals with escaping
+        REPLY=${hook_com[patch-replace]}
+    fi
+    hook_com=()
+
+}
diff --git a/Functions/VCS_Info/vcs_info b/Functions/VCS_Info/vcs_info
index f13f6b501..4e9ac6c6a 100644
--- a/Functions/VCS_Info/vcs_info
+++ b/Functions/VCS_Info/vcs_info
@@ -19,9 +19,12 @@ static_functions=(
     VCS_INFO_check_com
     VCS_INFO_formats
     VCS_INFO_get_cmd
+    VCS_INFO_hexdump
     VCS_INFO_hook
+    VCS_INFO_set-patch-format
     VCS_INFO_maxexports
     VCS_INFO_nvcsformats
+    VCS_INFO_patch2subject
     VCS_INFO_quilt
     VCS_INFO_realpath
     VCS_INFO_reposub
diff --git a/Functions/Zle/bracketed-paste-magic b/Functions/Zle/bracketed-paste-magic
index 498cf554d..4baae823e 100644
--- a/Functions/Zle/bracketed-paste-magic
+++ b/Functions/Zle/bracketed-paste-magic
@@ -20,7 +20,7 @@
 # active-widgets
 #  Looked up in the context :bracketed-paste-magic to obtain a list of
 #  patterns that match widget names that should be activated during the
-#  paste.  All other key sequences are processed as self-insert-unmeta.
+#  paste.  All other key sequences are processed as "zle .self-insert".
 #  The default is 'self-*' so any user-defined widgets named with that
 #  prefix are active along with the builtin self-insert.  If this style is
 #  not set (note: it must be explicitly deleted after loading this
@@ -31,7 +31,7 @@
 #
 # inactive-keys
 #  This is the inverse of active-widgets, it lists key sequences that
-#  always use self-insert-unmeta even when bound to an active-widget.
+#  always use "zle .self-insert" even when bound to an active-widget.
 #  Note that this is a list of literal key sequences, not patterns.
 #  This style is in context :bracketed-paste-magic and has no default.
 #
@@ -145,27 +145,26 @@ bracketed-paste-magic() {
 	done
     fi
 
-    # Save context, create a clean slate for the paste
-    integer bpm_mark=$MARK bpm_cursor=$CURSOR bpm_region=$REGION_ACTIVE
-    integer bpm_numeric=${NUMERIC:-1}
-    local bpm_buffer=$BUFFER
-    fc -p -a /dev/null 0 0
-    BUFFER=
-
     zstyle -a :bracketed-paste-magic inactive-keys bpm_inactive
     if zstyle -s :bracketed-paste-magic active-widgets bpm_active '|'; then
-        # There are active widgets.  Reprocess $PASTED as keystrokes.
-	NUMERIC=1
-	zle -U - $PASTED
-
+	# Save context, create a clean slate for the paste
+	integer bpm_mark=$MARK bpm_region=$REGION_ACTIVE
+	integer bpm_numeric=${NUMERIC:-1}
+	integer bpm_limit=$UNDO_LIMIT_NO bpm_undo=$UNDO_CHANGE_NO
+	zle .split-undo
+	UNDO_LIMIT_NO=$UNDO_CHANGE_NO
+	BUFFER=
+	CURSOR=1
+	fc -p -a /dev/null 0 0
 	if [[ $bmp_keymap = vicmd ]]; then
 	    zle -K viins
 	fi
 
+	# There are active widgets.  Reprocess $PASTED as keystrokes.
+	NUMERIC=1
+	zle -U - $PASTED
+
 	# Just in case there are active undo widgets
-	zle .split-undo
-	integer bpm_limit=$UNDO_LIMIT_NO bpm_undo=$UNDO_CHANGE_NO
-	UNDO_LIMIT_NO=$UNDO_CHANGE_NO
 
 	while [[ -n $PASTED ]] && zle .read-command; do
 	    PASTED=${PASTED#$KEYS}
@@ -183,21 +182,16 @@ bracketed-paste-magic() {
 	done
 	PASTED=$BUFFER
 
-	# Reset the undo state
+	# Restore state
+	zle -K $bpm_keymap
+	fc -P
+	MARK=$bpm_mark
+	REGION_ACTIVE=$bpm_region
+	NUMERIC=$bpm_numeric
 	zle .undo $bpm_undo
 	UNDO_LIMIT_NO=$bpm_limit
-
-	zle -K $bpm_keymap
     fi
 
-    # Restore state
-    BUFFER=$bpm_buffer
-    MARK=$bpm_mark
-    CURSOR=$bpm_cursor
-    REGION_ACTIVE=$bpm_region
-    NUMERIC=$bpm_numeric
-    fc -P
-
     # PASTED has been updated, run the paste-finish functions
     if zstyle -a :bracketed-paste-magic paste-finish bpm_hooks; then
 	for bpm_func in $bpm_hooks; do
@@ -212,7 +206,7 @@ bracketed-paste-magic() {
 
     # Reprocess $PASTED as an actual paste this time
     zle -U - $PASTED$'\e[201~'	# append paste-end marker
-    zle .bracketed-paste
+    zle .bracketed-paste -- "$@"
     zle .split-undo
 
     # Arrange to display highlighting if necessary
diff --git a/Functions/Zle/bracketed-paste-url-magic b/Functions/Zle/bracketed-paste-url-magic
index 06dee2657..b894696bb 100644
--- a/Functions/Zle/bracketed-paste-url-magic
+++ b/Functions/Zle/bracketed-paste-url-magic
@@ -19,7 +19,7 @@
 # The default can be seen just below.
 
 local -a schema
-zstyle -a :bracketed-paste-url-magic schema schema || schema=(http https ftp ftps file ssh sftp)
+zstyle -a :bracketed-paste-url-magic schema schema || schema=(http:// https:// ftp:// ftps:// file:// ssh:// sftp:// magnet:)
 
 local wantquote=${NUMERIC:-0}
 local content
@@ -28,7 +28,7 @@ local start=$#LBUFFER
 zle .$WIDGET -N content
 
 if (( $wantquote == 0 )); then
-  if [[ $content = (${(~j:|:)schema})://* ]]; then
+  if [[ $content = (${(~j:|:)schema})* ]]; then
     wantquote=1
   fi
 fi
diff --git a/Functions/Zle/delete-whole-word-match b/Functions/Zle/delete-whole-word-match
index aece86065..3d52dd3d7 100644
--- a/Functions/Zle/delete-whole-word-match
+++ b/Functions/Zle/delete-whole-word-match
@@ -12,30 +12,29 @@ emulate -L zsh
 setopt extendedglob
 
 local curcontext=:zle:$WIDGET
-local -a matched_words
+local -A matched_words
 # Start and end of range of characters to remove.
 integer pos1 pos2
 
 autoload -Uz match-words-by-style
 match-words-by-style
 
-if [[ -n "${matched_words[3]}" ]]; then
-    # There's whitespace before the cursor, so the word we are deleting
-    # starts at the cursor position.
+if (( ${matched_words[is-word-start]} )); then
+    # The word we are deleting starts at the cursor position.
     pos1=$CURSOR
 else
-    # No whitespace before us, so delete any wordcharacters there.
-    pos1="${#matched_words[1]}"
+    # Not, so delete any wordcharacters before, too
+    pos1="${#matched_words[start]}"
 fi
 
-if [[ -n "${matched_words[4]}" ]]; then
+if [[ -n "${matched_words[ws-after-cursor]}" ]]; then
     # There's whitespace at the cursor position, so only delete
     # up to the cursor position.
     (( pos2 = CURSOR + 1 ))
 else
     # No whitespace at the cursor position, so delete the
     # current character and any following wordcharacters.
-    (( pos2 = CURSOR + ${#matched_words[5]} + 1 ))
+    (( pos2 = CURSOR + ${#matched_words[word-after-cursor]} + 1 ))
 fi
 
 # Move the cursor then delete the block in one go for the
diff --git a/Functions/Zle/expand-absolute-path b/Functions/Zle/expand-absolute-path
index b85757600..4887f3c60 100644
--- a/Functions/Zle/expand-absolute-path
+++ b/Functions/Zle/expand-absolute-path
@@ -10,7 +10,7 @@ autoload -Uz modify-current-argument
 if (( ! ${+functions[glob-expand-absolute-path]} )); then
   glob-expand-absolute-path() {
     local -a files
-    files=(${~1}(N:A))
+    files=(${~1}(N:P))
     (( ${#files} )) || return
     REPLY=${(D)files[1]}
   }
diff --git a/Functions/Zle/history-beginning-search-menu b/Functions/Zle/history-beginning-search-menu
index 105518102..0e1bbc734 100644
--- a/Functions/Zle/history-beginning-search-menu
+++ b/Functions/Zle/history-beginning-search-menu
@@ -112,7 +112,7 @@ fi
 # go to the last one.  This allows accept-line-and-down-history etc.
 # to work.
 local -a lines
-local matchq=${matches[$chars]//(#m)[\][()\\*?#<>~^]/\\$MATCH}
+local matchq=${matches[$chars]//(#m)[\][|()\\*?#<>~^]/\\$MATCH}
 lines=(${(kon)history[(R)$matchq]})
 HISTNO=$lines[-1]
 
diff --git a/Functions/Zle/insert-unicode-char b/Functions/Zle/insert-unicode-char
index af9aad914..b943fb7f7 100644
--- a/Functions/Zle/insert-unicode-char
+++ b/Functions/Zle/insert-unicode-char
@@ -12,11 +12,11 @@ then
   local -i 16 -Z 10 arg=$NUMERIC
   # ...and use print to turn this into a Unicode character.
   LBUFFER+="$(print -n "\U${arg##0x}")"
-  _insert_unicode_ready=0
+  integer -g _insert_unicode_ready=0
 else
   # Set the base to 16...
   zle argument-base 16
   # ...wait for user to type hex keys then call this widget again.
   zle universal-argument
-  _insert_unicode_ready=1
+  integer -g _insert_unicode_ready=1
 fi
diff --git a/Functions/Zle/match-words-by-style b/Functions/Zle/match-words-by-style
index 54e019d23..fc59c2764 100644
--- a/Functions/Zle/match-words-by-style
+++ b/Functions/Zle/match-words-by-style
@@ -5,8 +5,16 @@
 #    <whitespace-after-cursor> <word-after-cursor> <whitespace-after-word>
 #    <stuff-at-end>
 # where the cursor position is always after the third item and `after'
-# is to be interpreted as `after or on'.  Some
-# of the array elements will be empty; this depends on the style.
+# is to be interpreted as `after or on'.
+#
+# matched_words may be an associative array, in which case the
+# values above are now given by the elements named start, word-before-cursor,
+# ws-before-cursor, ws-after-cursor, word-after-cursor, ws-after-word,
+# end.  In addition, the element is-word-start is 1 if the cursor
+# is on the start of a word; this is non-trivial in the case of subword
+# (camel case) matching as there may be no white space to test.
+#
+# Some of the array elements will be empty; this depends on the style.
 # For example
 #    foo bar  rod stick
 #            ^
@@ -202,7 +210,7 @@ if [[ $wordstyle = *subword* ]]; then
   # followed by a lower case letter, or an upper case letter at
   # the start of a group of upper case letters.  To make
   # it easier to be consistent, we just use anything that
-  # isn't an upper case characer instead of a lower case
+  # isn't an upper case character instead of a lower case
   # character.
   # Here the initial "*" will match greedily, so we get the
   # last such match, as we want.
@@ -224,11 +232,18 @@ charskip=${(l:skip::?:)}
 
 eval pat2='${RBUFFER##(#b)('${charskip}${spacepat}')('\
 ${wordpat2}')('${spacepat}')}'
+if [[ -n $match[2] ]]; then
+  ws2=$match[1]
+  word2=$match[2]
+  ws3=$match[3]
+else
+  # No more words, so anything left is white space after cursor.
+  ws2=$RBUFFER
+  pat2=
+fi
 
-ws2=$match[1]
-word2=$match[2]
-ws3=$match[3]
-
+integer wordstart
+[[ ( -n $ws1 || -n $ws2 ) && -n $word2 ]] && wordstart=1
 if [[ $wordstyle = *subword* ]]; then
   # Do we have a group of upper case characters at the start
   # of word2 (that don't form the entire word)?
@@ -237,12 +252,19 @@ if [[ $wordstyle = *subword* ]]; then
 	  -n $match[2] ]]; then
     # Yes, so the last one is new word boundary.
     (( epos = ${#match[1]} - 1 ))
+    # Otherwise, are we in the middle of a word?
+    # In other, er, words, we've got something on the left with no
+    # white space following and something that doesn't start a word here.
+  elif [[ -n $word1 && -z $ws1 && -z $ws2 && \
+    $word2 = (#b)([^${~subwordrange}]##)* ]]; then
+    (( epos = ${#match[1]} ))
     # Otherwise, do we have upper followed by non-upper not
     # at the start?  Ignore the initial character, we already
     # know it's a word boundary so it can be an upper case character
     # if it wants.
   elif [[ $word2 = (#b)(?[^${~subwordrange}]##)[${~subwordrange}]* ]]; then
     (( epos = ${#match[1]} ))
+    (( wordstart = 1 ))
   else
     (( epos = 0 ))
   fi
@@ -256,4 +278,19 @@ if [[ $wordstyle = *subword* ]]; then
   fi
 fi
 
-matched_words=("$pat1" "$word1" "$ws1" "$ws2" "$word2" "$ws3" "$pat2")
+# matched_words should be local to caller.
+# Just fix type here.
+if [[ ${(t)matched_words} = *association* ]]; then
+  matched_words=(
+    start              "$pat1"
+    word-before-cursor "$word1"
+    ws-before-cursor   "$ws1"
+    ws-after-cursor    "$ws2"
+    word-after-cursor  "$word2"
+    ws-after-word      "$ws3"
+    end                "$pat2"
+    is-word-start      $wordstart
+  )
+else
+  matched_words=("$pat1" "$word1" "$ws1" "$ws2" "$word2" "$ws3" "$pat2")
+fi
diff --git a/Functions/Zle/select-bracketed b/Functions/Zle/select-bracketed
index 00f51be2c..d467bb804 100644
--- a/Functions/Zle/select-bracketed
+++ b/Functions/Zle/select-bracketed
@@ -12,6 +12,8 @@
 #	done
 #     done
 
+setopt localoptions noksharrays
+
 local style=${${1:-$KEYS}[1]} matching="(){}[]<>bbBB"
 local -i find=${NUMERIC:-1} idx=${matching[(I)[${${1:-$KEYS}[2]}]]}%9
 (( idx )) || return 1 # no corresponding closing bracket
diff --git a/Functions/Zle/select-word-match b/Functions/Zle/select-word-match
new file mode 100644
index 000000000..8440852ab
--- /dev/null
+++ b/Functions/Zle/select-word-match
@@ -0,0 +1,120 @@
+# Select the entire word around the cursor. Intended for use as
+# a vim-style text object in vi mode but with customisable
+# word boundaries.
+#
+# For example:
+#   autoload -U select-word-match
+#   zle -N select-in-camel select-word-match
+#   bindkey -M viopp ic select-in-camel
+#   zstyle ':zle:*-camel' word-style normal-subword
+
+emulate -L zsh
+setopt extendedglob
+
+local curcontext=:zle:$WIDGET
+local -A matched_words
+# Start and end of range of characters
+integer pos1 pos2 num=${NUMERIC:-1}
+local style word
+
+# choose between inner word or a word style of widget
+for style in $1 ${${WIDGET#*-}[1]} $KEYS[1] "i"; do
+  [[ $style = [ai] ]] && break
+done
+
+autoload -Uz match-words-by-style
+
+while (( num-- )); do
+  if (( MARK > CURSOR )); then
+    # if cursor is at the start of the selection, just move back a word
+    match-words-by-style
+    if [[ $style = i && -n $matched_words[ws-before-cursor] ]]; then
+      word=$matched_words[ws-before-cursor]
+    else
+      word=$matched_words[word-before-cursor]$matched_words[ws-before-cursor]
+    fi
+    if [[ -n $word ]]; then
+      (( CURSOR -= ${#word} ))
+    else
+      return 1
+    fi
+  elif (( MARK >= 0 && MARK < CURSOR )); then
+    # cursor at the end, move forward a word
+    (( CURSOR+1 == $#BUFFER )) && return 1
+    (( CURSOR++ ))
+    match-words-by-style
+    if [[ -n $matched_words[ws-after-cursor] ]]; then
+      if [[ $style = i ]]; then
+	# just skip the whitespace
+	word=$matched_words[ws-after-cursor]
+      else
+	# skip the whitespace plus word
+	word=$matched_words[ws-after-cursor]$matched_words[word-after-cursor]
+      fi
+    else
+      if [[ $style = i ]]; then
+	# skip the word
+	word=$matched_words[word-after-cursor]
+      else
+	# skip word and following whitespace
+	word=$matched_words[word-after-cursor]$matched_words[ws-after-word]
+      fi
+    fi
+    (( CURSOR += ${#word} - 1 ))
+  else
+    match-words-by-style
+
+    if (( ${matched_words[is-word-start]} )); then
+      # The word we are selecting starts at the cursor position.
+      pos1=$CURSOR
+    else
+      # No whitespace before us, so select any wordcharacters there.
+      pos1="${#matched_words[start]}"
+    fi
+
+    if [[ -n "${matched_words[ws-after-cursor]}" ]]; then
+      if [[ -n "${matched_words[ws-before-cursor]}" ]] || (( CURSOR == 0 )); then
+        # whitespace either side, select it
+	(( pos1 = CURSOR - ${#matched_words[ws-before-cursor]} ))
+	(( pos2 = CURSOR + ${#matched_words[ws-after-cursor]} ))
+      else
+	# There's whitespace at the cursor position, so only select
+	# up to the cursor position.
+	(( pos2 = CURSOR + 1 ))
+      fi
+    else
+      # No whitespace at the cursor position, so select the
+      # current character and any following wordcharacters.
+      (( pos2 = CURSOR + ${#matched_words[word-after-cursor]} ))
+    fi
+
+    if [[ $style = a ]]; then
+      if [[ -n "${matched_words[ws-after-cursor]}"  && ( -n "${matched_words[ws-before-cursor]}" || CURSOR -eq 0 ) ]]; then
+	# in the middle of whitespace so grab a word
+	if [[ -n "${matched_words[word-after-cursor]}" ]]; then
+	  (( pos2 += ${#matched_words[word-after-cursor]} )) # preferably the one after
+	else
+	  (( pos1 -= ${#matched_words[word-before-cursor]} )) # otherwise the one before
+	fi
+      elif [[ -n "${matched_words[ws-after-word]}" ]]; then
+	(( pos2 += ${#matched_words[ws-after-word]} ))
+      elif [[ -n "${matched_words[ws-before-cursor]}" ]]; then
+	# couldn't grab whitespace forwards so try backwards
+	(( pos1 -= ${#matched_words[ws-before-cursor]} ))
+      elif (( pos1 > 0 )); then
+	# There might have been whitespace before the word
+	(( CURSOR = pos1 ))
+	match-words-by-style
+	if [[ -n "${matched_words[ws-before-cursor]}" ]]; then
+	  (( pos1 -= ${#matched_words[ws-before-cursor]} ))
+	fi
+      fi
+    fi
+
+    (( MARK = pos1, CURSOR = pos2-1 ))
+  fi
+done
+
+if [[ $KEYMAP == vicmd ]] && (( !REGION_ACTIVE )); then
+  (( CURSOR++ )) # Need to include cursor position for operators
+fi
diff --git a/Functions/Zle/surround b/Functions/Zle/surround
index b7be30b75..b51b77c04 100644
--- a/Functions/Zle/surround
+++ b/Functions/Zle/surround
@@ -19,6 +19,7 @@ local before after
 local -A matching
 matching=( \( \) \{ \} \< \> \[ \] )
 
+zle -f vichange
 case $WIDGET in
   change-*)
     local MARK="$MARK" CURSOR="$CURSOR" call
@@ -69,7 +70,11 @@ case $WIDGET in
       before="${(k)matching[(r)[$before:q]]}"
     fi
     CUTBUFFER="$before$CUTBUFFER$after"
-    zle .vi-put-after -n 1
+    if [[ CURSOR -eq 0 || $BUFFER[CURSOR] = $'\n' ]]; then
+      zle .vi-put-before -n 1
+    else
+      zle .vi-put-after -n 1
+    fi
     CUTBUFFER="$save_cut" CURSOR="$save_cur"
   ;;
 esac
diff --git a/Functions/Zle/vi-pipe b/Functions/Zle/vi-pipe
new file mode 100644
index 000000000..1729cb6e1
--- /dev/null
+++ b/Functions/Zle/vi-pipe
@@ -0,0 +1,39 @@
+# Example of a widget that takes a vi motion
+
+# Filter part of buffer corresponding to a vi motion through an external
+# program.
+
+# To enable with vi compatible bindings use:
+#   autoload -Uz vi-pipe
+#   bindkey -a '!' vi-pipe
+
+setopt localoptions noksharrays
+
+autoload -Uz read-from-minibuffer
+local _save_cut="$CUTBUFFER" REPLY
+
+# mark this widget as a vi change so it can be repeated as a whole
+zle -f vichange
+
+# force movement to default to line mode
+(( REGION_ACTIVE )) || zle -U V
+# Use the standard vi-change to accept a vi motion.
+zle .vi-change || return
+read-from-minibuffer "!"
+zle .vi-cmd-mode
+local _save_cur=$CURSOR
+
+# cut buffer contains the deleted text and can be modified
+CUTBUFFER=$(eval "$REPLY" <<<"$CUTBUFFER")
+
+# put the modified text back in position.
+if [[ CURSOR -eq 0 || $BUFFER[CURSOR] = $'\n' ]]; then
+  # at the beginning of a line, vi-delete won't have moved the cursor
+  # back to a previous line
+  zle .vi-put-before -n 1
+else
+  zle .vi-put-after -n 1
+fi
+
+# restore cut buffer and cursor to the start of the range
+CUTBUFFER="$_save_cut" CURSOR="$_save_cur"
diff --git a/Functions/Zle/zcalc-auto-insert b/Functions/Zle/zcalc-auto-insert
index c9a5c8867..e1affd1c3 100644
--- a/Functions/Zle/zcalc-auto-insert
+++ b/Functions/Zle/zcalc-auto-insert
@@ -1,6 +1,7 @@
 # Bind to a binary operator keystroke for use with zcalc
+# Not useful in RPN mode.
 
-if [[ -n $ZCALC_ACTIVE ]]; then
+if [[ -n $ZCALC_ACTIVE && $ZCALC_ACTIVE != rpn ]]; then
   if [[ $CURSOR -eq 0 || $LBUFFER[-1] = "(" ]]; then
     LBUFFER+=${ZCALC_AUTO_INSERT_PREFIX:-"ans "}
   fi