summary refs log tree commit diff
path: root/Functions/Misc
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/Misc')
-rw-r--r--Functions/Misc/.distfiles3
-rw-r--r--Functions/Misc/allopt28
-rw-r--r--Functions/Misc/checkmail26
-rw-r--r--Functions/Misc/colors79
-rw-r--r--Functions/Misc/harden6
-rw-r--r--Functions/Misc/is-at-least38
-rw-r--r--Functions/Misc/mere83
-rw-r--r--Functions/Misc/nslookup48
-rw-r--r--Functions/Misc/run-help88
-rw-r--r--Functions/Misc/zed70
-rw-r--r--Functions/Misc/zkbd248
-rw-r--r--Functions/Misc/zmv268
-rw-r--r--Functions/Misc/zrecompile244
-rw-r--r--Functions/Misc/zstyle+35
14 files changed, 1264 insertions, 0 deletions
diff --git a/Functions/Misc/.distfiles b/Functions/Misc/.distfiles
index 9e276f99b..414abad59 100644
--- a/Functions/Misc/.distfiles
+++ b/Functions/Misc/.distfiles
@@ -1,3 +1,6 @@
 DISTFILES_SRC='
 .distfiles
+allopt       harden       nslookup     zkbd         zstyle+
+checkmail    is-at-least  run-help     zmv
+colors       mere         zed          zrecompile
 '
diff --git a/Functions/Misc/allopt b/Functions/Misc/allopt
new file mode 100644
index 000000000..0d59fa3e9
--- /dev/null
+++ b/Functions/Misc/allopt
@@ -0,0 +1,28 @@
+# This function lists options with the no's in front removed for
+# improved comprehension, i.e. `norcs off' becomes `rcs on'.
+# The format is otherwise like that with `kshoptionprint' set,
+# i.e. you can see all options whether on or off.
+# It can take a list of option names or parts thereof to search for
+# via egrep.
+#
+# Written by Sweth Chandramouli with hacks by Bart Schaefer.
+
+listalloptions () {
+   builtin setopt localoptions kshoptionprint
+   local OPT_NAME OPT_PAIR OPT_VALUE
+   for OPT_PAIR in "${(f)$(builtin setopt)}" ; do
+      OPT_VALUE=${OPT_PAIR##* }
+      OPT_NAME=${OPT_PAIR%% *}
+      if [[ ${OPT_NAME#no} != ${OPT_NAME} ]] ; then
+	 OPT_VALUE=${(L)${${OPT_VALUE:s/on/OFF}:s/off/on}}
+	 OPT_NAME=${OPT_NAME#no}
+      fi
+      echo "${(r:21:)OPT_NAME} ${OPT_VALUE}"
+   done
+}
+
+if [[ -n $@ ]]; then
+    listalloptions | egrep "${(j.|.)@}"
+else
+    listalloptions
+fi
diff --git a/Functions/Misc/checkmail b/Functions/Misc/checkmail
new file mode 100644
index 000000000..9cc743db4
--- /dev/null
+++ b/Functions/Misc/checkmail
@@ -0,0 +1,26 @@
+#! /usr/local/bin/zsh
+#
+# This autoloadable function checks the folders specified as arguments
+# for new mails.  The arguments are interpeted in exactly the same way
+# as the mailpath special zsh parameter (see zshparam(1)).
+#
+# If no arguments are given mailpath is used.  If mailpath is empty, $MAIL
+# is used and if that is also empty, /var/spool/mail/$LOGNAME is used.
+# This function requires zsh-3.0.1 or newer.
+#
+
+local file message
+
+for file in "${@:-${mailpath[@]:-${MAIL:-/var/spool/mail/$LOGNAME}}}"
+do
+	message="${${(M)file%%\?*}#\?}"
+	file="${file%%\?*}"
+	if [[ -d "$file" ]] then
+		file=( "$file"/**/*(.ND) )
+		if (($#file)) then
+			checkmail "${^file}\?$message"
+		fi
+	elif test -s "$file" -a -N "$file"; then  # this also sets $_ to $file
+		print -r -- "${(e)message:-You have new mail.}"
+	fi
+done
diff --git a/Functions/Misc/colors b/Functions/Misc/colors
new file mode 100644
index 000000000..6778cbd49
--- /dev/null
+++ b/Functions/Misc/colors
@@ -0,0 +1,79 @@
+# Put standard ANSI color codes in shell parameters for easy use.
+# Note that some terminals do not support all combinations.
+
+typeset -Ag color colour
+
+color=(
+# Attribute codes:
+  00 none
+  01 bold
+  02 faint                  22 normal
+  03 standout               23 no-standout
+  04 underline              24 no-underline
+  05 blink                  25 no-blink
+  07 reverse                27 no-reverse
+  08 conceal
+
+# Text color codes:
+  30 black                  40 bg-black
+  31 red                    41 bg-red
+  32 green                  42 bg-green
+  33 yellow                 43 bg-yellow
+  34 blue                   44 bg-blue
+  35 magenta                45 bg-magenta
+  36 cyan                   46 bg-cyan
+  37 white                  47 bg-white
+  39 default                49 bg-default
+)
+
+# A word about black and white:  The "normal" shade of white is really a
+# very pale grey on many terminals; to get truly white text, you have to
+# use bold white, and to get a truly white background you have to use
+# bold reverse white bg-xxx where xxx is your desired foreground color
+# (and which means the foreground is also bold).
+
+# Map in both directions; could do this with e.g. ${(k)colors[(i)normal]},
+# but it's clearer to include them all both ways.
+
+local k
+for k in ${(k)color}; do color[${color[$k]}]=$k; done
+
+# Add "fg-" keys for all the text colors, for clarity.
+
+for k in ${color[(I)3?]}; do color[fg-${color[$k]}]=$k; done
+
+# This is inaccurate, but the prompt theme system needs it.
+
+color[grey]=${color[black]}
+color[fg-grey]=${color[grey]}
+color[bg-grey]=${color[bg-black]}
+
+# Assistance for the color-blind.
+
+colour=(${(kv)color})	# A case where ksh namerefs would be useful ...
+
+# The following are terminal escape sequences used by colored prompt themes.
+
+local lc=$'\e[' rc=m	# Standard ANSI terminal escape values
+
+typeset -Hg reset_color bold_color
+reset_color="$lc${color[none]}$rc"
+bold_color="$lc${color[bold]}$rc"
+
+# Foreground
+
+typeset -AHg fg fg_bold fg_no_bold
+for k in ${(k)color[(I)fg-*]}; do
+    fg[${k#fg-}]="$lc${color[$k]}$rc"
+    fg_bold[${k#fg-}]="$lc${color[bold]};${color[$k]}$rc"
+    fg_no_bold[${k#fg-}]="$lc${color[normal]};${color[$k]}$rc"
+done
+
+# Background
+
+typeset -AHg bg bg_bold bg_no_bold
+for k in ${(k)color[(I)bg-*]}; do
+    bg[${k#bg-}]="$lc${color[$k]}$rc"
+    bg_bold[${k#bg-}]="$lc${color[bold]};${color[$k]}$rc"
+    bg_no_bold[${k#bg-}]="$lc${color[normal]};${color[$k]}$rc"
+done
diff --git a/Functions/Misc/harden b/Functions/Misc/harden
new file mode 100644
index 000000000..c02689362
--- /dev/null
+++ b/Functions/Misc/harden
@@ -0,0 +1,6 @@
+#! /bin/sh
+# harden a link (convert it to a singly linked file)
+cp $1 $1.foo
+rm $1
+mv $1.foo $1
+
diff --git a/Functions/Misc/is-at-least b/Functions/Misc/is-at-least
new file mode 100644
index 000000000..6debe4c5a
--- /dev/null
+++ b/Functions/Misc/is-at-least
@@ -0,0 +1,38 @@
+#
+# Test whether $ZSH_VERSION (or some value of your choice, if a second argument
+# is provided) is greater than or equal to x.y.z-r (in argument one). In fact,
+# it'll accept any dot/dash-separated string of numbers as its second argument
+# and compare it to the dot/dash-separated first argument. Leading non-number
+# parts of a segment (such as the "zefram" in 3.1.2-zefram4) are not considered
+# when the comparison is done; only the numbers matter. Any left-out segments
+# in the first argument that are present in the version string compared are
+# considered as zeroes, eg 3 == 3.0 == 3.0.0 == 3.0.0.0 and so on.
+#
+# Usage examples:
+# is-at-least 3.1.6-15 && setopt NO_GLOBAL_RCS
+# is-at-least 3.1.0 && setopt HIST_REDUCE_BLANKS
+# is-at-least 586 $MACHTYPE && echo 'You could be running Mandrake!'
+# is-at-least $ZSH_VERSION || print 'Something fishy here.'
+
+emulate zsh ; setopt LOCAL_OPTIONS
+
+local IFS=".-" min_cnt=0 ver_cnt=0 part min_ver version
+
+min_ver=(${=1})
+version=(${=2:-$ZSH_VERSION} 0)
+
+while (( $min_cnt <= ${#min_ver} )); do
+  while [[ "$part" != <-> ]]; do
+    (( ++ver_cnt > ${#version} )) && return 0
+    part=${version[ver_cnt]##*[^0-9]}
+  done
+
+  while true; do
+    (( ++min_cnt > ${#min_ver} )) && return 0
+    [[ ${min_ver[min_cnt]} = <-> ]] && break
+  done
+
+  (( part > min_ver[min_cnt] )) && return 0
+  (( part < min_ver[min_cnt] )) && return 1
+  part=''
+done
diff --git a/Functions/Misc/mere b/Functions/Misc/mere
new file mode 100644
index 000000000..a9beb2c10
--- /dev/null
+++ b/Functions/Misc/mere
@@ -0,0 +1,83 @@
+# read a man page
+
+setopt localoptions extendedglob
+
+local manual="$1" col=col terminal=man magic line
+
+# /usr/bin/col on SunOS 4 doesn't support -x.
+if [[ -x /usr/5bin/col ]]; then
+  col=/usr/5bin/col;
+fi
+
+# SunOS 5 has no `man' terminal.
+if [[ -d /usr/share/lib/nterm &&
+    ! -e /usr/share/lib/nterm/tab.$terminal ]]; then
+  terminal=lp;
+fi
+
+# HP-UX has no `man' terminal.
+if [[ -d /usr/share/lib/term &&
+    ! -e /usr/share/lib/term/tab$terminal ]]; then
+  terminal=lp;
+fi
+
+# Unixware has no `man' terminal.
+if [[ -d /usr/ucblib/doctools/nterm &&
+    ! -e /usr/ucblib/doctools/nterm/tab.$terminal ]]; then
+  terminal=lp;
+fi
+
+# Solaris has SGML manuals.
+if [[ -f /usr/lib/sgml/sgml2roff ]] && 
+   [[ "$(read -er < $manual)" = "<!DOCTYPE"* ]]; then
+  /usr/lib/sgml/sgml2roff $manual | {
+    read -r line
+    if [[ $line = ".so "* ]]; then
+      # There is no cascading .so directive.
+      # On Solaris 7, at least.
+      /usr/lib/sgml/sgml2roff ${line#.so }
+    else
+      print -lr - "$line"
+      cat
+    fi
+  }
+else
+  read -u0 -k 2 magic < $manual
+  case $magic in
+  $'\037\235') zcat $manual;;
+  $'\037\213') gzip -dc $manual;;
+  *) cat $manual;;
+  esac
+fi | (
+  # cd is required to work soelim called by nroff.
+  case $manual in
+  */man/man*/*) cd ${manual:h:h};;
+  */man/sman*/*) cd ${manual:h:h};;
+  esac
+  read -r line
+  # The first line beginning with '\" shows preprocessors.
+  # Unknown preprocessors is ignored.
+  if [[ $line = "'\\\" "* ]]; then
+    typeset -A filter
+    filter=(
+      e neqn
+      g grap
+      p pic
+      r refer
+      t tbl
+      v vgrind
+    )
+    eval ${(j:|:)${${(s::)line#\'\\\" }//(#m)?/$filter[$MATCH]}}
+  elif [[ $line = "'\\\"! "* ]]; then
+    typeset -A filter
+    filter=(
+      eqn neqn
+    )
+    eval ${(j:|:)${${${${(s:|:)line#\'\\\"! }# ##}% ##}//(#m)*/$filter[$MATCH]}}
+  else
+    print -lr - "$line"
+    cat
+  fi |
+  nroff -T$terminal -man | $col -x
+) |
+${=MANPAGER:-${PAGER:-more}} -s
diff --git a/Functions/Misc/nslookup b/Functions/Misc/nslookup
new file mode 100644
index 000000000..5b49ae9d2
--- /dev/null
+++ b/Functions/Misc/nslookup
@@ -0,0 +1,48 @@
+# Simple wrapper function for `nslookup'. With completion if you are using
+# the function based completion system.
+
+if [[ $argv[(I)-] -eq 0 && $argv[(I)[^-]*] -ne 0 ]]; then
+  command nslookup "$@"
+  return
+fi
+
+setopt localoptions localtraps completealiases
+
+local tmp line compcontext=nslookup curcontext='nslookup:::' pmpt
+local pager opager="$PAGER"
+typeset +g -x PAGER=cat
+
+zmodload -e zsh/zpty || zmodload -i zsh/zpty
+
+trap 'return 130' INT
+trap 'zpty -d nslookup' EXIT
+
+pmpt=()
+zstyle -s ':nslookup' prompt tmp && pmpt=(-p "$tmp")
+zstyle -s ':nslookup' rprompt tmp && pmpt=("$pmpt[@]" -r "$tmp")
+zstyle -s ':nslookup' pager tmp &&
+    [[ -z "$pager" ]] && pager="${opager:-more}"
+(( $#pmpt )) || pmpt=(-p '> ')
+
+zpty nslookup command nslookup "${(q)@}"
+
+zpty -r nslookup line '*
+> '
+print -nr "$line"
+
+while line=''; vared -he "$pmpt[@]" line; do
+  print -s "$line"
+  [[ "$line" = exit ]] && break
+
+  zpty -w nslookup "$line"
+
+  zpty -r nslookup line '*
+> '
+  if [[ -n "$pager" && ${#${(f)line}} -gt LINES ]]; then
+    print -nr "$line" | eval "$pager"
+  else
+    print -nr "$line"
+  fi
+done
+
+zpty -w nslookup 'exit'
diff --git a/Functions/Misc/run-help b/Functions/Misc/run-help
new file mode 100644
index 000000000..4f447b9f0
--- /dev/null
+++ b/Functions/Misc/run-help
@@ -0,0 +1,88 @@
+#!/usr/local/bin/zsh
+#
+# Figure out where to get the best help, and get it.
+#
+# Install this function by placing it in your FPATH and then
+# adding to your .zshrc the lines:
+#	unalias run-help
+#	autoload run-help
+#
+
+emulate -R zsh
+setopt localoptions
+
+[[ $1 == "." ]] && 1="dot"
+[[ $1 == ":" ]] && 1="colon"
+
+# Check whether Util/helpfiles has been used to generate zsh help
+if [[ $# == 0 || $1 == "-l" ]]
+then
+    if [[ -n "${HELPDIR:-}" && -d $HELPDIR ]]
+    then
+	echo "Here is a list of topics for which special help is available:"
+	echo ""
+	print -rc $HELPDIR/*(:t)
+    else
+	echo "There is no list of special help topics available at this time."
+    fi
+    return 0
+elif [[ -n "${HELPDIR:-}" && -r $HELPDIR/$1 && $1 != compctl ]]
+then
+    ${=PAGER:-more} $HELPDIR/$1
+    return $?
+fi
+
+# No zsh help; use "whence" to figure out where else we might look
+local what places newline='
+'
+integer i=0 didman=0
+
+places=( "${(@f)$(builtin whence -va $1)}" )
+
+while ((i++ < $#places))
+do
+    what=$places[$i]
+    builtin print -r $what
+    case $what in
+    (*( is an alias)*)
+	[[ ${what[(w)6]:t} != ${what[(w)1]} ]] && run-help ${what[(w)6]:t}
+	;;
+    (*( is a * function))
+	case ${what[(w)1]} in
+	(comp*) man zshcompsys;;
+	(zf*) man zshftpsys;;
+	(*) builtin functions ${what[(w)1]} | ${=PAGER:-more};;
+	esac;;
+    (*( is a * builtin))
+	case ${what[(w)1]} in
+	(compctl) man zshcompctl;;
+	(comp*) man zshcompwid;;
+	(bindkey|vared|zle) man zshzle;;
+	(*setopt) man zshoptions;;
+	(cap|getcap|setcap) ;&
+	(clone) ;&
+	(ln|mkdir|mv|rm|rmdir|sync) ;&
+	(sched) ;&
+	(stat) man zshmodules;;
+	(zftp) man zshftpsys;;
+	(*) man zshbuiltins;;
+	esac
+	;;
+    (*( is hashed to *))
+	man ${what[(w)-1]:t}
+	;;
+    (*( is a reserved word))
+	man zshmisc
+	;;
+    (*)
+	((! didman++)) && man $@
+	;;
+    esac
+    if ((i < $#places && ! didman))
+    then
+	builtin print -nP "%SPress any key for more help or q to quit%s"
+	builtin read -k what
+	[[ $what != $newline ]] && echo
+	[[ $what == [qQ] ]] && break
+    fi
+done
diff --git a/Functions/Misc/zed b/Functions/Misc/zed
new file mode 100644
index 000000000..3cee176a1
--- /dev/null
+++ b/Functions/Misc/zed
@@ -0,0 +1,70 @@
+#
+# zed
+#
+# No other shell could do this.
+# Edit small files with the command line editor.
+# Use ^X^W to save, ^C to abort.
+# Option -f: edit shell functions.  (Also if called as fned.)
+#
+# Completion: use
+# compctl -f -x 'w[1,-f]' -F -- zed
+#
+
+local var fun cleanup
+# We do not want timeout while we are editing a file
+integer TMOUT=0
+
+[[ $1 = -f || $0 = fned ]] && fun=1
+[[ $1 = -(|-|f) ]] && shift
+
+[[ -z "$1" ]] && echo 'Usage: "zed filename" or "zed -f function"' && return 1
+
+local curcontext=zed:::
+
+zstyle -m ":completion:zed:*" insert-tab '*' ||
+    zstyle ":completion:zed:*" insert-tab yes
+
+# catch interrupts
+cleanup="$(bindkey -L "^M"; bindkey -L -M emacs "^X^W"; bindkey -aL "ZZ"
+    echo "trap - INT EXIT"; trap)"
+trap "return 130" INT
+trap "$cleanup" EXIT
+
+# don't mangle !'s
+setopt localoptions nobanghist
+
+bindkey "^M" self-insert-unmeta
+# Depending on your stty's, you may be able to use ^J as accept-line, else:
+bindkey -M emacs "^X^W" accept-line
+bindkey -a "ZZ" accept-line
+
+if ((fun)) then
+  var="$(functions $1)"
+  # If function is undefined but autoloadable, load it
+  if [[ $var = *\#\ undefined* ]] then
+    local dir
+    for dir in $fpath; do
+      if [[ -f $dir/$1 ]] then
+	var="$1() {
+$(<$dir/$1)
+}"
+	break
+      fi
+    done
+  elif [[ -z $var ]] then
+    var="$1() {
+}"
+  fi
+  vared var && eval "$cleanup ;" function "$var"
+else
+  [[ -f $1 ]] && var="$(<$1)"
+  while vared var
+  do
+    (print -r -- "$var" >| $1) && break
+    echo -n -e '\a'
+  done
+fi
+
+return 0
+
+# End of zed
diff --git a/Functions/Misc/zkbd b/Functions/Misc/zkbd
new file mode 100644
index 000000000..30cb4a248
--- /dev/null
+++ b/Functions/Misc/zkbd
@@ -0,0 +1,248 @@
+#! /bin/zsh -f
+
+[[ -o interactive ]] && {
+    local -i ARGC
+    (ARGC=0) 2>/dev/null || {
+        print -u2 ${0}: must be run as a function or shell script, not sourced
+        return 1
+    }
+}
+
+emulate -RL zsh
+local zkbd term key seq
+
+zkbd=${ZDOTDIR:-$HOME}/.zkbd
+[[ -d $zkbd ]] || mkdir $zkbd || return 1
+
+print 'typeset -g -A key\n' > $zkbd/$TERM.tmp || return 1
+trap "unfunction getkey getseq; command rm -f $zkbd/$TERM.tmp" 0
+trap "return 1" 1 2 15
+
+getkey () {
+    local k='' i
+    for ((i=10; i>0; --i))
+    do
+	read -t -k 1 k && break
+	sleep 1
+    done
+    [[ -n $k ]] || return 1
+    [[ $k = $'\012' || $k = $'\015' || $k = ' ' ]] && return 0
+    print -Rn $k
+}
+
+getseq () {
+    trap "stty ${$(stty -g 2>/dev/null):-echo -raw}" 0 1 2 15
+    stty raw -echo
+    local k='' seq='' i
+    for ((i=10; i>0; --i))
+    do
+	read -t -k 1 k && break
+	sleep 1
+    done
+    [[ -n $k ]] || return 1
+    [[ $k = $'\012' || $k = $'\015' || $k = ' ' ]] && return 0
+    seq=$k
+    while read -t -k 1 k
+    do
+       seq=$seq$k 
+    done
+    print -Rn ${(V)seq}
+}
+
+read term"?Enter current terminal type: [$TERM] "
+[[ -n $term ]] && TERM=$term
+
+cat <<\EOF
+
+We will now test some features of your keyboard and terminal.
+
+If you do not press the requested keys within 10 seconds, key reading will
+abort.  If your keyboard does not have a requested key, press Space to
+skip to the next key.
+
+EOF
+
+local ctrl alt meta
+
+print -n "Hold down Ctrl and press X: "
+ctrl=$(getkey) || return 1
+print
+
+if [[ $ctrl != $'\030' ]]
+then
+    print "Your keyboard does not have a working Ctrl key?"
+    print "Giving up ..."
+    return 1
+else
+    print
+fi
+
+print "Your Meta key may have a Microsoft Windows logo on the cap."
+print -n "Hold down Meta and press X: "
+meta=$(getkey) || return 1
+print
+
+if [[ $meta == x ]]
+then
+    print "Your keyboard or terminal does not recognize the Meta key."
+    unset meta
+elif [[ $meta > $'\177' ]]
+then
+    print "Your keyboard uses the Meta key to send high-order characters."
+else
+    unset meta
+fi
+print
+
+print -n "Hold down Alt and press X: "
+alt=$(getkey) || return 1
+print
+
+if [[ $alt == x ]]
+then
+    print "Your keyboard or terminal does not recognize the Alt key."
+    unset alt
+elif [[ $alt == $meta ]]
+then
+    print "Your keyboard does not distinguish Alt from Meta."
+elif [[ $alt > $'\177' ]]
+then
+    print "Your keyboard uses the Alt key to send high-order characters."
+else
+    unset alt
+fi
+
+(( $+alt + $+meta == 0 )) && cat <<EOF
+
+---------
+
+Your current terminal and keyboard configuration does not appear to use
+high-order characters.  You may be able to enable the Meta or Alt keys
+with a command such as
+
+    stty pass8
+
+If you want to use these extra keys with zsh, try adding the above command
+to your ${ZDOTDIR:-$HOME}/.zshrc file.
+
+See also "man stty" or the documentation for your terminal or emulator.
+EOF
+
+(( $+alt || $+meta )) && cat <<EOF
+
+---------
+
+You may enable keybindings that use the \
+${meta:+Meta}${meta:+${alt:+ and }}${alt:+Alt} key${meta:+${alt:+s}} \
+by adding
+
+    bindkey -m
+
+to your ${ZDOTDIR:-$HOME}/.zshrc file.
+EOF
+
+cat <<\EOF
+
+---------
+
+You will now be asked to press in turn each of the 12 function keys, then
+the Backspace key, the 6 common keypad keys found on typical PC keyboards,
+plus the 4 arrow keys, and finally the Menu key (near Ctrl on the right).
+If your keyboard does not have the requested key, press Space to skip to
+the next key.
+
+Do not type ahead!  Wait at least one second after pressing each key for
+zsh to read the entire sequence and prompt for the next key.  If a key
+sequence does not echo within 2 seconds after you press it, that key may
+not be sending any sequence at all.  In this case zsh is not able to make
+use of that key.  Press Space to skip to the next key.
+
+EOF
+
+read -k 1 key"?Press any key when ready to begin: "
+[[ $key != $'\n' ]] && print
+
+cat <<\EOF
+
+If you do not press a key within 10 seconds, key reading will abort.
+If you make a mistake, stop typing and wait, then run this program again.
+
+EOF
+
+# There are 509 combinations of the following three arrays that represent
+# possible keystrokes.  (Actually, Sun keyboards don't have Meta or Menu,
+# though some have R{1..12} keys as well, so really there are either 433
+# or 517 combinations; but some X11 apps map Shift-F{1..11} to emulate the
+# unmodified Sun keys, so really only the 345 PC combinations are usable.
+# Let's not even get into distinguishing Left and Right Shift/Alt/Meta.)
+# No one would ever want to type them all into this program (would they?),
+# so by default ask for the 23 unmodified PC keys.  If you uncomment more,
+# you should fix the introductory text above.
+
+local -a pckeys sunkeys modifiers
+pckeys=(F{1..12}
+        Backspace  Insert  Home   PageUp 
+                   Delete  End   PageDown
+                            Up
+                    Left   Down   Right
+        Menu
+       )
+sunkeys=(Stop  Again
+         Props Undo
+         Front Copy
+         Open  Paste
+         Find  Cut
+            Help
+        )
+modifiers=(Shift- # Control- Alt- Meta-
+           # Control-Shift- Alt-Shift- Meta-Shift-
+           # Control-Alt- Control-Meta- Alt-Meta-
+           # Control-Alt-Shift- Control-Meta-Shift-
+           # Alt-Meta-Shift- Control-Alt-Meta-Shift-
+          )
+
+exec 3>/dev/tty
+
+for key in $pckeys # $^modifiers$^pckeys $sunkeys $^modifiers$^sunkeys
+do
+    print -u3 -Rn "Press $key: "
+    seq="$(getseq)" || return 1
+    print "key[$key]='${(q)seq}'"
+    print -u3 -R $seq
+done >> $zkbd/$TERM.tmp
+
+source $zkbd/$TERM.tmp || return 1
+if [[ "${key[Delete]}" == "${key[Backspace]}" ]]
+then
+    print
+    print Warning: Backspace and Delete key both send "${(q)key[Delete]}"
+else
+    if [[ "${key[Delete]}" != "^?" ]]
+    then
+	print
+        print Warning: Delete key sends "${(q)key[Delete]}" '(not ^?)'
+    fi
+    if [[ "${key[Backspace]}" != "^H" ]]
+    then
+	print
+        print Warning: Backspace sends "${(q)key[Backspace]}"
+    fi
+fi
+
+command mv $zkbd/$TERM.tmp $zkbd/$TERM-$VENDOR-$OSTYPE
+
+cat <<EOF
+
+Parameter assignments for the keys you typed have been written to the file:
+$zkbd/$TERM-$VENDOR-$OSTYPE
+
+You may read this file into ${ZDOTDIR:-$HOME}/.zshrc or another startup
+file with the "source" or "." commands, then reference the \$key parameter
+in bindkey commands, like this:
+
+    source ${zkbd/$HOME/~}/\$TERM-\$VENDOR-\$OSTYPE
+    [[ -n \${key[Left]} ]] && bindkey "\${key[Left]}" backward-char
+    [[ -n \${key[Right]} ]] && bindkey "\${key[Right]}" forward-char
+    # etc.
+
+EOF
diff --git a/Functions/Misc/zmv b/Functions/Misc/zmv
new file mode 100644
index 000000000..b4f9b94ba
--- /dev/null
+++ b/Functions/Misc/zmv
@@ -0,0 +1,268 @@
+# function zmv {
+# zmv, zcp, zln:
+#
+# This is a multiple move based on zsh pattern matching.  To get the full
+# power of it, you need a postgraduate degree in zsh.  However, simple
+# tasks work OK, so if that's all you need, here are some basic examples:
+#   zmv '(*).txt' '$1.lis'
+# Rename foo.txt to foo.lis, etc.  The parenthesis is the thing that
+# gets replaced by the $1 (not the `*', as happens in mmv, and note the
+# `$', not `=', so that you need to quote both words).
+#   zmv '(**/)(*).txt '$1$2.lis'
+# The same, but scanning through subdirectories.  The $1 becomes the full
+# path.  Note that you need to write it like this; you can't get away with
+# '(**/*).txt'.
+#   zmv -w '**/*.txt' '$1$2.lis'
+# This is the lazy version of the one above; zsh picks out the patterns
+# for you.  The catch here is that you don't need the / in the replacement
+# pattern.  (It's not really a catch, since $1 can be empty.)
+#   zmv -C '**/(*).txt' ~/save/'$1'.lis
+# Copy, instead of move, all .txt files in subdirectories to .lis files
+# in the single directory `~/save'.  Note that the ~ was not quoted.
+# You can test things safely by using the `-n' (no, not now) option.
+# Clashes, where multiple files are renamed or copied to the same one, are
+# picked up.
+#
+# Here's a more detailed description.
+#
+# Use zsh pattern matching to move, copy or link files, depending on
+# the last two characters of the function name.  The general syntax is
+#   zmv '<inpat>' '<outstring>'
+# <inpat> is a globbing pattern, so it should be quoted to prevent it from
+# immediate expansion, while <outstring> is a string that will be
+# re-evaluated and hence may contain parameter substitutions, which should
+# also be quoted.  Each set of parentheses in <inpat> (apart from those
+# around glob qualifiers, if you use the -Q option, and globbing flags) may
+# be referred to by a positional parameter in <outstring>, i.e. the first
+# (...) matched is given by $1, and so on.  For example,
+#   zmv '([a-z])(*).txt' '${(C)1}$2.txt'
+# renames algernon.txt to Algernon.txt, boris.txt to Boris.txt and so on.
+# The original file matched can be referred to as $f in the second
+# argument; accidental or deliberate use of other parameters is at owner's
+# risk and is not covered by the (non-existent) guarantee.
+#
+# As usual in zsh, /'s don't work inside parentheses.  There is a special
+# case for (**/) and (***/):  these have the expected effect that the
+# entire relevant path will be substituted by the appropriate positional
+# parameter.
+#
+# There is a shortcut avoiding the use of parenthesis with the option -w
+# (with wildcards), which picks out any expressions `*', `?', `<range>'
+# (<->, <1-10>, etc.), `[...]', possibly followed by `#'s, `**/', `***/', and
+# automatically parenthesises them. (You should quote any ['s or ]'s which
+# appear inside [...] and which do not come from ranges of the form
+# `[:alpha:]'.)  So for example, in
+#    zmv -w '[[:upper:]]*' '${(L)1}$2'
+# the $1 refers to the expression `[[:upper:]]' and the $2 refers to
+# `*'. Thus this finds any file with an upper case first character and
+# renames it to one with a lowercase first character.  Note that any
+# existing parentheses are active, too, so you must count accordingly.
+# Furthermore, an expression like '(?)' will be rewritten as '((?))' --- in
+# other words, parenthesising of wildcards is independent of any existing
+# parentheses.
+#
+# Any file whose name is not changed by the substitution is simply ignored.
+# Any error --- a substitution resulted in an empty string, two
+# substitutions gave the same result, the destination was an existing
+# regular file and -f was not given --- causes the entire function to abort
+# without doing anything.
+#
+# Options:
+#  -f  force overwriting of destination files.  Not currently passed
+#      down to the mv/cp/ln command due to vagaries of implementations
+#      (but you can use -o-f to do that).
+#  -i  interactive: show each line to be executed and ask the user whether
+#      to execute it.  Y or y will execute it, anything else will skip it.
+#      Note that you just need to type one character.
+#  -n  no execution: print what would happen, but don't do it.
+#  -q  Turn bare glob qualifiers off:  now assumed by default, so this
+#      has no effect.
+#  -Q  Force bare glob qualifiers on.  Don't turn this on unless you are
+#      actually using glob qualifiers in a pattern (see below).
+#  -s  symbolic, passed down to ln; only works with zln or z?? -L.
+#  -v  verbose: print line as it's being executed.
+#  -o <optstring>
+#      <optstring> will be split into words and passed down verbatim
+#      to the cp, ln or mv called to perform the work.  It will probably
+#      begin with a `-'.
+#  -p <program>
+#      Call <program> instead of cp, ln or mv.  Whatever it does, it should
+#      at least understand the form '<program> -- <oldname> <newname>',
+#      where <oldname> and <newname> are filenames generated.
+#  -w  Pick out wildcard parts of the pattern, as described above, and
+#      implicitly add parentheses for referring to them.
+#  -C
+#  -L
+#  -M  Force cp, ln or mv, respectively, regardless of the name of the
+#      function.
+#
+# Bugs:
+#   Parenthesised expressions can be confused with glob qualifiers, for
+#   example a trailing '(*)' would be treated as a glob qualifier in
+#   ordinary globbing.  This has proved so annoying that glob qualifiers
+#   are now turned off by default.  To force the use of glob qualifiers,
+#   give the flag -Q.
+#
+#   The second argument is re-evaluated in order to expand the parameters,
+#   so quoting may be a bit haphazard.  In particular, a double quote
+#   will need an extra level of quoting.
+#
+#   The pattern is always treated as an extendedglob pattern.  This
+#   can also be interpreted as a feature.
+#
+# Unbugs:
+#   You don't need braces around the 1 in expressions like '$1t' as
+#   non-positional parameters may not start with a number, although
+#   paranoiacs like the author will probably put them there anyway.
+
+emulate -RL zsh
+setopt extendedglob
+
+local f g args match mbegin mend files action myname tmpf opt exec
+local opt_f opt_i opt_n opt_q opt_Q opt_s opt_M opt_C opt_L 
+local opt_o opt_p opt_v opt_w MATCH MBEGIN MEND
+local pat repl errstr fpat hasglobqual opat
+typeset -A from to
+integer stat
+
+while getopts ":o:p:MCLfinqQsvw" opt; do
+  if [[ $opt = "?" ]]; then
+    print -P "%N: unrecognized option: -$OPTARG" >&2
+    return 1
+  fi
+  eval "opt_$opt=${OPTARG:--$opt}"
+done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
+
+[[ -z $opt_Q ]] && setopt nobareglobqual
+[[ -n $opt_M ]] && action=mv
+[[ -n $opt_C ]] && action=cp
+[[ -n $opt_L ]] && action=ln
+[[ -n $opt_p ]] && action=$opt_p
+
+if (( $# != 2 )); then
+  print -P "Usage:
+  %N oldpattern newpattern
+where oldpattern contains parenthesis surrounding patterns which will
+be replaced in turn by $1, $2, ... in newpattern.  For example,
+  %N '(*).lis' '\$1.txt'
+renames 'foo.lis' to 'foo.txt', 'my.old.stuff.lis' to 'my.old.stuff.txt',
+and so on." >&2
+  return 1
+fi
+
+pat=$1
+repl=$2
+
+if [[ -z $action ]]; then
+  # We can't necessarily get the name of the function directly, because
+  # of no_function_argzero stupidity.
+  tmpf=${TMPPREFIX}zmv$$
+  print -P %N >$tmpf
+  myname=$(<$tmpf)
+  rm -f $tmpf
+
+  action=$myname[-2,-1]
+
+  if [[ $action != (cp|mv|ln) ]]; then
+    print "Action $action not recognised: must be cp, mv or ln." >&2
+    return 1
+  fi
+fi
+
+
+if [[ -n $opt_s && $action != ln ]]; then
+  print -P "%N: invalid option: -s" >&2
+  return 1
+fi
+
+if [[ -n $opt_w ]]; then
+  # Parenthesise all wildcards.
+  local newpat
+  # Well, this seems to work.
+  # The tricky bit is getting all forms of [...] correct, but as long
+  # as we require inactive bits to be backslashed its not so bad.
+  newpat="${pat//\
+(#m)(\*\*#\/|[*?]|\<[0-9]#-[0-9]#\>|\[(\[:[a-z]##:\]|\\\[|\\\]|[^\[\]]##)##\])\##\
+/($MATCH)}"
+  if [[ $newpat = $pat ]]; then
+    print -P "%N: warning: no wildcards were found" >&2
+  else
+    pat=$newpat
+  fi
+fi
+
+if [[ -n $opt_Q && $pat = (#b)(*)\([^\)\|\~]##\) ]]; then
+  hasglobqual=q
+  # strip off qualifiers for use as ordinary pattern
+  opat=$match[1]
+fi
+
+if [[ $pat = (#b)(*)\((\*\*##/)\)(*) ]]; then
+  fpat="$match[1]$match[2]$match[3]"
+  # Now make sure we do depth-first searching.
+  # This is so that the names of any files are altered before the
+  # names of the directories they are in.
+  if [[ -n $opt_Q && -n $hasglobqual ]]; then
+    fpat[-1]="odon)"
+  else
+    setopt bareglobqual
+    fpat="${fpat}(odon)"
+  fi
+else
+  fpat=$pat
+fi
+files=(${~fpat})
+
+[[ -n $hasglobqual ]] && pat=$opat
+
+errs=()
+
+for f in $files; do
+  if [[ $pat = (#b)(*)\(\*\*##/\)(*) ]]; then
+    # This looks like a recursive glob.  This isn't good enough,
+    # because we should really enforce that $match[1] and $match[2]
+    # don't match slashes unless they were explicitly given.  But
+    # it's a start.  It's fine for the classic case where (**/) is
+    # at the start of the pattern.
+    pat="$match[1](*/|)$match[2]"
+  fi
+  [[ -e $f && $f = (#b)${~pat} ]] || continue
+  set -- "$match[@]"
+  eval g=\"$repl\"
+  if [[ -z $g ]]; then
+    errs=($errs "$f expanded to empty string")
+  elif [[ $f = $g ]]; then
+    # don't cause error: more useful just to skip
+    #   errs=($errs "$f not altered by substitution")
+    [[ -n $opt_v ]] && print "$f not altered, ignored"
+    continue
+  elif [[ -n $from[$g] && ! -d $g ]]; then
+    errs=($errs "$f and $from[$g] both map to $g")
+  elif [[ -f $g && -z $opt_f ]]; then
+    errs=($errs "file exists: $g")
+  fi
+  from[$g]=$f
+  to[$f]=$g
+done
+
+if (( $#errs )); then
+  print -P "%N: error(s) in substitution:" >&2
+  print -l $errs >&2
+  return 1
+fi
+
+for f in $files; do
+  [[ -z $to[$f] ]] && continue
+  exec=($action ${=opt_o} $opt_s -- $f $to[$f])
+  [[ -n $opt_i$opt_n$opt_v ]] && print -- $exec
+  if [[ -n $opt_i ]]; then
+    read -q 'opt?Execute? ' || continue
+  fi
+  if [[ -z $opt_n ]]; then
+    $exec || stat=1
+  fi
+done
+
+return $stat
+# }
diff --git a/Functions/Misc/zrecompile b/Functions/Misc/zrecompile
new file mode 100644
index 000000000..abebbbd9e
--- /dev/null
+++ b/Functions/Misc/zrecompile
@@ -0,0 +1,244 @@
+# This tries to find wordcode files and automatically re-compile them if
+# at least one of the original files is newer than the wordcode file.
+# This will only work if the original files were added with their full
+# paths or if the names stored in the wordcode files are relative to the
+# directory where the wordcode file is.
+#
+# Arguments are the names of wordcode files and directories containing
+# wordcode files that should be checked. If no arguments are given, the
+# directories and wordcode files in $fpath are used.
+#
+# And then there are two options:
+#   -t: Only check if there are wordcode files that have to be 
+#       re-compiled. The return status is zero if there are files
+#       that need to be re-compiled and non-zero otherwise.
+#   -q: Be quiet, i.e.: only set the return status.
+#   -p: If this is given, the arguments are interpreted differently:
+#       they should form one or more sets of arguments for zcompile,
+#       seperated by `--'. For example:
+#
+#         zrecompile -p \
+#                    -R ~/.zshrc -- \
+#                    -M ~/.zcompdump -- \
+#                    ~/zsh/comp.zwc ~/zsh/Completion/*/_* \
+#
+#       This makes ~/.zshrc be compiled into ~/.zshrc.zwc if that doesn't
+#       exist or if it is older than ~/.zshrc. The wordcode file will be
+#       marked for reading instead of mapping. The same is done for
+#       ~/.zcompdump and ~/.zcompdump.zwc, but the wordcode file is marked
+#       for mapping. The last line re-creates the file ~/zsh/comp.zwc if
+#       any of the files matching the given pattern is newer than it.
+#
+# Without the -t option, the return status is zero if all wordcode files
+# that needed re-compilation could be compiled and non-zero if compilation
+# for at least one of the files failed.
+
+setopt localoptions extendedglob noshwordsplit noksharrays
+
+local opt check quiet zwc files re file pre ret map tmp mesg pats
+
+tmp=()
+while getopts ":tqp" opt; do
+  case $opt in
+  t) check=yes ;;
+  q) quiet=yes ;;
+  p) pats=yes  ;;
+  *)
+    if [[ -n $pats ]]; then
+      tmp=( $tmp $OPTARG )
+    else
+      print -u2 zrecompile: bad option: -$OPTARG
+      return 1
+    fi
+  esac
+done
+shift OPTIND-${#tmp:-1}
+
+if [[ -n $check ]]; then
+  ret=1
+else
+  ret=0
+fi
+
+if [[ -n $pats ]]; then
+  local end num
+
+  while (( $# )); do
+    end=$argv[(i)--]
+
+    if [[ end -le $# ]]; then
+      files=( $argv[1,end-1] )
+      shift end
+    else
+      files=( $argv )
+      argv=()
+    fi
+
+    tmp=()
+    map=()
+    OPTIND=1
+    while getopts :MR opt $files; do
+      case $opt in
+      [MR]) map=( -$opt ) ;;
+      *) tmp=( $tmp $files[OPTIND] );;
+      esac
+    done
+    shift OPTIND-1 files
+    (( $#files )) || continue
+
+    files=( $files[1] ${files[2,-1]:#*(.zwc|~)} )
+
+    (( $#files )) || continue
+
+    zwc=${files[1]%.zwc}.zwc
+    shift 1 files
+
+    (( $#files )) || files=( ${zwc%.zwc} )
+
+    if [[ -f $zwc ]]; then
+      num=$(zcompile -t $zwc | wc -l)
+      if [[ num-1 -ne $#files ]]; then
+        re=yes
+      else
+        re=
+        for file in $files; do
+          if [[ $file -nt $zwc ]]; then
+            re=yes
+	    break
+          fi
+        done
+      fi
+    else
+      re=yes
+    fi
+
+    if [[ -n $re ]]; then
+      if [[ -n $check ]]; then
+
+        # ... say so.
+
+        [[ -z $quiet ]] && print $zwc needs re-compilation
+        ret=0
+      else
+
+        # ... or do it.
+
+        [[ -z $quiet ]] && print -n "re-compiling ${zwc}: "
+
+        # If the file is mapped, it might be mapped right now, so keep the
+	# old file by renaming it.
+
+	if { [[ ! -f $zwc ]] || mv $zwc ${zwc}.old } &&
+             zcompile $map $tmp $zwc $files 2> /dev/null; then
+          [[ -z $quiet ]] && print succeeded
+        else
+          [[ -z $quiet ]] && print failed
+          ret=1
+        fi
+      fi
+    fi
+  done
+
+  return ret
+fi
+
+# Get the names of wordcode files.
+
+if (( $# )); then
+  argv=( ${^argv}/*.zwc(ND)  ${^argv}.zwc(ND)  ${(M)argv:#*.zwc}  )
+else
+  argv=( ${^fpath}/*.zwc(ND) ${^fpath}.zwc(ND) ${(M)fpath:#*.zwc} )
+fi
+
+# We only handle *.zwc files. zcompile only handles *.zwc files. Everybody
+# seems to handle only *.zwc files.
+
+argv=( ${^argv%.zwc}.zwc )
+
+for zwc; do
+
+  # Get the files in the wordcode file.
+
+  files=( ${(f)"$(zcompile -t $zwc)"} )
+
+  # See if the wordcode file will be mapped.
+
+  if [[ $files[1] = *\(mapped\)* ]]; then
+    map=-M
+    mesg='succeeded (old saved)'
+  else
+    map=-R
+    mesg=succeeded
+  fi
+
+  # Get the path prefix of the wordcode file to prepend it to names of
+  # original files that are relative pathnames.
+  
+  if [[ $zwc = */* ]]; then
+    pre=${zwc%/*}/
+  else
+    pre=
+  fi
+
+  # Maybe this is even for an older version of the shell?
+
+  if [[ $files[1] != *$ZSH_VERSION ]]; then
+    re=yes
+  else
+    re=
+  fi
+
+  files=( ${pre}${^files[2,-1]:#/*} ${(M)files[2,-1]:#/*} )
+
+  # If the version is correct, compare the age of every original file
+  # to the age of the wordcode file.
+
+  [[ -z $re ]] &&
+    for file in $files; do
+      if [[ $file -nt $zwc ]]; then
+        re=yes
+        break
+      fi
+    done
+
+  if [[ -n $re ]]; then
+
+    # The wordcode files needs re-compilation...
+
+    if [[ -n $check ]]; then
+
+      # ... say so.
+
+      [[ -z $quiet ]] && print $zwc needs re-compilation
+      ret=0
+    else
+
+      # ... or do it.
+
+      [[ -z $quiet ]] && print -n "re-compiling ${zwc}: "
+
+      tmp=( ${^files}(N) )
+
+      # Here is the call to zcompile, but if we can't find all the original
+      # files, we don't try compilation.
+
+      if [[ $#tmp -ne $#files ]]; then
+        [[ -z $quiet ]] && print 'failed (missing files)'
+        ret=1
+      else
+
+        # If the file is mapped, it might be mapped right now, so keep the
+	# old file by renaming it.
+
+	if mv $zwc ${zwc}.old && zcompile $map $zwc $files 2> /dev/null; then
+          [[ -z $quiet ]] && print $mesg
+        else
+          [[ -z $quiet ]] && print failed
+          ret=1
+        fi
+      fi
+    fi
+  fi
+done
+
+return ret
diff --git a/Functions/Misc/zstyle+ b/Functions/Misc/zstyle+
new file mode 100644
index 000000000..eb3c14df5
--- /dev/null
+++ b/Functions/Misc/zstyle+
@@ -0,0 +1,35 @@
+# This makes defining styles a bit simpler by using a single `+' as a
+# special token that allows to append a context name to the previously
+# used context name. Like this:
+#
+#   zstyle+ ':foo:bar' style1 value1 \
+#         + ':baz'     style2 value2 \
+#         + ':frob'    style3 value3
+#
+# This defines style1 with value1 for the context :foo:bar as usual.
+# But it also defines styles2 with value2 for the context :foo:bar:baz
+# and style3 with value3 for :foo:bar:frob.
+# Of course, any of the sub-contexts after the plus signs may be 
+# empty strings to re-use the previous context unchanged.
+#
+# If you don't want to change all your calls to `zstyle' to use
+# `zstyle+' you can use an alias `alias zstyle=zstyle+' and make sure
+# the completion functions are autoloaded without alias expansion (the
+# -U option to the autoload builtin). The completion system normally
+# loads its functions with without alias expansion.
+
+case "$1" in
+-*) zstyle "$@";;
+
+*)  setopt localoptions noksharrays
+    integer i
+    local context="$1"
+    1=''
+    for ((i=2; $#; ++i)); do
+      if [[ $i -gt $# || "$argv[i]" == '+' ]]; then
+        zstyle "$context${(@)argv[1,i-1]}"
+        shift "i > $# ? $# : i"  # Stupid shift error on i > $#
+  	i=1
+      fi
+    done;;
+esac