zshrc: New prompt: Fix wrong logic for user name colouring
[grml-etc-core.git] / etc / zsh / zshrc
index f5b943f..4fa913e 100644 (file)
@@ -109,7 +109,7 @@ if [[ $ZSH_PROFILE_RC -gt 0 ]] ; then
 fi
 
 # load .zshrc.pre to give the user the chance to overwrite the defaults
-[[ -r ${HOME}/.zshrc.pre ]] && source ${HOME}/.zshrc.pre
+[[ -r ${ZDOTDIR:-${HOME}}/.zshrc.pre ]] && source ${ZDOTDIR:-${HOME}}/.zshrc.pre
 
 # check for version/system
 # check for versions (compatibility reasons)
@@ -179,6 +179,11 @@ isdarwin(){
     return 1
 }
 
+isfreebsd(){
+    [[ $OSTYPE == freebsd* ]] && return 0
+    return 1
+}
+
 #f1# are we running within an utf environment?
 isutfenv() {
     case "$LANG $CHARSET $LANGUAGE" in
@@ -237,7 +242,7 @@ function zrcautoload() {
     local -i ffound
 
     ffile=$1
-    (( found = 0 ))
+    (( ffound = 0 ))
     for fdir in ${fpath} ; do
         [[ -e ${fdir}/${ffile} ]] && (( ffound = 1 ))
     done
@@ -497,7 +502,7 @@ xunfunction() {
 # modifications in ~/.zshrc.local
 zrclocal() {
     xsource "/etc/zsh/zshrc.local"
-    xsource "${HOME}/.zshrc.local"
+    xsource "${ZDOTDIR:-${HOME}}/.zshrc.local"
     return 0
 }
 
@@ -533,8 +538,9 @@ export SHELL='/bin/zsh'
 
 # color setup for ls:
 check_com -c dircolors && eval $(dircolors -b)
-# color setup for ls on OS X:
+# color setup for ls on OS X / FreeBSD:
 isdarwin && export CLICOLOR=1
+isfreebsd && export CLICOLOR=1
 
 # do MacPorts setup on darwin
 if isdarwin && [[ -d /opt/local ]]; then
@@ -549,7 +555,7 @@ isdarwin && xsource /sw/bin/init.sh
 # load our function and completion directories
 for fdir in /usr/share/grml/zsh/completion /usr/share/grml/zsh/functions; do
     fpath=( ${fdir} ${fdir}/**/*(/N) ${fpath} )
-    if [[ ${fpath} == '/usr/share/grml/zsh/functions' ]] ; then
+    if [[ ${fdir} == '/usr/share/grml/zsh/functions' ]] ; then
         for func in ${fdir}/**/[^_]*[^~](N.) ; do
             zrcautoload ${func:t}
         done
@@ -579,6 +585,22 @@ watch=(notme root)
 # automatically remove duplicates from these arrays
 typeset -U path cdpath fpath manpath
 
+# Remove zle-line-{init,finish} if it looks like it turns smkx. This would be
+# better fixed by working with those modes too, but we use way too many
+# hardcoded bindings for now.
+function remove_widget () {
+    local name=$1
+    local cap=$2
+    if (( ${+functions[$name]} )) && [[ ${functions[$name]} == *${cap}* ]]; then
+        local w=${widgets[$name]}
+        zle -D $name
+        [[ $w == user:* ]] && unfunction ${w#*:}
+    fi
+}
+remove_widget zle-line-init smkx
+remove_widget zle-line-finish rmkx
+unfunction remove_widget
+
 # keybindings
 if [[ "$TERM" != emacs ]] ; then
     [[ -z "$terminfo[kdch1]" ]] || bindkey -M emacs "$terminfo[kdch1]" delete-char
@@ -898,7 +920,7 @@ abk=(
     '....' '../../..'
     'BG'   '& exit'
     'C'    '| wc -l'
-    'G'    '|& grep '${grep_options:+"${grep_options[*]} "}
+    'G'    '|& grep '${grep_options:+"${grep_options[*]}"}
     'H'    '| head'
     'Hl'   ' --help |& less -r'    #d (Display help in pager)
     'L'    '| less'
@@ -924,8 +946,7 @@ zleiab() {
         return 0
     fi
 
-    matched_chars='[.-|_a-zA-Z0-9]#'
-    LBUFFER=${LBUFFER%%(#m)[.-|_a-zA-Z0-9]#}
+    LBUFFER=${LBUFFER%%(#m)[.\-+:|_a-zA-Z0-9]#}
     LBUFFER+=${abk[$MATCH]:-$MATCH}
 }
 
@@ -963,7 +984,6 @@ if zrcautoload compinit ; then
     compinit || print 'Notice: no compinit available :('
 else
     print 'Notice: no compinit available :('
-    function zstyle { }
     function compdef { }
 fi
 
@@ -1105,17 +1125,17 @@ fi
 
 # history
 
-ZSHDIR=$HOME/.zsh
+ZSHDIR=${ZDOTDIR:-${HOME}/.zsh}
 
 #v#
-HISTFILE=$HOME/.zsh_history
+HISTFILE=${ZDOTDIR:-${HOME}}/.zsh_history
 isgrmlcd && HISTSIZE=500  || HISTSIZE=5000
 isgrmlcd && SAVEHIST=1000 || SAVEHIST=10000 # useful for setopt append_history
 
 # dirstack handling
 
 DIRSTACKSIZE=${DIRSTACKSIZE:-20}
-DIRSTACKFILE=${DIRSTACKFILE:-${HOME}/.zdirs}
+DIRSTACKFILE=${DIRSTACKFILE:-${ZDOTDIR:-${HOME}}/.zdirs}
 
 if [[ -f ${DIRSTACKFILE} ]] && [[ ${#dirstack[*]} -eq 0 ]] ; then
     dirstack=( ${(f)"$(< $DIRSTACKFILE)"} )
@@ -1137,85 +1157,39 @@ chpwd() {
 
 if is433 ; then
 
-CHPWD_PROFILE='default'
+# chpwd_profiles(): Directory Profiles, Quickstart:
+#
+# In .zshrc.local:
+#
+#   zstyle ':chpwd:profiles:/usr/src/grml(|/|/*)'   profile grml
+#   zstyle ':chpwd:profiles:/usr/src/debian(|/|/*)' profile debian
+#   chpwd_profiles
+#
+# For details see the `grmlzshrc.5' manual page.
 function chpwd_profiles() {
-    # Say you want certain settings to be active in certain directories.
-    # This is what you want.
-    #
-    # zstyle ':chpwd:profiles:/usr/src/grml(|/|/*)'   profile grml
-    # zstyle ':chpwd:profiles:/usr/src/debian(|/|/*)' profile debian
-    #
-    # When that's done and you enter a directory that matches the pattern
-    # in the third part of the context, a function called chpwd_profile_grml,
-    # for example, is called (if it exists).
-    #
-    # If no pattern matches (read: no profile is detected) the profile is
-    # set to 'default', which means chpwd_profile_default is attempted to
-    # be called.
-    #
-    # A word about the context (the ':chpwd:profiles:*' stuff in the zstyle
-    # command) which is used: The third part in the context is matched against
-    # ${PWD}. That's why using a pattern such as /foo/bar(|/|/*) makes sense.
-    # Because that way the profile is detected for all these values of ${PWD}:
-    #   /foo/bar
-    #   /foo/bar/
-    #   /foo/bar/baz
-    # So, if you want to make double damn sure a profile works in /foo/bar
-    # and everywhere deeper in that tree, just use (|/|/*) and be happy.
-    #
-    # The name of the detected profile will be available in a variable called
-    # 'profile' in your functions. You don't need to do anything, it'll just
-    # be there.
-    #
-    # Then there is the parameter $CHPWD_PROFILE is set to the profile, that
-    # was is currently active. That way you can avoid running code for a
-    # profile that is already active, by running code such as the following
-    # at the start of your function:
-    #
-    # function chpwd_profile_grml() {
-    #     [[ ${profile} == ${CHPWD_PROFILE} ]] && return 1
-    #   ...
-    # }
-    #
-    # The initial value for $CHPWD_PROFILE is 'default'.
-    #
-    # Version requirement:
-    #   This feature requires zsh 4.3.3 or newer.
-    #   If you use this feature and need to know whether it is active in your
-    #   current shell, there are several ways to do that. Here are two simple
-    #   ways:
-    #
-    #   a) If knowing if the profiles feature is active when zsh starts is
-    #      good enough for you, you can put the following snippet into your
-    #      .zshrc.local:
-    #
-    #   (( ${+functions[chpwd_profiles]} )) && print "directory profiles active"
-    #
-    #   b) If that is not good enough, and you would prefer to be notified
-    #      whenever a profile changes, you can solve that by making sure you
-    #      start *every* profile function you create like this:
-    #
-    #   function chpwd_profile_myprofilename() {
-    #       [[ ${profile} == ${CHPWD_PROFILE} ]] && return 1
-    #       print "chpwd(): Switching to profile: $profile"
-    #     ...
-    #   }
-    #
-    #      That makes sure you only get notified if a profile is *changed*,
-    #      not everytime you change directory, which would probably piss
-    #      you off fairly quickly. :-)
-    #
-    # There you go. Now have fun with that.
-    local -x profile
-
-    zstyle -s ":chpwd:profiles:${PWD}" profile profile || profile='default'
-    if (( ${+functions[chpwd_profile_$profile]} )) ; then
-        chpwd_profile_${profile}
+    local profile context
+    local -i reexecute
+
+    context=":chpwd:profiles:$PWD"
+    zstyle -s "$context" profile profile || profile='default'
+    zstyle -T "$context" re-execute && reexecute=1 || reexecute=0
+
+    if (( ${+parameters[CHPWD_PROFILE]} == 0 )); then
+        typeset -g CHPWD_PROFILE
+        local CHPWD_PROFILES_INIT=1
+        (( ${+functions[chpwd_profiles_init]} )) && chpwd_profiles_init
+    elif [[ $profile != $CHPWD_PROFILE ]]; then
+        (( ${+functions[chpwd_leave_profile_$CHPWD_PROFILE]} )) \
+            && chpwd_leave_profile_${CHPWD_PROFILE}
+    fi
+    if (( reexecute )) || [[ $profile != $CHPWD_PROFILE ]]; then
+        (( ${+functions[chpwd_profile_$profile]} )) && chpwd_profile_${profile}
     fi
 
     CHPWD_PROFILE="${profile}"
     return 0
 }
+
 chpwd_functions=( ${chpwd_functions} chpwd_profiles )
 
 fi # is433
@@ -1250,7 +1224,7 @@ if zrcautoload colors && colors 2>/dev/null ; then
     MAGENTA="%{${fg[magenta]}%}"
     YELLOW="%{${fg[yellow]}%}"
     WHITE="%{${fg[white]}%}"
-    NO_COLOUR="%{${reset_color}%}"
+    NO_COLOR="%{${reset_color}%}"
 else
     BLUE=$'%{\e[1;34m%}'
     RED=$'%{\e[1;31m%}'
@@ -1259,7 +1233,7 @@ else
     WHITE=$'%{\e[1;37m%}'
     MAGENTA=$'%{\e[1;35m%}'
     YELLOW=$'%{\e[1;33m%}'
-    NO_COLOUR=$'%{\e[0m%}'
+    NO_COLOR=$'%{\e[0m%}'
 fi
 
 # gather version control information for inclusion in a prompt
@@ -1291,10 +1265,10 @@ if [[ "$TERM" == dumb ]] ; then
     zstyle ':vcs_info:*' actionformats "(%s%)-[%b|%a] " "zsh: %r"
     zstyle ':vcs_info:*' formats       "(%s%)-[%b] "    "zsh: %r"
 else
-    # these are the same, just with a lot of colours:
-    zstyle ':vcs_info:*' actionformats "${MAGENTA}(${NO_COLOUR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${YELLOW}|${RED}%a${MAGENTA}]${NO_COLOUR} " \
+    # 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_COLOUR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${MAGENTA}]${NO_COLOUR}%} " \
+    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"
 fi
@@ -1310,9 +1284,271 @@ function command_not_found_handler() {
     return 1
 }
 
+function prompt_grml_help () {
+    cat <<__EOF0__
+  prompt grml
+
+    This is the prompt as used by the grml-live system <http://grml.org>. It is
+    a rather simple one-line prompt, that by default looks something like this:
+
+        <user>@<host> <current-working-directory>[ <vcs_info-data>]%
+
+    The prompt itself integrates with zsh's prompt themes system (as you are
+    witnessing right now) and is configurable to a certain degree. In
+    particular, these aspects are customisable:
+
+        - The items used in the prompt (e.g. you can remove \`user' from
+          the list of activated items, which will cause the user name to
+          be omitted from the prompt string).
+
+        - The attributes used with the items are customisable via strings
+          used before and after the actual item.
+
+    The available items are: rc, rc-always, change-root, user, at, host, path,
+    vcs, percent, sad-smiley, battery.
+
+    The actual configuration is done via zsh's \`zstyle' mechanism. The
+    context, that is used while looking up styles is:
+
+        ':prompt:grml:<left-or-right>:<subcontext>'
+
+    Here <left-or-right> is either \`left' or \`right', signifying whether the
+    style should affect the left or the right prompt. <subcontext> is either
+    \`setup' or 'items:<item>', where \`<item>' is one of the available items.
+
+    The styles:
+
+        - use-rprompt (boolean): If \`true' (the default), print a sad smiley
+          in $RPROMPT if the last command a returned non-successful error code.
+          (This in only valid if <left-or-right> is "right"; ignored otherwise)
+
+        - items (list): The list of items used in the prompt. If \`vcs' is
+          present in the list, the theme's code invokes \`vcs_info'
+          accordingly. Default: rc user change-root at host path vcs precent
+
+    Available styles in 'items:<item>' are: pre, post. These are strings that
+    are inserted before (pre) and after (post) the item in question. Thus, the
+    following would cause the user name to be printed in red instead of the
+    default blue:
+
+        zstyle ':prompt:grml:*:items:user' pre '%F{red}'
+
+    Note, that the \`post' style may remain at its default value, because its
+    default value is '%f', which turns the foreground text attribute off (which
+    is exactly, what is still required with the new \`pre' value).
+__EOF0__
+}
+
+function grml_prompt_setup () {
+    emulate -L zsh
+    autoload -Uz vcs_info
+    autoload -Uz add-zsh-hook
+    add-zsh-hook precmd prompt_$1_precmd
+}
+
+function prompt_grml_setup () {
+    grml_prompt_setup grml
+}
+
+function prompt_grml-chroot_setup () {
+    grml_prompt_setup grml-chroot
+}
+
+function prompt_grml-large_setup () {
+    grml_prompt_setup grml-large
+}
+
+typeset -gA grml_prompt_pre_default \
+            grml_prompt_post_default \
+            grml_prompt_token_default
+
+grml_prompt_pre_default=(
+    rc                '%F{red}'
+    rc-always         ''
+    change-root       ''
+    user              '%B%F{blue}'
+    at                ''
+    host              ''
+    path              '%b'
+    vcs               ''
+    percent           ''
+    sad-smiley        ''
+    battery           ' '
+    newline           ''
+    jobs              '%F{cyan}'
+    history           '%F{green}'
+    date              '%F{blue}'
+    time              '%F{blue}'
+    shell-level       '%F{red}'
+    grml-chroot       '%F{red}'
+)
+
+grml_prompt_post_default=(
+    rc                '%f'
+    rc-always         ''
+    change-root       ''
+    user              '%f%b'
+    at                ''
+    host              ''
+    path              '%B'
+    vcs               ''
+    percent           ''
+    sad-smiley        ''
+    battery           ''
+    newline           ''
+    jobs              '%f'
+    history           '%f'
+    date              '%f'
+    time              '%f'
+    shell-level       '%f'
+    grml-chroot       '%f '
+)
+
+grml_prompt_token_default=(
+    rc                '%(?..%? )'
+    rc-always         '%?'
+    change-root       'debian_chroot'
+    user              '%n'
+    at                '@'
+    host              '%m '
+    path              '%40<..<%~%<< '
+    vcs               '0'
+    percent           '%% '
+    sad-smiley        '%(?..:()'
+    battery           'PERCENT'
+    newline           $'\n'
+    jobs              '[%j running job(s)] '
+    history           '{history#%!} '
+    date              '%D{%Y-%m-%d}'
+    time              '%D{%H:%M:%S} '
+    shell-level       '%(3L.+ .)'
+    grml-chroot       'GRML_CHROOT'
+)
+
+function grml_typeset_and_wrap () {
+    emulate -L zsh
+    local target="$1"
+    local new="$2"
+    local left="$3"
+    local right="$4"
+
+    if (( ${+parameters[$new]} )); then
+        typeset -g "${target}=${(P)target}${left}${(P)new}${right}"
+    fi
+}
+
+function grml_prompt_addto () {
+    emulate -L zsh
+    local target="$1"
+    local lr it apre apost new v
+    local -a items
+    shift
+
+    [[ $target == PS1 ]] && lr=left || lr=right
+    zstyle -a ":prompt:${grmltheme}:${lr}:setup" items items || items=( "$@" )
+    typeset -g "${target}="
+    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 \
+            || apost=${grml_prompt_post_default[$it]}
+        zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" token new \
+            || new=${grml_prompt_token_default[$it]}
+        typeset -g "${target}=${(P)target}${apre}"
+        case $it in
+            battery)
+                grml_typeset_and_wrap $target $new '' ''
+                ;;
+            change-root)
+                grml_typeset_and_wrap $target $new '(' ')'
+                ;;
+            grml-chroot)
+                if [[ -n ${(P)new} ]]; then
+                    typeset -g "${target}=${(P)target}(CHROOT)"
+                fi
+                ;;
+            vcs)
+                v="vcs_info_msg_${new}_"
+                if (( ! vcscalled )); then
+                    vcs_info
+                    vcscalled=1
+                fi
+                if (( ${+parameters[$v]} )) && [[ -n "${(P)v}" ]]; then
+                    typeset -g "${target}=${(P)target}${(P)v}"
+                fi
+                ;;
+            *) typeset -g "${target}=${(P)target}${new}" ;;
+        esac
+        typeset -g "${target}=${(P)target}${apost}"
+    done
+}
+
+function prompt_grml_precmd () {
+    emulate -L zsh
+    local grmltheme=grml
+    local -a left_items right_items
+    left_items=(rc change-root user at host path vcs percent)
+    right_items=(sad-smiley)
+
+    prompt_grml_precmd_worker
+}
+
+function prompt_grml-chroot_precmd () {
+    emulate -L zsh
+    local grmltheme=grml-chroot
+    local -a left_items right_items
+    left_items=(grml-chroot user at host path percent)
+    right_items=()
+
+    prompt_grml_precmd_worker
+}
+
+function prompt_grml-large_precmd () {
+    emulate -L zsh
+    local grmltheme=grml-large
+    local -a left_items right_items
+    left_items=(rc jobs history shell-level change-root time date newline
+                user at host path vcs percent)
+    right_items=(sad-smiley)
+
+    prompt_grml_precmd_worker
+}
+
+function prompt_grml_precmd_worker () {
+    emulate -L zsh
+    local -i vcscalled=0
+
+    grml_prompt_addto PS1 "${left_items[@]}"
+    if zstyle -T ":prompt:${grmltheme}:right:setup" use-rprompt; then
+        grml_prompt_addto RPS1 "${right_items[@]}"
+    fi
+}
+
 # set prompt
 if zrcautoload promptinit && promptinit 2>/dev/null ; then
     promptinit # people should be able to use their favourite prompt
+
+    # Since we define the required functions in here and not in files in
+    # $fpath, we need to stick the theme's name into `$prompt_themes'
+    # ourselves, since promptinit does not pick them up otherwise.
+    prompt_themes+=( grml grml-chroot grml-large )
+    # Also, keep the array sorted...
+    prompt_themes=( "${(@on)prompt_themes}" )
+
+    if [[ $BATTERY -gt 0 ]]; then
+        zstyle ':prompt:grml:right:setup' items sad-smiley battery
+        add-zsh-hook precmd battery
+    fi
+    if [[ "$TERM" == dumb ]] ; then
+        for i in rc user path jobs history date time shell-level; do
+            zstyle ":prompt:grml(|-large|-chroot):*:items:$i" pre ''
+            zstyle ':prompt:grml(|-large|-chroot):*:items:$i' post ''
+        done
+        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}'
+    fi
 else
     print 'Notice: no promptinit available :('
 fi
@@ -1340,30 +1576,7 @@ function info_print () {
     printf '%s' "${esc_end}"
 }
 
-# TODO: revise all these NO* variables and especially their documentation
-#       in zsh-help() below.
-is4 && [[ $NOPRECMD -eq 0 ]] && precmd () {
-    [[ $NOPRECMD -gt 0 ]] && return 0
-    # update VCS information
-    (( ${+functions[vcs_info]} )) && vcs_info
-
-    if [[ $TERM == screen* ]] ; then
-        if [[ -n ${vcs_info_msg_1_} ]] ; then
-            ESC_print ${vcs_info_msg_1_}
-        else
-            ESC_print "zsh"
-        fi
-    fi
-    # just use DONTSETRPROMPT=1 to be able to overwrite RPROMPT
-    if [[ ${DONTSETRPROMPT:-} -eq 0 ]] ; then
-        if [[ $BATTERY -gt 0 ]] ; then
-            # update battery (dropped into $PERCENT) information
-            battery
-            RPROMPT="%(?..:() ${PERCENT}"
-        else
-            RPROMPT="%(?..:() "
-        fi
-    fi
+function grml_reset_screen_title () {
     # adjust title of xterm
     # see http://www.faqs.org/docs/Linux-mini/Xterm-Title.html
     [[ ${NOTITLE:-} -gt 0 ]] && return 0
@@ -1374,6 +1587,22 @@ is4 && [[ $NOPRECMD -eq 0 ]] && precmd () {
     esac
 }
 
+function grml_vcs_to_screen_title () {
+    if [[ $TERM == screen* ]] ; then
+        if [[ -n ${vcs_info_msg_1_} ]] ; then
+            ESC_print ${vcs_info_msg_1_}
+        else
+            ESC_print "zsh"
+        fi
+    fi
+}
+
+zrcautoload add-zsh-hook || add-zsh-hook () { :; }
+if [[ $NOPRECMD -gt 0 ]]; then
+    add-zsh-hook precmd grml_reset_screen_title
+    add-zsh-hook precmd grml_vcs_to_screen_title
+fi
+
 # preexec() => a function running before every command
 is4 && [[ $NOPRECMD -eq 0 ]] && \
 preexec () {
@@ -1421,13 +1650,13 @@ else
     # prompt below)
     if [[ $GRMLPROMPT -gt 0 ]] ; then
         PROMPT="${RED}${EXITCODE}${CYAN}[%j running job(s)] ${GREEN}{history#%!} ${RED}%(3L.+.) ${BLUE}%* %D
-${BLUE}%n${NO_COLOUR}@%m %40<...<%B%~%b%<< "
+${BLUE}%n${NO_COLOR}@%m %40<...<%B%~%b%<< "
     else
         # This assembles the primary prompt string
         if (( EUID != 0 )); then
-            PROMPT="${RED}${EXITCODE}${WHITE}${debian_chroot:+($debian_chroot)}${BLUE}%n${NO_COLOUR}@%m %40<...<%B%~%b%<< "
+            PROMPT="${RED}${EXITCODE}${WHITE}${debian_chroot:+($debian_chroot)}${BLUE}%n${NO_COLOR}@%m %40<...<%B%~%b%<< "
         else
-            PROMPT="${BLUE}${EXITCODE}${WHITE}${debian_chroot:+($debian_chroot)}${RED}%n${NO_COLOUR}@%m %40<...<%B%~%b%<< "
+            PROMPT="${BLUE}${EXITCODE}${WHITE}${debian_chroot:+($debian_chroot)}${RED}%n${NO_COLOR}@%m %40<...<%B%~%b%<< "
         fi
     fi
 fi
@@ -1474,15 +1703,15 @@ fi
 # do we have GNU ls with color-support?
 if [[ "$TERM" != dumb ]]; then
     #a1# execute \kbd{@a@}:\quad ls with colors
-    alias ls='ls -b -CF '${ls_options:+"${ls_options[*]} "}
+    alias ls='ls -b -CF '${ls_options:+"${ls_options[*]}"}
     #a1# execute \kbd{@a@}:\quad list all files, with colors
-    alias la='ls -la '${ls_options:+"${ls_options[*]} "}
+    alias la='ls -la '${ls_options:+"${ls_options[*]}"}
     #a1# long colored list, without dotfiles (@a@)
-    alias ll='ls -l '${ls_options:+"${ls_options[*]} "}
+    alias ll='ls -l '${ls_options:+"${ls_options[*]}"}
     #a1# long colored list, human readable sizes (@a@)
-    alias lh='ls -hAl '${ls_options:+"${ls_options[*]} "}
+    alias lh='ls -hAl '${ls_options:+"${ls_options[*]}"}
     #a1# List files, append qualifier to filenames \\&\quad(\kbd{/} for directories, \kbd{@} for symlinks ...)
-    alias l='ls -lF '${ls_options:+"${ls_options[*]} "}
+    alias l='ls -lF '${ls_options:+"${ls_options[*]}"}
 else
     alias ls='ls -b -CF'
     alias la='ls -la'
@@ -1751,6 +1980,15 @@ grmlcomp() {
     zstyle ':completion:*:manuals.*'  insert-sections   true
     zstyle ':completion:*:man:*'      menu yes select
 
+    # Search path for sudo completion
+    zstyle ':completion:*:sudo:*' command-path /usr/local/sbin \
+                                               /usr/local/bin  \
+                                               /usr/sbin       \
+                                               /usr/bin        \
+                                               /sbin           \
+                                               /bin            \
+                                               /usr/X11R6/bin
+
     # provide .. as a completion
     zstyle ':completion:*' special-dirs ..
 
@@ -1800,7 +2038,6 @@ grmlcomp() {
         $(hostname)
         "$_ssh_hosts[@]"
         "$_etc_hosts[@]"
-        grml.org
         localhost
     )
     zstyle ':completion:*:hosts' hosts $hosts
@@ -1929,6 +2166,19 @@ if check_com -c $PAGER ; then
     _dnews() { _files -W /usr/share/doc -/ }
     compdef _dnews dnews
 
+    #f1# View Debian's copyright of a given package
+    dcopyright() {
+        emulate -L zsh
+        if [[ -r /usr/share/doc/$1/copyright ]] ; then
+            $PAGER /usr/share/doc/$1/copyright
+        else
+            echo "No copyright file for package $1 found, sorry."
+            return 1
+        fi
+    }
+    _dcopyright() { _files -W /usr/share/doc -/ }
+    compdef _dcopyright dcopyright
+
     #f1# View upstream's changelog of a given package
     uchange() {
         emulate -L zsh
@@ -1966,6 +2216,7 @@ compdef _functions edfunc
 #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}
 if [[ -d /etc/init.d || -d /etc/service ]] ; then
     __start_stop() {
         local action_="${1:l}"  # e.g Start/Stop/Restart
@@ -1997,7 +2248,7 @@ if [[ -d /etc/init.d || -d /etc/service ]] ; then
         _describe "service startup script" scripts
     }
 
-    for i in Start Restart Stop Force-Reload Reload ; do
+    for i in Start Restart Stop Force-Reload Reload Status ; do
         eval "$i() { __start_stop $i \"\$1\" \"\$2\" ; }"
         compdef _grmlinitd $i
     done
@@ -2195,9 +2446,11 @@ deswap() {
 #           VIM_OPTIONS=( -p )
 #       This will cause vim to send every file given on the
 #       commandline to be send to it's own tab (needs vim7).
-vim() {
-    VIM_PLEASE_SET_TITLE='yes' command vim ${VIM_OPTIONS} "$@"
-}
+if check_com vim; then
+    vim() {
+        VIM_PLEASE_SET_TITLE='yes' command vim ${VIM_OPTIONS} "$@"
+    }
+fi
 
 # make a backup of a file
 bk() {
@@ -2393,6 +2646,7 @@ inplaceMkDirs() {
         PATHTOMKDIR="${(Q)bufwords[iword]}"
     fi
     [[ -z "${PATHTOMKDIR}" ]] && return 1
+    PATHTOMKDIR=${~PATHTOMKDIR}
     if [[ -e "${PATHTOMKDIR}" ]]; then
         zle -M " path already exists, doing nothing"
     else
@@ -2425,7 +2679,7 @@ 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[*]} "}
+(( $#grep_options > 0 )) && alias grep='grep '${grep_options:+"${grep_options[*]}"}
 
 # Translate DE<=>EN
 # 'translate' looks up fot a word in a file with language-to-language
@@ -2464,62 +2718,62 @@ simple-extract() {
     zparseopts -D -E "d=DELETE_ORIGINAL"
     for ARCHIVE in "${@}"; do
         case $ARCHIVE in
-            *.(tar.bz2|tbz2|tbz))
+            *(tar.bz2|tbz2|tbz))
                 DECOMP_CMD="tar -xvjf -"
                 USES_STDIN=true
                 USES_STDOUT=false
                 ;;
-            *.(tar.gz|tgz))
+            *(tar.gz|tgz))
                 DECOMP_CMD="tar -xvzf -"
                 USES_STDIN=true
                 USES_STDOUT=false
                 ;;
-            *.(tar.xz|txz|tar.lzma))
+            *(tar.xz|txz|tar.lzma))
                 DECOMP_CMD="tar -xvJf -"
                 USES_STDIN=true
                 USES_STDOUT=false
                 ;;
-            *.tar)
+            *tar)
                 DECOMP_CMD="tar -xvf -"
                 USES_STDIN=true
                 USES_STDOUT=false
                 ;;
-            *.rar)
+            *rar)
                 DECOMP_CMD="unrar x"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.lzh)
+            *lzh)
                 DECOMP_CMD="lha x"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.7z)
+            *7z)
                 DECOMP_CMD="7z x"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.(zip|jar))
+            *(zip|jar))
                 DECOMP_CMD="unzip"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.deb)
+            *deb)
                 DECOMP_CMD="ar -x"
                 USES_STDIN=false
                 USES_STDOUT=false
                 ;;
-            *.bz2)
+            *bz2)
                 DECOMP_CMD="bzip2 -d -c -"
                 USES_STDIN=true
                 USES_STDOUT=true
                 ;;
-            *.(gz|Z))
+            *(gz|Z))
                 DECOMP_CMD="gzip -d -c -"
                 USES_STDIN=true
                 USES_STDOUT=true
                 ;;
-            *.(xz|lzma))
+            *(xz|lzma))
                 DECOMP_CMD="xz -d -c -"
                 USES_STDIN=true
                 USES_STDOUT=true
@@ -2640,46 +2894,50 @@ xtrename() {
     return 0
 }
 
-# TODO:
-# Rewrite this by either using tinyurl.com's API
-# or using another shortening service to comply with
-# tinyurl.com's policy.
-#
-# Create small urls via http://tinyurl.com using wget(1).
-#function zurl() {
-#    emulate -L zsh
-#    [[ -z $1 ]] && { print "USAGE: zurl <URL>" ; return 1 }
-#
-#    local PN url tiny grabber search result preview
-#    PN=$0
-#    url=$1
-##   Check existence of given URL with the help of ping(1).
-##   N.B. ping(1) only works without an eventual given protocol.
-#    ping -c 1 ${${url#(ftp|http)://}%%/*} >& /dev/null || \
-#        read -q "?Given host ${${url#http://*/}%/*} is not reachable by pinging. Proceed anyway? [y|n] "
-#
-#    if (( $? == 0 )) ; then
-##           Prepend 'http://' to given URL where necessary for later output.
-#            [[ ${url} != http(s|)://* ]] && url='http://'${url}
-#            tiny='http://tinyurl.com/create.php?url='
-#            if check_com -c wget ; then
-#                grabber='wget -O- -o/dev/null'
-#            else
-#                print "wget is not available, but mandatory for ${PN}. Aborting."
-#            fi
-##           Looking for i.e.`copy('http://tinyurl.com/7efkze')' in TinyURL's HTML code.
-#            search='copy\(?http://tinyurl.com/[[:alnum:]]##*'
-#            result=${(M)${${${(f)"$(${=grabber} ${tiny}${url})"}[(fr)${search}*]}//[()\';]/}%%http:*}
-##           TinyURL provides the rather new feature preview for more confidence. <http://tinyurl.com/preview.php>
-#            preview='http://preview.'${result#http://}
-#
-#            printf '%s\n\n' "${PN} - Shrinking long URLs via webservice TinyURL <http://tinyurl.com>."
-#            printf '%s\t%s\n\n' 'Given URL:' ${url}
-#            printf '%s\t%s\n\t\t%s\n' 'TinyURL:' ${result} ${preview}
-#    else
-#        return 1
-#    fi
-#}
+# Create small urls via http://goo.gl using curl(1).
+# API reference: https://code.google.com/apis/urlshortener/
+function zurl() {
+    emulate -L zsh
+    setopt extended_glob
+
+    if [[ -z $1 ]]; then
+        print "USAGE: zurl <URL>"
+        return 1
+    fi
+
+    local PN url prog api json contenttype item
+    local -a data
+    PN=$0
+    url=$1
+
+    # Prepend 'http://' to given URL where necessary for later output.
+    if [[ ${url} != http(s|)://* ]]; then
+        url='http://'${url}
+    fi
+
+    if check_com -c curl; then
+        prog=curl
+    else
+        print "curl is not available, but mandatory for ${PN}. Aborting."
+        return 1
+    fi
+    api='https://www.googleapis.com/urlshortener/v1/url'
+    contenttype="Content-Type: application/json"
+    json="{\"longUrl\": \"${url}\"}"
+    data=(${(f)"$($prog --silent -H ${contenttype} -d ${json} $api)"})
+    # Parse the response
+    for item in "${data[@]}"; do
+        case "$item" in
+            ' '#'"id":'*)
+                item=${item#*: \"}
+                item=${item%\",*}
+                printf '%s\n' "$item"
+                return 0
+                ;;
+        esac
+    done
+    return 1
+}
 
 #f2# Find history events by search pattern and list them by date.
 whatwhen()  {