zshrc: Add comments about the ls_ and grep_options we set
[grml-etc-core.git] / etc / zsh / zshrc
index 33b0c26..afcb5b7 100644 (file)
@@ -180,14 +180,26 @@ else
     isgrmlsmall() { return 1 }
 fi
 
+GRML_OSTYPE=$(uname -s)
+
+islinux(){
+    [[ $GRML_OSTYPE == "Linux" ]]
+}
+
 isdarwin(){
-    [[ $OSTYPE == darwin* ]] && return 0
-    return 1
+    [[ $GRML_OSTYPE == "Darwin" ]]
 }
 
 isfreebsd(){
-    [[ $OSTYPE == freebsd* ]] && return 0
-    return 1
+    [[ $GRML_OSTYPE == "FreeBSD" ]]
+}
+
+isopenbsd(){
+    [[ $GRML_OSTYPE == "OpenBSD" ]]
+}
+
+issolaris(){
+    [[ $GRML_OSTYPE == "SunOS" ]]
 }
 
 #f1# are we running within an utf environment?
@@ -262,6 +274,83 @@ function zrcautoload() {
     return 0
 }
 
+# The following is the ‘add-zsh-hook’ function from zsh upstream. It is
+# included here to make the setup work with older versions of zsh (prior to
+# 4.3.7) in which this function had a bug that triggers annoying errors during
+# shell startup. This is exactly upstreams code from f0068edb4888a4d8fe94def,
+# with just a few adjustments in coding style to make the function look more
+# compact. This definition can be removed as soon as we raise the minimum
+# version requirement to 4.3.7 or newer.
+function add-zsh-hook() {
+    # Add to HOOK the given FUNCTION.
+    # HOOK is one of chpwd, precmd, preexec, periodic, zshaddhistory,
+    # zshexit, zsh_directory_name (the _functions subscript is not required).
+    #
+    # With -d, remove the function from the hook instead; delete the hook
+    # variable if it is empty.
+    #
+    # -D behaves like -d, but pattern characters are active in the function
+    # name, so any matching function will be deleted from the hook.
+    #
+    # Without -d, the FUNCTION 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 actually defined inline.)
+    emulate -L zsh
+    local -a hooktypes
+    hooktypes=(
+        chpwd precmd preexec periodic zshaddhistory zshexit
+        zsh_directory_name
+    )
+    local usage="Usage: $0 hook function\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 ))
+    if (( list )); then
+        typeset -mp "(${1:-${(@j:|:)hooktypes}})_functions"
+        return $?
+    elif (( help || $# != 2 || ${hooktypes[(I)$1]} == 0 )); then
+        print -u$(( 2 - help )) $usage
+        return $(( 1 - help ))
+    fi
+    local hook="${1}_functions"
+    local fn="$2"
+    if (( del )); then
+        # delete, if hook is set
+        if (( ${(P)+hook} )); then
+            if (( del == 2 )); then
+                set -A $hook ${(P)hook:#${~fn}}
+            else
+                set -A $hook ${(P)hook:#$fn}
+            fi
+            # unset if no remaining entries --- this can give better
+            # performance in some cases
+            if (( ! ${(P)#hook} )); then
+                unset $hook
+            fi
+        fi
+    else
+        if (( ${(P)+hook} )); then
+            if (( ${${(P)hook}[(I)$fn]} == 0 )); then
+                set -A $hook ${(P)hook} $fn
+            fi
+        else
+            set -A $hook $fn
+        fi
+        autoload $autoopts -- $fn
+    fi
+}
+
 # Load is-at-least() for more precise version checks Note that this test will
 # *always* fail, if the is-at-least function could not be marked for
 # autoloading.
@@ -299,9 +388,6 @@ setopt extended_glob
 # display PID when suspending processes as well
 setopt longlistjobs
 
-# try to avoid the 'zsh: no matches found...'
-setopt nonomatch
-
 # report the status of backgrounds jobs immediately
 setopt notify
 
@@ -339,20 +425,29 @@ NOMENU=${NOMENU:-0}
 NOPRECMD=${NOPRECMD:-0}
 COMMAND_NOT_FOUND=${COMMAND_NOT_FOUND:-0}
 GRML_ZSH_CNF_HANDLER=${GRML_ZSH_CNF_HANDLER:-/usr/share/command-not-found/command-not-found}
-BATTERY=${BATTERY:-0}
+GRML_DISPLAY_BATTERY=${GRML_DISPLAY_BATTERY:-${BATTERY:-0}}
 GRMLSMALL_SPECIFIC=${GRMLSMALL_SPECIFIC:-1}
 ZSH_NO_DEFAULT_LOCALE=${ZSH_NO_DEFAULT_LOCALE:-0}
 
 typeset -ga ls_options
 typeset -ga grep_options
-if ls --help 2> /dev/null | grep -q GNU; then
-    ls_options=( --color=auto )
-elif [[ $OSTYPE == freebsd* ]]; then
-    ls_options=( -G )
+
+# Colors on GNU ls(1)
+if ls --color=auto / >/dev/null 2>&1; then
+    ls_options+=( --color=auto )
+# Colors on FreeBSD and OSX ls(1)
+elif ls -G / >/dev/null 2>&1; then
+    ls_options+=( -G )
 fi
-if grep --help 2> /dev/null | grep -q GNU || \
-   [[ $OSTYPE == freebsd* ]]; then
-    grep_options=( --color=auto )
+
+# Natural sorting order on GNU ls(1)
+if ls -v / >/dev/null 2>&1; then
+    ls_options+=( -v )
+fi
+
+# Color on GNU and FreeBSD grep(1)
+if grep --color=auto -q "a" <<< "a" >/dev/null 2>&1; then
+    grep_options+=( --color=auto )
 fi
 
 # utility functions
@@ -406,23 +501,23 @@ check_com() {
 salias() {
     emulate -L zsh
     local only=0 ; local multi=0
-    while [[ $1 == -* ]] ; do
-        case $1 in
-            (-o) only=1 ;;
-            (-a) multi=1 ;;
-            (--) shift ; break ;;
-            (-h)
-                printf 'usage: salias [-h|-o|-a] <alias-expression>\n'
+    local key val
+    while getopts ":hao" opt; do
+        case $opt in
+            o) only=1 ;;
+            a) multi=1 ;;
+            h)
+                printf 'usage: salias [-hoa] <alias-expression>\n'
                 printf '  -h      shows this help text.\n'
                 printf '  -a      replace '\'' ; '\'' sequences with '\'' ; sudo '\''.\n'
                 printf '          be careful using this option.\n'
                 printf '  -o      only sets an alias if a preceding sudo would be needed.\n'
                 return 0
                 ;;
-            (*) printf "unkown option: '%s'\n" "$1" ; return 1 ;;
+            *) salias -h >&2; return 1 ;;
         esac
-        shift
     done
+    shift "$((OPTIND-1))"
 
     if (( ${#argv} > 1 )) ; then
         printf 'Too many arguments %s\n' "${#argv}"
@@ -440,30 +535,6 @@ salias() {
     return 0
 }
 
-# a "print -l ${(u)foo}"-workaround for pre-4.2.0 shells
-# usage: uprint foo
-#   Where foo is the *name* of the parameter you want printed.
-#   Note that foo is no typo; $foo would be wrong here!
-if ! is42 ; then
-    uprint () {
-        emulate -L zsh
-        local -a u
-        local w
-        local parameter=$1
-
-        if [[ -z ${parameter} ]] ; then
-            printf 'usage: uprint <parameter>\n'
-            return 1
-        fi
-
-        for w in ${(P)parameter} ; do
-            [[ -z ${(M)u:#$w} ]] && u=( $u $w )
-        done
-
-        builtin print -l $u
-    }
-fi
-
 # Check if we can read given files and source those we can.
 xsource() {
     if (( ${#argv} < 1 )) ; then
@@ -495,11 +566,8 @@ xcat() {
 xunfunction() {
     emulate -L zsh
     local -a funcs
-    funcs=(salias xcat xsource xunfunction zrcautoload
-                                           zrcautozle
-                                           zrcbindkey
-                                           zrcgotkeymap
-                                           zrcgotwidget)
+    local func
+    funcs=(salias xcat xsource xunfunction zrcautoload zrcautozle)
     for func in $funcs ; do
         [[ -n ${functions[$func]} ]] \
             && unfunction $func
@@ -523,10 +591,7 @@ fi
 for var in LANG LC_ALL LC_MESSAGES ; do
     [[ -n ${(P)var} ]] && export $var
 done
-
-xsource "/etc/sysconfig/keyboard"
-
-TZ=$(xcat /etc/timezone)
+builtin unset -v var
 
 # set some variables
 if check_com -c vim ; then
@@ -542,9 +607,6 @@ export PAGER=${PAGER:-less}
 #v#
 export MAIL=${MAIL:-/var/mail/$USER}
 
-# if we don't set $SHELL then aterm, rxvt,.. will use /bin/sh or /bin/bash :-/
-export SHELL='/bin/zsh'
-
 # color setup for ls:
 check_com -c dircolors && eval $(dircolors -b)
 # color setup for ls on OS X / FreeBSD:
@@ -598,7 +660,7 @@ typeset -U path cdpath fpath manpath
 is4 && \
 for mod in parameter complist deltochar mathfunc ; do
     zmodload -i zsh/${mod} 2>/dev/null || print "Notice: no ${mod} available :("
-done
+done && builtin unset -v mod
 
 # autoload zsh modules when they are referenced
 if is4 ; then
@@ -608,8 +670,9 @@ if is4 ; then
 fi
 
 # completion system
+COMPDUMPFILE=${COMPDUMPFILE:-${ZDOTDIR:-${HOME}}/.zcompdump}
 if zrcautoload compinit ; then
-    compinit || print 'Notice: no compinit available :('
+    compinit -d ${COMPDUMPFILE} || print 'Notice: no compinit available :('
 else
     print 'Notice: no compinit available :('
     function compdef { }
@@ -752,20 +815,30 @@ grmlcomp() {
     # command for process lists, the local web server details and host completion
     zstyle ':completion:*:urls' local 'www' '/var/www/' 'public_html'
 
-    # caching
-    [[ -d $ZSHDIR/cache ]] && zstyle ':completion:*' use-cache yes && \
-                            zstyle ':completion::complete:*' cache-path $ZSHDIR/cache/
+    # Some functions, like _apt and _dpkg, are very slow. We can use a cache in
+    # order to speed things up
+    if [[ ${GRML_COMP_CACHING:-yes} == yes ]]; then
+        GRML_COMP_CACHE_DIR=${GRML_COMP_CACHE_DIR:-${ZDOTDIR:-$HOME}/.cache}
+        if [[ ! -d ${GRML_COMP_CACHE_DIR} ]]; then
+            command mkdir -p "${GRML_COMP_CACHE_DIR}"
+        fi
+        zstyle ':completion:*' use-cache  yes
+        zstyle ':completion:*:complete:*' cache-path "${GRML_COMP_CACHE_DIR}"
+    fi
 
     # host completion
     if is42 ; then
+        [[ -r ~/.ssh/config ]] && _ssh_config_hosts=(${${(s: :)${(ps:\t:)${${(@M)${(f)"$(<$HOME/.ssh/config)"}:#Host *}#Host }}}:#*[*?]*}) || _ssh_config_hosts=()
         [[ -r ~/.ssh/known_hosts ]] && _ssh_hosts=(${${${${(f)"$(<$HOME/.ssh/known_hosts)"}:#[\|]*}%%\ *}%%,*}) || _ssh_hosts=()
         [[ -r /etc/hosts ]] && : ${(A)_etc_hosts:=${(s: :)${(ps:\t:)${${(f)~~"$(</etc/hosts)"}%%\#*}##[:blank:]#[^[:blank:]]#}}} || _etc_hosts=()
     else
+        _ssh_config_hosts=()
         _ssh_hosts=()
         _etc_hosts=()
     fi
     hosts=(
         $(hostname)
+        "$_ssh_config_hosts[@]"
         "$_ssh_hosts[@]"
         "$_etc_hosts[@]"
         localhost
@@ -776,8 +849,8 @@ grmlcomp() {
 
     # use generic completion system for programs not yet defined; (_gnu_generic works
     # with commands that provide a --help option with "standard" gnu-like output.)
-    for compcom in cp deborphan df feh fetchipac head hnb ipacsum mv \
-                   pal stow tail uname ; do
+    for compcom in cp deborphan df feh fetchipac gpasswd head hnb ipacsum mv \
+                   pal stow uname ; do
         [[ -z ${_comps[$compcom]} ]] && compdef _gnu_generic ${compcom}
     done; unset compcom
 
@@ -785,11 +858,11 @@ grmlcomp() {
     compdef _hosts upgrade
 }
 
-# Keyboard setup: The following is the same code, we wrote for debian's setup.
-# It ensures the terminal is in the right mode, when zle is active, so the
-# values from $terminfo are valid. Therefore, this setup should work on all
-# systems, that have support for `terminfo'. It also requires the zsh in use to
-# have the `zsh/terminfo' module built.
+# Keyboard setup: The following is based on the same code, we wrote for
+# debian's setup. It ensures the terminal is in the right mode, when zle is
+# active, so the values from $terminfo are valid. Therefore, this setup should
+# work on all systems, that have support for `terminfo'. It also requires the
+# zsh in use to have the `zsh/terminfo' module built.
 #
 # If you are customising your `zle-line-init()' or `zle-line-finish()'
 # functions, make sure you call the following utility functions in there:
@@ -819,21 +892,6 @@ beginning-or-end-of-somewhere() {
 zle -N beginning-of-somewhere beginning-or-end-of-somewhere
 zle -N end-of-somewhere beginning-or-end-of-somewhere
 
-## toggle the ,. abbreviation feature on/off
-# NOABBREVIATION: default abbreviation-state
-#                 0 - enabled (default)
-#                 1 - disabled
-NOABBREVIATION=${NOABBREVIATION:-0}
-
-grml_toggle_abbrev() {
-    if (( ${NOABBREVIATION} > 0 )) ; then
-        NOABBREVIATION=0
-    else
-        NOABBREVIATION=1
-    fi
-}
-zle -N grml_toggle_abbrev
-
 # add a command line to the shells history without executing it
 commit-to-history() {
     print -s ${(z)BUFFER}
@@ -913,7 +971,7 @@ function Accept-Line-HandleContext() {
 
 function accept-line() {
     setopt localoptions noksharrays
-    local -ax cmdline
+    local -a cmdline
     local -x alcontext
     local buf com fname format msg default_action
 
@@ -995,11 +1053,10 @@ zle -N accept-line
 zle -N Accept-Line
 zle -N Accept-Line-HandleContext
 
-# power completion - abbreviation expansion
 # power completion / abbreviation expansion / buffer expansion
 # see http://zshwiki.org/home/examples/zleiab for details
 # less risky than the global aliases but powerful as well
-# just type the abbreviation key and afterwards ',.' to expand it
+# just type the abbreviation key and afterwards 'ctrl-x .' to expand it
 declare -A abk
 setopt extendedglob
 setopt interactivecomments
@@ -1031,11 +1088,6 @@ zleiab() {
     setopt extendedglob
     local MATCH
 
-    if (( NOABBREVIATION > 0 )) ; then
-        LBUFFER="${LBUFFER},."
-        return 0
-    fi
-
     LBUFFER=${LBUFFER%%(#m)[.\-+:|_a-zA-Z0-9]#}
     LBUFFER+=${abk[$MATCH]:-$MATCH}
 }
@@ -1044,7 +1096,7 @@ zle -N zleiab
 
 help-show-abk()
 {
-  zle -M "$(print "Type ,. after these abbreviations to expand them:"; print -a -C 2 ${(kv)abk})"
+  zle -M "$(print "Available abbreviations for expansion:"; print -a -C 2 ${(kv)abk})"
 }
 
 zle -N help-show-abk
@@ -1126,7 +1178,6 @@ inplaceMkDirs() {
     fi
 }
 
-#k# mkdir -p <dir> from string under cursor or marked area
 zle -N inplaceMkDirs
 
 #v1# set number of lines to display per page
@@ -1145,6 +1196,7 @@ help_zle_parse_keybindings()
 
     if [[ -r $HELP_ZLE_CACHE_FILE ]]; then
         local load_cache=0
+        local f
         for f ($HELPZLE_KEYBINDING_FILES) [[ $f -nt $HELP_ZLE_CACHE_FILE ]] && load_cache=1
         [[ $load_cache -eq 0 ]] && . $HELP_ZLE_CACHE_FILE && return
     fi
@@ -1186,7 +1238,7 @@ help_zle_parse_keybindings()
     typeset -g -a help_zle_lines
     typeset -g help_zle_sln=1
 
-    local k v
+    local k v f cline
     local lastkeybind_desc contents     #last description starting with #k# that we found
     local num_lines_elapsed=0            #number of lines between last description and keybinding
     #search config files in the order they a called (and thus the order in which they overwrite keybindings)
@@ -1198,10 +1250,10 @@ help_zle_parse_keybindings()
             if [[ "$cline" == (#s)[[:space:]]#\#k\#[[:space:]]##(#b)(*)[[:space:]]#(#e) ]]; then
                 lastkeybind_desc="$match[*]"
                 num_lines_elapsed=0
-            #zsh pattern: matches lines that set a keybinding using bindkey or compdef -k
+            #zsh pattern: matches lines that set a keybinding using bind2map, bindkey or compdef -k
             #             ignores lines that are commentend out
             #             grabs first in '' or "" enclosed string with length between 1 and 6 characters
-            elif [[ "$cline" == [^#]#(bindkey|compdef -k)[[:space:]](*)(#b)(\"((?)(#c1,6))\"|\'((?)(#c1,6))\')(#B)(*)  ]]; then
+            elif [[ "$cline" == [^#]#(bind2maps[[:space:]](*)-s|bindkey|compdef -k)[[:space:]](*)(#b)(\"((?)(#c1,6))\"|\'((?)(#c1,6))\')(#B)(*)  ]]; then
                 #description prevously found ? description not more than 2 lines away ? keybinding not empty ?
                 if [[ -n $lastkeybind_desc && $num_lines_elapsed -lt 2 && -n $match[1] ]]; then
                     #substitute keybinding string with something readable
@@ -1328,7 +1380,12 @@ function bind2maps () {
     done
     shift
 
-    sequence="${key[$1]}"
+    if [[ "$1" == "-s" ]]; then
+        shift
+        sequence="$1"
+    else
+        sequence="${key[$1]}"
+    fi
     widget="$2"
 
     [[ -z "$sequence" ]] && return 1
@@ -1340,9 +1397,11 @@ function bind2maps () {
 
 if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
     function zle-smkx () {
+        emulate -L zsh
         printf '%s' ${terminfo[smkx]}
     }
     function zle-rmkx () {
+        emulate -L zsh
         printf '%s' ${terminfo[rmkx]}
     }
     function zle-line-init () {
@@ -1375,6 +1434,19 @@ key=(
     BackTab  "${terminfo[kcbt]}"
 )
 
+# Guidelines for adding key bindings:
+#
+#   - Do not add hardcoded escape sequences, to enable non standard key
+#     combinations such as Ctrl-Meta-Left-Cursor. They are not easily portable.
+#
+#   - Adding Ctrl characters, such as '^b' is okay; note that '^b' and '^B' are
+#     the same key.
+#
+#   - All keys from the $key[] mapping are obviously okay.
+#
+#   - Most terminals send "ESC x" when Meta-x is pressed. Thus, sequences like
+#     '\ex' are allowed in here as well.
+
 bind2maps emacs             -- Home   beginning-of-somewhere
 bind2maps       viins vicmd -- Home   vi-beginning-of-line
 bind2maps emacs             -- End    end-of-somewhere
@@ -1389,94 +1461,108 @@ bind2maps emacs             -- Left   backward-char
 bind2maps       viins vicmd -- Left   vi-backward-char
 bind2maps emacs             -- Right  forward-char
 bind2maps       viins vicmd -- Right  vi-forward-char
-bind2maps       viins vicmd -- Right  vi-forward-char
-
-if zrcgotkeymap menuselect; then
-    #m# k Shift-tab Perform backwards menu completion
-    bind2maps menuselect -- BackTab reverse-menu-complete
-
-    #k# menu selection: pick item but stay in the menu
-    zrcbindkey -M menuselect '\e^M' accept-and-menu-complete
-    # also use + and INSERT since it's easier to press repeatedly
-    zrcbindkey -M menuselect "+" accept-and-menu-complete
-    zrcbindkey -M menuselect "^[[2~" accept-and-menu-complete
-
-    # accept a completion and try to complete again by using menu
-    # completion; very useful with completing directories
-    # by using 'undo' one's got a simple file browser
-    zrcbindkey -M menuselect '^o' accept-and-infer-next-history
-fi
-
-#k# Display list of abbreviations that expand when followed by ,.
-zrcbindkey ",." zleiab
-zrcbindkey '^xb' help-show-abk
-zrcbindkey '^xM' inplaceMkDirs
+#k# Perform abbreviation expansion
+bind2maps emacs viins       -- -s '^x.' zleiab
+#k# Display list of abbreviations that would expand
+bind2maps emacs viins       -- -s '^xb' help-show-abk
+#k# mkdir -p <dir> from string under cursor or marked area
+bind2maps emacs viins       -- -s '^xM' inplaceMkDirs
 #k# display help for keybindings and ZLE
-zrcbindkey '^xz' help-zle
-
+bind2maps emacs viins       -- -s '^xz' help-zle
 #k# Insert files and test globbing
-zrcbindkey "^xf" insert-files # C-x-f
-
+bind2maps emacs viins       -- -s "^xf" insert-files
 #k# Edit the current line in \kbd{\$EDITOR}
-zrcbindkey '\ee' edit-command-line
-
-## use Ctrl-left-arrow and Ctrl-right-arrow for jumping to word-beginnings on the CL
-zrcbindkey "\e[5C" forward-word
-zrcbindkey "\e[5D" backward-word
-zrcbindkey "\e[1;5C" forward-word
-zrcbindkey "\e[1;5D" backward-word
-## the same for alt-left-arrow and alt-right-arrow
-zrcbindkey '^[[1;3C' forward-word
-zrcbindkey '^[[1;3D' backward-word
-
+bind2maps emacs viins       -- -s '\ee' edit-command-line
 #k# search history backward for entry beginning with typed text
-zrcbindkey '^xp'   history-beginning-search-backward-end
+bind2maps emacs viins       -- -s '^xp' history-beginning-search-backward-end
 #k# search history forward for entry beginning with typed text
-zrcbindkey '^xP'   history-beginning-search-forward-end
+bind2maps emacs viins       -- -s '^xP' history-beginning-search-forward-end
 #k# search history backward for entry beginning with typed text
-zrcbindkey "\e[5~" history-beginning-search-backward-end # PageUp
+bind2maps emacs viins       -- PageUp history-beginning-search-backward-end
 #k# search history forward for entry beginning with typed text
-zrcbindkey "\e[6~" history-beginning-search-forward-end  # PageDown
-
-# insert unicode character
-# usage example: 'ctrl-x i' 00A7 'ctrl-x i' will give you an §
-# See for example http://unicode.org/charts/ for unicode characters code
-#k# Insert Unicode character
-zrcbindkey '^xi' insert-unicode-char
-
-#k# Toggle abbreviation expansion on/off
-zrcbindkey '^xA' grml_toggle_abbrev
-zrcbindkey "^x^h" commit-to-history
-
+bind2maps emacs viins       -- PageDown history-beginning-search-forward-end
+bind2maps emacs viins       -- -s "^x^h" commit-to-history
 #k# Kill left-side word or everything up to next slash
-zrcbindkey '\ev' slash-backward-kill-word
+bind2maps emacs viins       -- -s '\ev' slash-backward-kill-word
 #k# Kill left-side word or everything up to next slash
-zrcbindkey '\e^h' slash-backward-kill-word
+bind2maps emacs viins       -- -s '\e^h' slash-backward-kill-word
 #k# Kill left-side word or everything up to next slash
-zrcbindkey '\e^?' slash-backward-kill-word
-
-# use the new *-pattern-* widgets for incremental history search
-if zrcgotwidget history-incremental-pattern-search-backward; then
-    zrcbindkey '^r' history-incremental-pattern-search-backward
-    zrcbindkey '^s' history-incremental-pattern-search-forward
-fi
-
+bind2maps emacs viins       -- -s '\e^?' slash-backward-kill-word
 # Do history expansion on space:
-zrcbindkey ' ' magic-space
+bind2maps emacs viins       -- -s ' ' magic-space
 #k# Trigger menu-complete
-zrcbindkey '\ei' menu-complete  # menu completion via esc-i
+bind2maps emacs viins       -- -s '\ei' menu-complete  # menu completion via esc-i
 #k# Insert a timestamp on the command line (yyyy-mm-dd)
-zrcbindkey '^ed' insert-datestamp
+bind2maps emacs viins       -- -s '^ed' insert-datestamp
 #k# Insert last typed word
-zrcbindkey "\em" insert-last-typed-word
+bind2maps emacs viins       -- -s "\em" insert-last-typed-word
 #k# A smart shortcut for \kbd{fg<enter>}
-zrcbindkey '^z' grml-zsh-fg
+bind2maps emacs viins       -- -s '^z' grml-zsh-fg
 #k# prepend the current command with "sudo"
-zrcbindkey "^os" sudo-command-line
+bind2maps emacs viins       -- -s "^os" sudo-command-line
 #k# jump to after first word (for adding options)
-zrcbindkey '^x1' jump_after_first_word
+bind2maps emacs viins       -- -s '^x1' jump_after_first_word
 #k# complete word from history with menu
-zrcbindkey "^x^x" hist-complete
+bind2maps emacs viins       -- -s "^x^x" hist-complete
+
+# insert unicode character
+# usage example: 'ctrl-x i' 00A7 'ctrl-x i' will give you an §
+# See for example http://unicode.org/charts/ for unicode characters code
+#k# Insert Unicode character
+bind2maps emacs viins       -- -s '^xi' insert-unicode-char
+
+# use the new *-pattern-* widgets for incremental history search
+if zrcgotwidget history-incremental-pattern-search-backward; then
+    for seq wid in '^r' history-incremental-pattern-search-backward \
+                   '^s' history-incremental-pattern-search-forward
+    do
+        bind2maps emacs viins vicmd -- -s $seq $wid
+    done
+    builtin unset -v seq wid
+fi
+
+if zrcgotkeymap menuselect; then
+    #m# k Shift-tab Perform backwards menu completion
+    bind2maps menuselect -- BackTab reverse-menu-complete
+
+    #k# menu selection: pick item but stay in the menu
+    bind2maps menuselect -- -s '\e^M' accept-and-menu-complete
+    # also use + and INSERT since it's easier to press repeatedly
+    bind2maps menuselect -- -s '+' accept-and-menu-complete
+    bind2maps menuselect -- Insert accept-and-menu-complete
+
+    # accept a completion and try to complete again by using menu
+    # completion; very useful with completing directories
+    # by using 'undo' one's got a simple file browser
+    bind2maps menuselect -- -s '^o' accept-and-infer-next-history
+fi
+
+# Finally, here are still a few hardcoded escape sequences; Special sequences
+# like Ctrl-<Cursor-key> etc do suck a fair bit, because they are not
+# standardised and most of the time are not available in a terminals terminfo
+# entry.
+#
+# While we do not encourage adding bindings like these, we will keep these for
+# backward compatibility.
+
+## use Ctrl-left-arrow and Ctrl-right-arrow for jumping to word-beginnings on
+## the command line.
+# URxvt sequences:
+bind2maps emacs viins vicmd -- -s '\eOc' forward-word
+bind2maps emacs viins vicmd -- -s '\eOd' backward-word
+# These are for xterm:
+bind2maps emacs viins vicmd -- -s '\e[1;5C' forward-word
+bind2maps emacs viins vicmd -- -s '\e[1;5D' backward-word
+## the same for alt-left-arrow and alt-right-arrow
+# URxvt again:
+bind2maps emacs viins vicmd -- -s '\e\e[C' forward-word
+bind2maps emacs viins vicmd -- -s '\e\e[D' backward-word
+# Xterm again:
+bind2maps emacs viins vicmd -- -s '^[[1;3C' forward-word
+bind2maps emacs viins vicmd -- -s '^[[1;3D' backward-word
+# Also try ESC Left/Right:
+bind2maps emacs viins vicmd -- -s '\e'${key[Right]} forward-word
+bind2maps emacs viins vicmd -- -s '\e'${key[Left]}  backward-word
 
 # autoloading
 
@@ -1511,8 +1597,6 @@ function command_not_found_handler() {
 
 # history
 
-ZSHDIR=${ZDOTDIR:-${HOME}/.zsh}
-
 #v#
 HISTFILE=${ZDOTDIR:-${HOME}}/.zsh_history
 isgrmlcd && HISTSIZE=500  || HISTSIZE=5000
@@ -1523,22 +1607,52 @@ isgrmlcd && SAVEHIST=1000 || SAVEHIST=10000 # useful for setopt append_history
 DIRSTACKSIZE=${DIRSTACKSIZE:-20}
 DIRSTACKFILE=${DIRSTACKFILE:-${ZDOTDIR:-${HOME}}/.zdirs}
 
-if [[ -f ${DIRSTACKFILE} ]] && [[ ${#dirstack[*]} -eq 0 ]] ; then
-    dirstack=( ${(f)"$(< $DIRSTACKFILE)"} )
-    # "cd -" won't work after login by just setting $OLDPWD, so
-    [[ -d $dirstack[1] ]] && cd $dirstack[1] && cd $OLDPWD
-fi
+typeset -gaU GRML_PERSISTENT_DIRSTACK
+function grml_dirstack_filter() {
+    local -a exclude
+    local filter entry
+    if zstyle -s ':grml:chpwd:dirstack' filter filter; then
+        $filter $1 && return 0
+    fi
+    if zstyle -a ':grml:chpwd:dirstack' exclude exclude; then
+        for entry in "${exclude[@]}"; do
+            [[ $1 == ${~entry} ]] && return 0
+        done
+    fi
+    return 1
+}
 
 chpwd() {
-    local -ax my_stack
-    my_stack=( ${PWD} ${dirstack} )
-    if is42 ; then
-        builtin print -l ${(u)my_stack} >! ${DIRSTACKFILE}
-    else
-        uprint my_stack >! ${DIRSTACKFILE}
-    fi
+    (( $DIRSTACKSIZE <= 0 )) && return
+    [[ -z $DIRSTACKFILE ]] && return
+    grml_dirstack_filter $PWD && return
+    GRML_PERSISTENT_DIRSTACK=(
+        $PWD "${(@)GRML_PERSISTENT_DIRSTACK[1,$DIRSTACKSIZE]}"
+    )
+    builtin print -l ${GRML_PERSISTENT_DIRSTACK} >! ${DIRSTACKFILE}
 }
 
+if [[ -f ${DIRSTACKFILE} ]]; then
+    # Enabling NULL_GLOB via (N) weeds out any non-existing
+    # directories from the saved dir-stack file.
+    dirstack=( ${(f)"$(< $DIRSTACKFILE)"}(N) )
+    # "cd -" won't work after login by just setting $OLDPWD, so
+    [[ -d $dirstack[1] ]] && cd -q $dirstack[1] && cd -q $OLDPWD
+fi
+
+if zstyle -T ':grml:chpwd:dirstack' filter-on-load; then
+    for i in "${dirstack[@]}"; do
+        if ! grml_dirstack_filter "$i"; then
+            GRML_PERSISTENT_DIRSTACK=(
+                "${GRML_PERSISTENT_DIRSTACK[@]}"
+                $i
+            )
+        fi
+    done
+else
+    GRML_PERSISTENT_DIRSTACK=( "${dirstack[@]}" )
+fi
+
 # directory based profiles
 
 if is433 ; then
@@ -1585,7 +1699,16 @@ fi # is433
 # set colors for use in prompts (modern zshs allow for the use of %F{red}foo%f
 # in prompts to get a red "foo" embedded, but it's good to keep these for
 # backwards compatibility).
-if zrcautoload colors && colors 2>/dev/null ; then
+if is437; then
+    BLUE="%F{blue}"
+    RED="%F{red}"
+    GREEN="%F{green}"
+    CYAN="%F{cyan}"
+    MAGENTA="%F{magenta}"
+    YELLOW="%F{yellow}"
+    WHITE="%F{white}"
+    NO_COLOR="%f"
+elif zrcautoload colors && colors 2>/dev/null ; then
     BLUE="%{${fg[blue]}%}"
     RED="%{${fg_bold[red]}%}"
     GREEN="%{${fg[green]}%}"
@@ -1621,25 +1744,129 @@ PS4='+%N:%i:%_> '
 #    - debian_chroot
 #    - vcs_info setup and version specific fixes
 
-# display battery status on right side of prompt via running 'BATTERY=1 zsh'
-if [[ $BATTERY -gt 0 ]] ; then
-    if ! check_com -c acpi ; then
-        BATTERY=0
-    fi
-fi
+# display battery status on right side of prompt using 'GRML_DISPLAY_BATTERY=1' in .zshrc.pre
 
 battery() {
-if [[ $BATTERY -gt 0 ]] ; then
-    PERCENT="${${"$(acpi 2>/dev/null)"}/(#b)[[:space:]]#Battery <->: [^0-9]##, (<->)%*/${match[1]}}"
-    if [[ -z "$PERCENT" ]] ; then
-        PERCENT='acpi not present'
+if [[ $GRML_DISPLAY_BATTERY -gt 0 ]] ; then
+    if islinux ; then
+        batterylinux
+    elif isopenbsd ; then
+        batteryopenbsd
+    elif isfreebsd ; then
+        batteryfreebsd
+    elif isdarwin ; then
+        batterydarwin
     else
-        if [[ "$PERCENT" -lt 20 ]] ; then
-            PERCENT="warning: ${PERCENT}%%"
-        else
-            PERCENT="${PERCENT}%%"
-        fi
+        #not yet supported
+        GRML_DISPLAY_BATTERY=0
+    fi
+fi
+}
+
+batterylinux(){
+GRML_BATTERY_LEVEL=''
+local batteries bat capacity
+batteries=( /sys/class/power_supply/BAT*(N) )
+if (( $#batteries > 0 )) ; then
+    for bat in $batteries ; do
+        capacity=$(< $bat/capacity)
+        case $(< $bat/status) in
+        Charging)
+            GRML_BATTERY_LEVEL+=" ^"
+            ;;
+        Discharging)
+            if (( capacity < 20 )) ; then
+                GRML_BATTERY_LEVEL+=" !v"
+            else
+                GRML_BATTERY_LEVEL+=" v"
+            fi
+            ;;
+        *) # Full, Unknown
+            GRML_BATTERY_LEVEL+=" ="
+            ;;
+        esac
+        GRML_BATTERY_LEVEL+="${capacity}%%"
+    done
+fi
+}
+
+batteryopenbsd(){
+GRML_BATTERY_LEVEL=''
+local bat batfull batwarn batnow num
+for num in 0 1 ; do
+    bat=$(sysctl -n hw.sensors.acpibat${num} 2>/dev/null)
+    if [[ -n $bat ]]; then
+        batfull=${"$(sysctl -n hw.sensors.acpibat${num}.amphour0)"%% *}
+        batwarn=${"$(sysctl -n hw.sensors.acpibat${num}.amphour1)"%% *}
+        batnow=${"$(sysctl -n hw.sensors.acpibat${num}.amphour3)"%% *}
+        case "$(sysctl -n hw.sensors.acpibat${num}.raw0)" in
+            *" discharging"*)
+                if (( batnow < batwarn )) ; then
+                    GRML_BATTERY_LEVEL+=" !v"
+                else
+                    GRML_BATTERY_LEVEL+=" v"
+                fi
+                ;;
+            *" charging"*)
+                GRML_BATTERY_LEVEL+=" ^"
+                ;;
+            *)
+                GRML_BATTERY_LEVEL+=" ="
+                ;;
+        esac
+        GRML_BATTERY_LEVEL+="${$(( 100 * batnow / batfull ))%%.*}%%"
+    fi
+done
+}
+
+batteryfreebsd(){
+GRML_BATTERY_LEVEL=''
+local num
+local -A table
+for num in 0 1 ; do
+    table=( ${=${${${${${(M)${(f)"$(acpiconf -i $num 2>&1)"}:#(State|Remaining capacity):*}%%( ##|%)}//:[ $'\t']##/@}// /-}//@/ }} )
+    if [[ -n $table ]] && [[ $table[State] != "not-present" ]] ; then
+        case $table[State] in
+            *discharging*)
+                if (( $table[Remaining-capacity] < 20 )) ; then
+                    GRML_BATTERY_LEVEL+=" !v"
+                else
+                    GRML_BATTERY_LEVEL+=" v"
+                fi
+                ;;
+            *charging*)
+                GRML_BATTERY_LEVEL+=" ^"
+                ;;
+            *)
+                GRML_BATTERY_LEVEL+=" ="
+                ;;
+        esac
+        GRML_BATTERY_LEVEL+="$table[Remaining-capacity]%%"
     fi
+done
+}
+
+batterydarwin(){
+GRML_BATTERY_LEVEL=''
+local -a table
+table=( ${$(pmset -g ps)[(w)7,8]%%(\%|);} )
+if [[ -n $table[2] ]] ; then
+    case $table[2] in
+        charging)
+            GRML_BATTERY_LEVEL+=" ^"
+            ;;
+        discharging)
+            if (( $table[1] < 20 )) ; then
+                GRML_BATTERY_LEVEL+=" !v"
+            else
+                GRML_BATTERY_LEVEL+=" v"
+            fi
+            ;;
+        *)
+            GRML_BATTERY_LEVEL+=" ="
+            ;;
+    esac
+    GRML_BATTERY_LEVEL+="$table[1]%%"
 fi
 }
 
@@ -1668,21 +1895,61 @@ if zrcautoload vcs_info; then
     fi
 fi
 
+typeset -A grml_vcs_coloured_formats
+typeset -A grml_vcs_plain_formats
+
+grml_vcs_plain_formats=(
+    format "(%s%)-[%b] "    "zsh: %r"
+    actionformat "(%s%)-[%b|%a] " "zsh: %r"
+    rev-branchformat "%b:%r"
+)
+
+grml_vcs_coloured_formats=(
+    format "${MAGENTA}(${NO_COLOR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${MAGENTA}]${NO_COLOR} "
+    actionformat "${MAGENTA}(${NO_COLOR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${YELLOW}|${RED}%a${MAGENTA}]${NO_COLOR} "
+    rev-branchformat "%b${RED}:${YELLOW}%r"
+)
+
+typeset GRML_VCS_COLOUR_MODE=xxx
+
+grml_vcs_info_toggle_colour () {
+    emulate -L zsh
+    if [[ $GRML_VCS_COLOUR_MODE == plain ]]; then
+        grml_vcs_info_set_formats coloured
+    else
+        grml_vcs_info_set_formats plain
+    fi
+    return 0
+}
+
+grml_vcs_info_set_formats () {
+    emulate -L zsh
+    #setopt localoptions xtrace
+    local mode=$1 AF F BF
+    if [[ $mode == coloured ]]; then
+        AF=${grml_vcs_coloured_formats[actionformat]}
+        F=${grml_vcs_coloured_formats[format]}
+        BF=${grml_vcs_coloured_formats[rev-branchformat]}
+        GRML_VCS_COLOUR_MODE=coloured
+    else
+        AF=${grml_vcs_plain_formats[actionformat]}
+        F=${grml_vcs_plain_formats[format]}
+        BF=${grml_vcs_plain_formats[rev-branchformat]}
+        GRML_VCS_COLOUR_MODE=plain
+    fi
+
+    zstyle ':vcs_info:*'              actionformats "$AF" "zsh: %r"
+    zstyle ':vcs_info:*'              formats       "$F"  "zsh: %r"
+    zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat  "$BF"
+    return 0
+}
+
 # Change vcs_info formats for the grml prompt. The 2nd format sets up
 # $vcs_info_msg_1_ to contain "zsh: repo-name" used to set our screen title.
-# TODO: The included vcs_info() version still uses $VCS_INFO_message_N_.
-#       That needs to be the use of $VCS_INFO_message_N_ needs to be changed
-#       to $vcs_info_msg_N_ as soon as we use the included version.
 if [[ "$TERM" == dumb ]] ; then
-    zstyle ':vcs_info:*' actionformats "(%s%)-[%b|%a] " "zsh: %r"
-    zstyle ':vcs_info:*' formats       "(%s%)-[%b] "    "zsh: %r"
+    grml_vcs_info_set_formats plain
 else
-    # these are the same, just with a lot of colors:
-    zstyle ':vcs_info:*' actionformats "${MAGENTA}(${NO_COLOR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${YELLOW}|${RED}%a${MAGENTA}]${NO_COLOR} " \
-                                       "zsh: %r"
-    zstyle ':vcs_info:*' formats       "${MAGENTA}(${NO_COLOR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${MAGENTA}]${NO_COLOR}%} " \
-                                       "zsh: %r"
-    zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat "%b${RED}:${YELLOW}%r"
+    grml_vcs_info_set_formats coloured
 fi
 
 # Now for the fun part: The grml prompt themes in `promptsys' mode of operation
@@ -1698,7 +1965,7 @@ fi
 # below, which gets called when the user does this: prompt -h grml
 
 function prompt_grml_help () {
-    cat <<__EOF0__
+    <<__EOF0__
   prompt grml
 
     This is the prompt as used by the grml-live system <http://grml.org>. It is
@@ -1755,7 +2022,7 @@ __EOF0__
 }
 
 function prompt_grml-chroot_help () {
-    cat <<__EOF0__
+    <<__EOF0__
   prompt grml-chroot
 
     This is a variation of the grml prompt, see: prompt -h grml
@@ -1769,7 +2036,7 @@ __EOF0__
 }
 
 function prompt_grml-large_help () {
-    cat <<__EOF0__
+    <<__EOF0__
   prompt grml-large
 
     This is a variation of the grml prompt, see: prompt -h grml
@@ -1788,7 +2055,10 @@ __EOF0__
 function grml_prompt_setup () {
     emulate -L zsh
     autoload -Uz vcs_info
-    autoload -Uz add-zsh-hook
+    # The following autoload is disabled for now, since this setup includes a
+    # static version of the ‘add-zsh-hook’ function above. It needs to be
+    # reenabled as soon as that static definition is removed again.
+    #autoload -Uz add-zsh-hook
     add-zsh-hook precmd prompt_$1_precmd
 }
 
@@ -1824,7 +2094,7 @@ grml_prompt_pre_default=(
     newline           ''
     path              '%B'
     percent           ''
-    rc                '%F{red}'
+    rc                '%B%F{red}'
     rc-always         ''
     sad-smiley        ''
     shell-level       '%F{red}'
@@ -1845,7 +2115,7 @@ grml_prompt_post_default=(
     newline           ''
     path              '%b'
     percent           ''
-    rc                '%f'
+    rc                '%f%b'
     rc-always         ''
     sad-smiley        ''
     shell-level       '%f'
@@ -1856,7 +2126,7 @@ grml_prompt_post_default=(
 
 grml_prompt_token_default=(
     at                '@'
-    battery           'PERCENT'
+    battery           'GRML_BATTERY_LEVEL'
     change-root       'debian_chroot'
     date              '%D{%Y-%m-%d}'
     grml-chroot       'GRML_CHROOT'
@@ -1875,8 +2145,16 @@ grml_prompt_token_default=(
     vcs               '0'
 )
 
+function grml_theme_has_token () {
+    if (( ARGC != 1 )); then
+        printf 'usage: grml_theme_has_token <name>\n'
+        return 1
+    fi
+    (( ${+grml_prompt_token_default[$1]} ))
+}
+
 function GRML_theme_add_token_usage () {
-    cat <<__EOF__
+    <<__EOF0__
   Usage: grml_theme_add_token <name> [-f|-i] <token/function> [<pre> <post>]
 
     <name> is the name for the newly added token. If the \`-f' or \`-i' options
@@ -1896,6 +2174,11 @@ function GRML_theme_add_token_usage () {
     return value is expected in the \$REPLY parameter. The use of these
     options is mutually exclusive.
 
+    There is a utility function \`grml_theme_has_token', which you can use
+    to test if a token exists before trying to add it. This can be a guard
+    for situations in which a \`grml_theme_add_token' call may happen more
+    than once.
+
   Example:
 
     To add a new token \`day' that expands to the current weekday in the
@@ -1912,7 +2195,7 @@ function GRML_theme_add_token_usage () {
 
     After that, you will be able to use a changed \`items' style to
     assemble your prompt.
-__EOF__
+__EOF0__
 }
 
 function grml_theme_add_token () {
@@ -1959,7 +2242,7 @@ grml_theme_add_token: <pre> and <post> need to by specified _both_!\n\n'
         shift 2
     fi
 
-    if (( ${+grml_prompt_token_default[$name]} )); then
+    if grml_theme_has_token $name; then
         printf '
 grml_theme_add_token: Token `%s'\'' exists! Giving up!\n\n' $name
         GRML_theme_add_token_usage
@@ -2004,7 +2287,7 @@ function grml_prompt_addto () {
     for it in "${items[@]}"; do
         zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" pre apre \
             || apre=${grml_prompt_pre_default[$it]}
-        zstyle -s ":prompt:grml:${grmltheme}:${lr}:$it" post apost \
+        zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" post apost \
             || apost=${grml_prompt_post_default[$it]}
         zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" token new \
             || new=${grml_prompt_token_default[$it]}
@@ -2114,7 +2397,7 @@ fi
 if is437; then
     # The prompt themes use modern features of zsh, that require at least
     # version 4.3.7 of the shell. Use the fallback otherwise.
-    if [[ $BATTERY -gt 0 ]]; then
+    if [[ $GRML_DISPLAY_BATTERY -gt 0 ]]; then
         zstyle ':prompt:grml:right:setup' items sad-smiley battery
         add-zsh-hook precmd battery
     fi
@@ -2128,7 +2411,7 @@ if is437; then
         unset i
         zstyle ':prompt:grml(|-large|-chroot):right:setup' use-rprompt false
     elif (( EUID == 0 )); then
-        zstyle ':prompt:grml(|-large|-chroot):*:items:user' pre '%F{red}'
+        zstyle ':prompt:grml(|-large|-chroot):*:items:user' pre '%B%F{red}'
     fi
 
     # Finally enable one of the prompts.
@@ -2207,7 +2490,10 @@ function grml_control_xterm_title () {
     esac
 }
 
-zrcautoload add-zsh-hook || add-zsh-hook () { :; }
+# The following autoload is disabled for now, since this setup includes a
+# static version of the ‘add-zsh-hook’ function above. It needs to be
+# reenabled as soon as that static definition is removed again.
+#zrcautoload add-zsh-hook || add-zsh-hook () { :; }
 if [[ $NOPRECMD -eq 0 ]]; then
     add-zsh-hook precmd grml_reset_screen_title
     add-zsh-hook precmd grml_vcs_to_screen_title
@@ -2226,8 +2512,6 @@ hash -d linux=/lib/modules/$(command uname -r)/build/
 hash -d log=/var/log
 hash -d slog=/var/log/syslog
 hash -d src=/usr/src
-hash -d templ=/usr/share/doc/grml-templates
-hash -d tt=/usr/share/doc/texttools-doc
 hash -d www=/var/www
 #d# end
 
@@ -2235,16 +2519,14 @@ hash -d www=/var/www
 if check_com -c screen ; then
     if [[ $UID -eq 0 ]] ; then
         if [[ -r /etc/grml/screenrc ]]; then
-            alias screen="${commands[screen]} -c /etc/grml/screenrc"
+            alias screen='screen -c /etc/grml/screenrc'
         fi
-    elif [[ -r $HOME/.screenrc ]] ; then
-        alias screen="${commands[screen]} -c $HOME/.screenrc"
-    else
+    elif [[ ! -r $HOME/.screenrc ]] ; then
         if [[ -r /etc/grml/screenrc_grml ]]; then
-            alias screen="${commands[screen]} -c /etc/grml/screenrc_grml"
+            alias screen='screen -c /etc/grml/screenrc_grml'
         else
             if [[ -r /etc/grml/screenrc ]]; then
-                alias screen="${commands[screen]} -c /etc/grml/screenrc"
+                alias screen='screen -c /etc/grml/screenrc'
             fi
         fi
     fi
@@ -2252,22 +2534,21 @@ fi
 
 # do we have GNU ls with color-support?
 if [[ "$TERM" != dumb ]]; then
-    #a1# List files with colors (\kbd{ls -b -CF \ldots})
-    alias ls='ls -b -CF '${ls_options:+"${ls_options[*]}"}
+    #a1# List files with colors (\kbd{ls \ldots})
+    alias ls="command ls ${ls_options:+${ls_options[*]}}"
     #a1# List all files, with colors (\kbd{ls -la \ldots})
-    alias la='ls -la '${ls_options:+"${ls_options[*]}"}
+    alias la="command ls -la ${ls_options:+${ls_options[*]}}"
     #a1# List files with long colored list, without dotfiles (\kbd{ls -l \ldots})
-    alias ll='ls -l '${ls_options:+"${ls_options[*]}"}
+    alias ll="command ls -l ${ls_options:+${ls_options[*]}}"
     #a1# List files with long colored list, human readable sizes (\kbd{ls -hAl \ldots})
-    alias lh='ls -hAl '${ls_options:+"${ls_options[*]}"}
-    #a1# List files with long colored list, append qualifier to filenames (\kbd{ls -lF \ldots})\\&\quad(\kbd{/} for directories, \kbd{@} for symlinks ...)
-    alias l='ls -lF '${ls_options:+"${ls_options[*]}"}
+    alias lh="command ls -hAl ${ls_options:+${ls_options[*]}}"
+    #a1# List files with long colored list, append qualifier to filenames (\kbd{ls -l \ldots})\\&\quad(\kbd{/} for directories, \kbd{@} for symlinks ...)
+    alias l="command ls -l ${ls_options:+${ls_options[*]}}"
 else
-    alias ls='ls -b -CF'
-    alias la='ls -la'
-    alias ll='ls -l'
-    alias lh='ls -hAl'
-    alias l='ls -lF'
+    alias la='command ls -la'
+    alias ll='command ls -l'
+    alias lh='command ls -hAl'
+    alias l='command ls -l'
 fi
 
 alias mdstat='cat /proc/mdstat'
@@ -2286,6 +2567,7 @@ alias term2utf="echo 'Setting terminal to utf-8 mode'; print -n '\e%G'"
 [[ -n ${aliases[utf2iso]} ]] && unalias utf2iso
 utf2iso() {
     if isutfenv ; then
+        local ENV
         for ENV in $(env | command grep -i '.utf') ; do
             eval export "$(echo $ENV | sed 's/UTF-8/iso885915/ ; s/utf8/iso885915/')"
         done
@@ -2296,6 +2578,7 @@ utf2iso() {
 [[ -n ${aliases[iso2utf]} ]] && unalias iso2utf
 iso2utf() {
     if ! isutfenv ; then
+        local ENV
         for ENV in $(env | command grep -i '\.iso') ; do
             eval export "$(echo $ENV | sed 's/iso.*/UTF-8/ ; s/ISO.*/UTF-8/')"
         done
@@ -2357,7 +2640,8 @@ the zsh yet. :)
   "NOPRECMD=1 zsh" => disable the precmd + preexec commands (set GNU screen title)
   "NOTITLE=1  zsh" => disable setting the title of xterms without disabling
                       preexec() and precmd() completely
-  "BATTERY=1  zsh" => activate battery status (via acpi) on right side of prompt
+  "GRML_DISPLAY_BATTERY=1  zsh"
+                   => activate battery status on right side of prompt (WIP)
   "COMMAND_NOT_FOUND=1 zsh"
                    => Enable a handler if an external command was not found
                       The command called in the handler can be altered by setting
@@ -2403,7 +2687,7 @@ if [[ -r /etc/debian_version ]] ; then
     # get a root shell as normal user in live-cd mode:
     if isgrmlcd && [[ $UID -ne 0 ]] ; then
        alias su="sudo su"
-     fi
+    fi
 
     #a1# Take a look at the syslog: \kbd{\$PAGER /var/log/syslog}
     salias llog="$PAGER /var/log/syslog"     # take a look at the syslog
@@ -2421,22 +2705,21 @@ fi
 if [[ -L /usr/bin/cdrecord ]] || ! check_com -c cdrecord; then
     if check_com -c wodim; then
         cdrecord() {
-            cat <<EOMESS
+            <<__EOF0__
 cdrecord is not provided under its original name by Debian anymore.
 See #377109 in the BTS of Debian for more details.
 
 Please use the wodim binary instead
-EOMESS
+__EOF0__
             return 1
         }
     fi
 fi
 
-# Use hard limits, except for a smaller stack and no core dumps
-unlimit
-is425 && limit stack 8192
-isgrmlcd && limit core 0 # important for a live-cd-system
-limit -s
+if isgrmlcd; then
+    # No core dumps: important for a live-cd-system
+    limit -s core 0
+fi
 
 # grmlstuff
 grmlstuff() {
@@ -2488,41 +2771,129 @@ is4 && nt() {
 freload() { while (( $# )); do; unfunction $1; autoload -U $1; shift; done }
 compdef _functions freload
 
-#f1# List symlinks in detail (more detailed version of 'readlink -f' and 'whence -s')
+#
+# Usage:
+#
+#      e.g.:   a -> b -> c -> d  ....
+#
+#      sll a
+#
+#
+#      if parameter is given with leading '=', lookup $PATH for parameter and resolve that
+#
+#      sll =java
+#
+#      Note: limit for recursive symlinks on linux:
+#            http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/namei.c?id=refs/heads/master#l808
+#            This limits recursive symlink follows to 8,
+#            while limiting consecutive symlinks to 40.
+#
+#      When resolving and displaying information about symlinks, no check is made
+#      that the displayed information does make any sense on your OS.
+#      We leave that decission to the user.
+#
+#      The zstat module is used to detect symlink loops. zstat is available since zsh4.
+#      With an older zsh you will need to abort with <C-c> in that case.
+#      When a symlink loop is detected, a warning ist printed and further processing is stopped.
+#
+#      Module zstat is loaded by default in grml zshrc, no extra action needed for that.
+#
+#      Known bugs:
+#      If you happen to come accross a symlink that points to a destination on an other partition
+#      with the same inode number, that will be marked as symlink loop though it is not.
+#      Two hints for this situation:
+#      I)  Play lottery the same day, as you seem to be rather lucky right now.
+#      II) Send patches.
+#
+#      return status:
+#      0 upon success
+#      1 file/dir not accesible
+#      2 symlink loop detected
+#
+#f1# List symlinks in detail (more detailed version of 'readlink -f', 'whence -s' and 'namei -l')
 sll() {
-    [[ -z "$1" ]] && printf 'Usage: %s <file(s)>\n' "$0" && return 1
-    for file in "$@" ; do
+    if [[ -z ${1} ]] ; then
+        printf 'Usage: %s <symlink(s)>\n' "${0}"
+        return 1
+    fi
+
+    local file jumpd curdir
+    local -i RTN LINODE i
+    local -a SEENINODES
+    curdir="${PWD}"
+    RTN=0
+
+    for file in "${@}" ; do
+        SEENINODES=()
+        ls -l "${file:a}"   || RTN=1
+
         while [[ -h "$file" ]] ; do
-            ls -l $file
+            if is4 ; then
+                LINODE=$(zstat -L +inode "${file}")
+                for i in ${SEENINODES} ; do
+                    if (( ${i} == ${LINODE} )) ; then
+                        builtin cd "${curdir}"
+                        print "link loop detected, aborting!"
+                        return 2
+                    fi
+                done
+                SEENINODES+=${LINODE}
+            fi
+            jumpd="${file:h}"
+            file="${file:t}"
+
+            if [[ -d ${jumpd} ]] ; then
+                builtin cd "${jumpd}"  || RTN=1
+            fi
             file=$(readlink "$file")
+
+            jumpd="${file:h}"
+            file="${file:t}"
+
+            if [[ -d ${jumpd} ]] ; then
+                builtin cd "${jumpd}"  || RTN=1
+            fi
+
+            ls -l "${PWD}/${file}"     || RTN=1
         done
+        shift 1
+        if (( ${#} >= 1 )) ; then
+            print ""
+        fi
+        builtin cd "${curdir}"
     done
+    return ${RTN}
 }
 
 # TODO: Is it supported to use pager settings like this?
 #   PAGER='less -Mr' - If so, the use of $PAGER here needs fixing
 # with respect to wordsplitting. (ie. ${=PAGER})
 if check_com -c $PAGER ; then
-    #f3# View Debian's changelog of a given package
+    #f3# View Debian's changelog of given package(s)
     dchange() {
         emulate -L zsh
-        if [[ -r /usr/share/doc/$1/changelog.Debian.gz ]] ; then
-            $PAGER /usr/share/doc/$1/changelog.Debian.gz
-        elif [[ -r /usr/share/doc/$1/changelog.gz ]] ; then
-            $PAGER /usr/share/doc/$1/changelog.gz
-        else
-            if check_com -c aptitude ; then
-                echo "No changelog for package $1 found, using aptitude to retrieve it."
-                if isgrml ; then
-                    aptitude -t unstable changelog $1
+        [[ -z "$1" ]] && printf 'Usage: %s <package_name(s)>\n' "$0" && return 1
+
+        local package
+        for package in "$@" ; do
+            if [[ -r /usr/share/doc/${package}/changelog.Debian.gz ]] ; then
+                $PAGER /usr/share/doc/${package}/changelog.Debian.gz
+            elif [[ -r /usr/share/doc/${package}/changelog.gz ]] ; then
+                $PAGER /usr/share/doc/${package}/changelog.gz
+            elif [[ -r /usr/share/doc/${package}/changelog ]] ; then
+                $PAGER /usr/share/doc/${package}/changelog
+            else
+                if check_com -c aptitude ; then
+                    echo "No changelog for package $package found, using aptitude to retrieve it."
+                    aptitude changelog "$package"
+                elif check_com -c apt-get ; then
+                    echo "No changelog for package $package found, using apt-get to retrieve it."
+                    apt-get changelog "$package"
                 else
-                    aptitude changelog $1
+                    echo "No changelog for package $package found, sorry."
                 fi
-            else
-                echo "No changelog for package $1 found, sorry."
-                return 1
             fi
-        fi
+        done
     }
     _dchange() { _files -W /usr/share/doc -/ }
     compdef _dchange dchange
@@ -2573,7 +2944,7 @@ fi
 
 # zsh profiling
 profile() {
-    ZSH_PROFILE_RC=1 $SHELL "$@"
+    ZSH_PROFILE_RC=1 zsh "$@"
 }
 
 #f1# Edit an alias via zle
@@ -2589,12 +2960,12 @@ edfunc() {
 compdef _functions edfunc
 
 # use it e.g. via 'Restart apache2'
-#m# f6 Start() \kbd{/etc/init.d/\em{process}}\quad\kbd{start}
-#m# f6 Restart() \kbd{/etc/init.d/\em{process}}\quad\kbd{restart}
-#m# f6 Stop() \kbd{/etc/init.d/\em{process}}\quad\kbd{stop}
-#m# f6 Reload() \kbd{/etc/init.d/\em{process}}\quad\kbd{reload}
-#m# f6 Force-Reload() \kbd{/etc/init.d/\em{process}}\quad\kbd{force-reload}
-#m# f6 Status() \kbd{/etc/init.d/\em{process}}\quad\kbd{status}
+#m# f6 Start() \kbd{service \em{process}}\quad\kbd{start}
+#m# f6 Restart() \kbd{service \em{process}}\quad\kbd{restart}
+#m# f6 Stop() \kbd{service \em{process}}\quad\kbd{stop}
+#m# f6 Reload() \kbd{service \em{process}}\quad\kbd{reload}
+#m# f6 Force-Reload() \kbd{service \em{process}}\quad\kbd{force-reload}
+#m# f6 Status() \kbd{service \em{process}}\quad\kbd{status}
 if [[ -d /etc/init.d || -d /etc/service ]] ; then
     __start_stop() {
         local action_="${1:l}"  # e.g Start/Stop/Restart
@@ -2615,8 +2986,12 @@ if [[ -d /etc/init.d || -d /etc/service ]] ; then
                 *) $SUDO "/etc/init.d/$service_" "${action_}" "$param_" ;;
             esac
         else
-            # sysvinit
-            $SUDO "/etc/init.d/$service_" "${action_}" "$param_"
+            # sysv/sysvinit-utils, upstart
+            if check_com -c service ; then
+              $SUDO service "$service_" "${action_}" "$param_"
+            else
+              $SUDO "/etc/init.d/$service_" "${action_}" "$param_"
+            fi
         fi
     }
 
@@ -2630,6 +3005,7 @@ if [[ -d /etc/init.d || -d /etc/service ]] ; then
         eval "$i() { __start_stop $i \"\$1\" \"\$2\" ; }"
         compdef _grmlinitd $i
     done
+    builtin unset -v i
 fi
 
 #f1# Provides useful information on globbing
@@ -2708,11 +3084,6 @@ if check_com vim; then
     }
 fi
 
-# make a backup of a file
-bk() {
-    cp -a "$1" "${1}_$(date --iso-8601=seconds)"
-}
-
 ssl_hashes=( sha512 sha256 sha1 md5 )
 
 for sh in ${ssl_hashes}; do
@@ -2749,7 +3120,8 @@ ssl-cert-info() {
 }
 
 # make sure our environment is clean regarding colors
-for color in BLUE RED GREEN CYAN YELLOW MAGENTA WHITE ; unset $color
+for var in BLUE RED GREEN CYAN YELLOW MAGENTA WHITE ; unset $var
+builtin unset -v var
 
 # "persistent history"
 # just write important commands you always need to ~/.important_commands
@@ -2770,40 +3142,38 @@ export COLORTERM="yes"
 # general
 #a2# Execute \kbd{du -sch}
 alias da='du -sch'
-#a2# Execute \kbd{jobs -l}
-alias j='jobs -l'
 
 # listing stuff
 #a2# Execute \kbd{ls -lSrah}
-alias dir="ls -lSrah"
+alias dir="command ls -lSrah"
 #a2# Only show dot-directories
-alias lad='ls -d .*(/)'
+alias lad='command ls -d .*(/)'
 #a2# Only show dot-files
-alias lsa='ls -a .*(.)'
+alias lsa='command ls -a .*(.)'
 #a2# Only files with setgid/setuid/sticky flag
-alias lss='ls -l *(s,S,t)'
+alias lss='command ls -l *(s,S,t)'
 #a2# Only show symlinks
-alias lsl='ls -l *(@)'
+alias lsl='command ls -l *(@)'
 #a2# Display only executables
-alias lsx='ls -l *(*)'
+alias lsx='command ls -l *(*)'
 #a2# Display world-{readable,writable,executable} files
-alias lsw='ls -ld *(R,W,X.^ND/)'
+alias lsw='command ls -ld *(R,W,X.^ND/)'
 #a2# Display the ten biggest files
-alias lsbig="ls -flh *(.OL[1,10])"
+alias lsbig="command ls -flh *(.OL[1,10])"
 #a2# Only show directories
-alias lsd='ls -d *(/)'
+alias lsd='command ls -d *(/)'
 #a2# Only show empty directories
-alias lse='ls -d *(/^F)'
+alias lse='command ls -d *(/^F)'
 #a2# Display the ten newest files
-alias lsnew="ls -rtlh *(D.om[1,10])"
+alias lsnew="command ls -rtlh *(D.om[1,10])"
 #a2# Display the ten oldest files
-alias lsold="ls -rtlh *(D.Om[1,10])"
+alias lsold="command ls -rtlh *(D.Om[1,10])"
 #a2# Display the ten smallest files
-alias lssmall="ls -Srl *(.oL[1,10])"
+alias lssmall="command ls -Srl *(.oL[1,10])"
 #a2# Display the ten newest directories and ten newest .directories
-alias lsnewdir="ls -rthdl *(/om[1,10]) .*(D/om[1,10])"
+alias lsnewdir="command ls -rthdl *(/om[1,10]) .*(D/om[1,10])"
 #a2# Display the ten oldest directories and ten oldest .directories
-alias lsolddir="ls -rthdl *(/Om[1,10]) .*(D/Om[1,10])"
+alias lsolddir="command ls -rthdl *(/Om[1,10]) .*(D/Om[1,10])"
 
 # some useful aliases
 #a2# Remove current empty directory. Execute \kbd{cd ..; rmdir \$OLDCWD}
@@ -2834,10 +3204,79 @@ fi
 
 # useful functions
 
-#f5# Backup \kbd{file {\rm to} file\_timestamp}
+#f5# Backup \kbd{file_or_folder {\rm to} file_or_folder\_timestamp}
 bk() {
     emulate -L zsh
-    cp -b $1 $1_`date --iso-8601=m`
+    local current_date=$(date -u "+%Y-%m-%dT%H:%M:%SZ")
+    local clean keep move verbose result all to_bk
+    setopt extended_glob
+    keep=1
+    while getopts ":hacmrv" opt; do
+        case $opt in
+            a) (( all++ ));;
+            c) unset move clean && (( ++keep ));;
+            m) unset keep clean && (( ++move ));;
+            r) unset move keep && (( ++clean ));;
+            v) verbose="-v";;
+            h) <<__EOF0__
+bk [-hcmv] FILE [FILE ...]
+bk -r [-av] [FILE [FILE ...]]
+Backup a file or folder in place and append the timestamp
+Remove backups of a file or folder, or all backups in the current directory
+
+Usage:
+-h    Display this help text
+-c    Keep the file/folder as is, create a copy backup using cp(1) (default)
+-m    Move the file/folder, using mv(1)
+-r    Remove backups of the specified file or directory, using rm(1). If none
+      is provided, remove all backups in the current directory.
+-a    Remove all (even hidden) backups.
+-v    Verbose
+
+The -c, -r and -m options are mutually exclusive. If specified at the same time,
+the last one is used.
+
+The return code is the sum of all cp/mv/rm return codes.
+__EOF0__
+return 0;;
+            \?) bk -h >&2; return 1;;
+        esac
+    done
+    shift "$((OPTIND-1))"
+    if (( keep > 0 )); then
+        if islinux || isfreebsd; then
+            for to_bk in "$@"; do
+                cp $verbose -a "${to_bk%/}" "${to_bk%/}_$current_date"
+                (( result += $? ))
+            done
+        else
+            for to_bk in "$@"; do
+                cp $verbose -pR "${to_bk%/}" "${to_bk%/}_$current_date"
+                (( result += $? ))
+            done
+        fi
+    elif (( move > 0 )); then
+        while (( $# > 0 )); do
+            mv $verbose "${1%/}" "${1%/}_$current_date"
+            (( result += $? ))
+            shift
+        done
+    elif (( clean > 0 )); then
+        if (( $# > 0 )); then
+            for to_bk in "$@"; do
+                rm $verbose -rf "${to_bk%/}"_[0-9](#c4,)-(0[0-9]|1[0-2])-([0-2][0-9]|3[0-1])T([0-1][0-9]|2[0-3])(:[0-5][0-9])(#c2)Z
+                (( result += $? ))
+            done
+        else
+            if (( all > 0 )); then
+                rm $verbose -rf *_[0-9](#c4,)-(0[0-9]|1[0-2])-([0-2][0-9]|3[0-1])T([0-1][0-9]|2[0-3])(:[0-5][0-9])(#c2)Z(D)
+            else
+                rm $verbose -rf *_[0-9](#c4,)-(0[0-9]|1[0-2])-([0-2][0-9]|3[0-1])T([0-1][0-9]|2[0-3])(:[0-5][0-9])(#c2)Z
+            fi
+            (( result += $? ))
+        fi
+    fi
+    return $result
 }
 
 #f5# cd to directoy and list files
@@ -2873,10 +3312,8 @@ mkcd() {
 
 #f5# Create temporary directory and \kbd{cd} to it
 cdt() {
-    local t
-    t=$(mktemp -d)
-    echo "$t"
-    builtin cd "$t"
+    builtin cd "$(mktemp -d)"
+    builtin pwd
 }
 
 #f5# List files which have been accessed within the last {\it n} days, {\it n} defaults to 1
@@ -2900,16 +3337,21 @@ modified() {
 check_com new || alias new=modified
 
 # use colors when GNU grep with color-support
-#a2# Execute \kbd{grep -{}-color=auto}
-(( $#grep_options > 0 )) && alias grep='grep '${grep_options:+"${grep_options[*]}"}
+if (( $#grep_options > 0 )); then
+    o=${grep_options:+"${grep_options[*]}"}
+    #a2# Execute \kbd{grep -{}-color=auto}
+    alias grep='grep '$o
+    alias egrep='egrep '$o
+    unset o
+fi
 
 # Translate DE<=>EN
-# 'translate' looks up fot a word in a file with language-to-language
+# 'translate' looks up a word in a file with language-to-language
 # translations (field separator should be " : "). A typical wordlist looks
-# like at follows:
-#  | english-word : german-transmission
+# like the following:
+#  | english-word : german-translation
 # It's also only possible to translate english to german but not reciprocal.
-# Use the following oneliner to turn back the sort order:
+# Use the following oneliner to reverse the sort order:
 #  $ awk -F ':' '{ print $2" : "$1" "$3 }' \
 #    /usr/local/lib/words/en-de.ISO-8859-1.vok > ~/.translate/de-en.ISO-8859-1.vok
 #f5# Translates a word
@@ -2935,7 +3377,7 @@ trans() {
 simple-extract() {
     emulate -L zsh
     setopt extended_glob noclobber
-    local DELETE_ORIGINAL DECOMP_CMD USES_STDIN USES_STDOUT GZTARGET WGET_CMD
+    local ARCHIVE DELETE_ORIGINAL DECOMP_CMD USES_STDIN USES_STDOUT GZTARGET WGET_CMD
     local RC=0
     zparseopts -D -E "d=DELETE_ORIGINAL"
     for ARCHIVE in "${@}"; do
@@ -3034,11 +3476,13 @@ simple-extract() {
 
         elif [[ "$ARCHIVE" == (#s)(https|http|ftp)://* ]] ; then
             if check_com curl; then
-                WGET_CMD="curl -L -k -s -o -"
+                WGET_CMD="curl -L -s -o -"
             elif check_com wget; then
-                WGET_CMD="wget -q -O - --no-check-certificate"
+                WGET_CMD="wget -q -O -"
+            elif check_com fetch; then
+                WGET_CMD="fetch -q -o -"
             else
-                print "ERROR: neither wget nor curl is installed" >&2
+                print "ERROR: neither wget, curl nor fetch is installed" >&2
                 RC=$((RC+4))
                 continue
             fi
@@ -3083,17 +3527,6 @@ _simple_extract()
 compdef _simple_extract simple-extract
 alias se=simple-extract
 
-#f5# Set all ulimit parameters to \kbd{unlimited}
-allulimit() {
-    ulimit -c unlimited
-    ulimit -d unlimited
-    ulimit -f unlimited
-    ulimit -l unlimited
-    ulimit -n unlimited
-    ulimit -s unlimited
-    ulimit -t unlimited
-}
-
 #f5# Change the xterm title from within GNU-screen
 xtrename() {
     emulate -L zsh
@@ -3216,6 +3649,7 @@ if check_com -c hg ; then
     #f5# GNU like diff for mercurial
     hgdi() {
         emulate -L zsh
+        local i
         for i in $(hg status -marn "$@") ; diff -ubwd <(hg cat "$i") "$i"
     }