22ed16228d1e26b47322bd34b041f544aa5d458d
[grml-etc-core.git] / etc / zsh / zshrc
1 # Filename:      /etc/zsh/zshrc
2 # Purpose:       config file for zsh (z shell)
3 # Authors:       grml-team (grml.org), (c) Michael Prokop <mika@grml.org>
4 # Bug-Reports:   see http://grml.org/bugs/
5 # License:       This file is licensed under the GPL v2.
6 ################################################################################
7 # This file is sourced only for interactive shells. It
8 # should contain commands to set up aliases, functions,
9 # options, key bindings, etc.
10 #
11 # Global Order: zshenv, zprofile, zshrc, zlogin
12 ################################################################################
13
14 # USAGE
15 # If you are using this file as your ~/.zshrc file, please use ~/.zshrc.pre
16 # and ~/.zshrc.local for your own customisations. The former file is read
17 # before ~/.zshrc, the latter is read after it. Also, consider reading the
18 # refcard and the reference manual for this setup, both available from:
19 #     <http://grml.org/zsh/>
20
21 # Contributing:
22 # If you want to help to improve grml's zsh setup, clone the grml-etc-core
23 # repository from git.grml.org:
24 #   git clone git://git.grml.org/grml-etc-core.git
25 #
26 # Make your changes, commit them; use 'git format-patch' to create a series
27 # of patches and send those to the following address via 'git send-email':
28 #   grml-etc-core@grml.org
29 #
30 # Doing so makes sure the right people get your patches for review and
31 # possibly inclusion.
32
33 # zsh-refcard-tag documentation:
34 #   You may notice strange looking comments in this file.
35 #   These are there for a purpose. grml's zsh-refcard can now be
36 #   automatically generated from the contents of the actual configuration
37 #   file. However, we need a little extra information on which comments
38 #   and what lines of code to take into account (and for what purpose).
39 #
40 # Here is what they mean:
41 #
42 # List of tags (comment types) used:
43 #   #a#     Next line contains an important alias, that should
44 #           be included in the grml-zsh-refcard.
45 #           (placement tag: @@INSERT-aliases@@)
46 #   #f#     Next line contains the beginning of an important function.
47 #           (placement tag: @@INSERT-functions@@)
48 #   #v#     Next line contains an important variable.
49 #           (placement tag: @@INSERT-variables@@)
50 #   #k#     Next line contains an important keybinding.
51 #           (placement tag: @@INSERT-keybindings@@)
52 #   #d#     Hashed directories list generation:
53 #               start   denotes the start of a list of 'hash -d'
54 #                       definitions.
55 #               end     denotes its end.
56 #           (placement tag: @@INSERT-hasheddirs@@)
57 #   #A#     Abbreviation expansion list generation:
58 #               start   denotes the beginning of abbreviations.
59 #               end     denotes their end.
60 #           Lines within this section that end in '#d .*' provide
61 #           extra documentation to be included in the refcard.
62 #           (placement tag: @@INSERT-abbrev@@)
63 #   #m#     This tag allows you to manually generate refcard entries
64 #           for code lines that are hard/impossible to parse.
65 #               Example:
66 #                   #m# k ESC-h Call the run-help function
67 #               That would add a refcard entry in the keybindings table
68 #               for 'ESC-h' with the given comment.
69 #           So the syntax is: #m# <section> <argument> <comment>
70 #   #o#     This tag lets you insert entries to the 'other' hash.
71 #           Generally, this should not be used. It is there for
72 #           things that cannot be done easily in another way.
73 #           (placement tag: @@INSERT-other-foobar@@)
74 #
75 #   All of these tags (except for m and o) take two arguments, the first
76 #   within the tag, the other after the tag:
77 #
78 #   #<tag><section># <comment>
79 #
80 #   Where <section> is really just a number, which are defined by the
81 #   @secmap array on top of 'genrefcard.pl'. The reason for numbers
82 #   instead of names is, that for the reader, the tag should not differ
83 #   much from a regular comment. For zsh, it is a regular comment indeed.
84 #   The numbers have got the following meanings:
85 #         0 -> "default"
86 #         1 -> "system"
87 #         2 -> "user"
88 #         3 -> "debian"
89 #         4 -> "search"
90 #         5 -> "shortcuts"
91 #         6 -> "services"
92 #
93 #   So, the following will add an entry to the 'functions' table in the
94 #   'system' section, with a (hopefully) descriptive comment:
95 #       #f1# Edit an alias via zle
96 #       edalias() {
97 #
98 #   It will then show up in the @@INSERT-aliases-system@@ replacement tag
99 #   that can be found in 'grml-zsh-refcard.tex.in'.
100 #   If the section number is omitted, the 'default' section is assumed.
101 #   Furthermore, in 'grml-zsh-refcard.tex.in' @@INSERT-aliases@@ is
102 #   exactly the same as @@INSERT-aliases-default@@. If you want a list of
103 #   *all* aliases, for example, use @@INSERT-aliases-all@@.
104
105 # zsh profiling
106 # just execute 'ZSH_PROFILE_RC=1 zsh' and run 'zprof' to get the details
107 if [[ $ZSH_PROFILE_RC -gt 0 ]] ; then
108     zmodload zsh/zprof
109 fi
110
111 typeset -A GRML_STATUS_FEATURES
112
113 function grml_status_feature () {
114     emulate -L zsh
115     local f=$1
116     local -i success=$2
117     if (( success == 0 )); then
118         GRML_STATUS_FEATURES[$f]=success
119     else
120         GRML_STATUS_FEATURES[$f]=failure
121     fi
122     return 0
123 }
124
125 function grml_status_features () {
126     emulate -L zsh
127     local mode=${1:-+-}
128     local this
129     if [[ $mode == -h ]] || [[ $mode == --help ]]; then
130         cat <<EOF
131 grml_status_features [-h|--help|-|+|+-|FEATURE]
132
133 Prints a summary of features the grml setup is trying to load. The
134 result of loading a feature is recorded. This function lets you query
135 the result.
136
137 The function takes one argument: "-h" or "--help" to display this help
138 text, "+" to display a list of all successfully loaded features, "-" for
139 a list of all features that failed to load. "+-" to show a list of all
140 features with their statuses.
141
142 Any other word is considered to by a feature and prints its status.
143
144 The default mode is "+-".
145 EOF
146         return 0
147     fi
148     if [[ $mode != - ]] && [[ $mode != + ]] && [[ $mode != +- ]]; then
149         this="${GRML_STATUS_FEATURES[$mode]}"
150         if [[ -z $this ]]; then
151             printf 'unknown\n'
152             return 1
153         else
154             printf '%s\n' $this
155         fi
156         return 0
157     fi
158     for key in ${(ok)GRML_STATUS_FEATURES}; do
159         this="${GRML_STATUS_FEATURES[$key]}"
160         if [[ $this == success ]] && [[ $mode == *+* ]]; then
161             printf '%-16s %s\n' $key $this
162         fi
163         if [[ $this == failure ]] && [[ $mode == *-* ]]; then
164             printf '%-16s %s\n' $key $this
165         fi
166     done
167     return 0
168 }
169
170 # load .zshrc.pre to give the user the chance to overwrite the defaults
171 [[ -r ${ZDOTDIR:-${HOME}}/.zshrc.pre ]] && source ${ZDOTDIR:-${HOME}}/.zshrc.pre
172
173 # check for version/system
174 # check for versions (compatibility reasons)
175 function is51 () {
176     [[ $ZSH_VERSION == 5.<1->* ]] && return 0
177     return 1
178 }
179
180 function is4 () {
181     [[ $ZSH_VERSION == <4->* ]] && return 0
182     return 1
183 }
184
185 function is41 () {
186     [[ $ZSH_VERSION == 4.<1->* || $ZSH_VERSION == <5->* ]] && return 0
187     return 1
188 }
189
190 function is42 () {
191     [[ $ZSH_VERSION == 4.<2->* || $ZSH_VERSION == <5->* ]] && return 0
192     return 1
193 }
194
195 function is425 () {
196     [[ $ZSH_VERSION == 4.2.<5->* || $ZSH_VERSION == 4.<3->* || $ZSH_VERSION == <5->* ]] && return 0
197     return 1
198 }
199
200 function is43 () {
201     [[ $ZSH_VERSION == 4.<3->* || $ZSH_VERSION == <5->* ]] && return 0
202     return 1
203 }
204
205 function is433 () {
206     [[ $ZSH_VERSION == 4.3.<3->* || $ZSH_VERSION == 4.<4->* \
207                                  || $ZSH_VERSION == <5->* ]] && return 0
208     return 1
209 }
210
211 function is437 () {
212     [[ $ZSH_VERSION == 4.3.<7->* || $ZSH_VERSION == 4.<4->* \
213                                  || $ZSH_VERSION == <5->* ]] && return 0
214     return 1
215 }
216
217 function is439 () {
218     [[ $ZSH_VERSION == 4.3.<9->* || $ZSH_VERSION == 4.<4->* \
219                                  || $ZSH_VERSION == <5->* ]] && return 0
220     return 1
221 }
222
223 #f1# Checks whether or not you're running grml
224 function isgrml () {
225     [[ -f /etc/grml_version ]] && return 0
226     return 1
227 }
228
229 #f1# Checks whether or not you're running a grml cd
230 function isgrmlcd () {
231     [[ -f /etc/grml_cd ]] && return 0
232     return 1
233 }
234
235 if isgrml ; then
236 #f1# Checks whether or not you're running grml-small
237     function isgrmlsmall () {
238         if [[ ${${${(f)"$(</etc/grml_version)"}%% *}##*-} == 'small' ]]; then
239             return 0
240         fi
241         return 1
242     }
243 else
244     function isgrmlsmall () { return 1 }
245 fi
246
247 GRML_OSTYPE=$(uname -s)
248
249 function islinux () {
250     [[ $GRML_OSTYPE == "Linux" ]]
251 }
252
253 function isdarwin () {
254     [[ $GRML_OSTYPE == "Darwin" ]]
255 }
256
257 function isfreebsd () {
258     [[ $GRML_OSTYPE == "FreeBSD" ]]
259 }
260
261 function isopenbsd () {
262     [[ $GRML_OSTYPE == "OpenBSD" ]]
263 }
264
265 function issolaris () {
266     [[ $GRML_OSTYPE == "SunOS" ]]
267 }
268
269 #f1# are we running within an utf environment?
270 function isutfenv () {
271     case "$LANG $CHARSET $LANGUAGE" in
272         *utf*) return 0 ;;
273         *UTF*) return 0 ;;
274         *)     return 1 ;;
275     esac
276 }
277
278 # check for user, if not running as root set $SUDO to sudo
279 (( EUID != 0 )) && SUDO='sudo' || SUDO=''
280
281 # change directory to home on first invocation of zsh
282 # important for rungetty -> autologin
283 # Thanks go to Bart Schaefer!
284 isgrml && function checkhome () {
285     if [[ -z "$ALREADY_DID_CD_HOME" ]] ; then
286         export ALREADY_DID_CD_HOME=$HOME
287         cd
288     fi
289 }
290
291 # check for zsh v3.1.7+
292
293 if ! [[ ${ZSH_VERSION} == 3.1.<7->*      \
294      || ${ZSH_VERSION} == 3.<2->.<->*    \
295      || ${ZSH_VERSION} == <4->.<->*   ]] ; then
296
297     printf '-!-\n'
298     printf '-!- In this configuration we try to make use of features, that only\n'
299     printf '-!- require version 3.1.7 of the shell; That way this setup can be\n'
300     printf '-!- used with a wide range of zsh versions, while using fairly\n'
301     printf '-!- advanced features in all supported versions.\n'
302     printf '-!-\n'
303     printf '-!- However, you are running zsh version %s.\n' "$ZSH_VERSION"
304     printf '-!-\n'
305     printf '-!- While this *may* work, it might as well fail.\n'
306     printf '-!- Please consider updating to at least version 3.1.7 of zsh.\n'
307     printf '-!-\n'
308     printf '-!- DO NOT EXPECT THIS TO WORK FLAWLESSLY!\n'
309     printf '-!- If it does today, you'\''ve been lucky.\n'
310     printf '-!-\n'
311     printf '-!- Ye been warned!\n'
312     printf '-!-\n'
313
314     function zstyle () { : }
315 fi
316
317 # autoload wrapper - use this one instead of autoload directly
318 # We need to define this function as early as this, because autoloading
319 # 'is-at-least()' needs it.
320 function zrcautoload () {
321     emulate -L zsh
322     setopt extended_glob
323     local fdir ffile
324     local -i ffound
325
326     ffile=$1
327     (( ffound = 0 ))
328     for fdir in ${fpath} ; do
329         [[ -e ${fdir}/${ffile} ]] && (( ffound = 1 ))
330     done
331
332     (( ffound == 0 )) && return 1
333     if [[ $ZSH_VERSION == 3.1.<6-> || $ZSH_VERSION == <4->* ]] ; then
334         autoload -U ${ffile} || return 1
335     else
336         autoload ${ffile} || return 1
337     fi
338     return 0
339 }
340
341 # The following is the â€˜add-zsh-hook’ function from zsh upstream. It is
342 # included here to make the setup work with older versions of zsh (prior to
343 # 4.3.7) in which this function had a bug that triggers annoying errors during
344 # shell startup. This is exactly upstreams code from f0068edb4888a4d8fe94def,
345 # with just a few adjustments in coding style to make the function look more
346 # compact. This definition can be removed as soon as we raise the minimum
347 # version requirement to 4.3.7 or newer.
348 function add-zsh-hook () {
349     # Add to HOOK the given FUNCTION.
350     # HOOK is one of chpwd, precmd, preexec, periodic, zshaddhistory,
351     # zshexit, zsh_directory_name (the _functions subscript is not required).
352     #
353     # With -d, remove the function from the hook instead; delete the hook
354     # variable if it is empty.
355     #
356     # -D behaves like -d, but pattern characters are active in the function
357     # name, so any matching function will be deleted from the hook.
358     #
359     # Without -d, the FUNCTION is marked for autoload; -U is passed down to
360     # autoload if that is given, as are -z and -k. (This is harmless if the
361     # function is actually defined inline.)
362     emulate -L zsh
363     local -a hooktypes
364     hooktypes=(
365         chpwd precmd preexec periodic zshaddhistory zshexit
366         zsh_directory_name
367     )
368     local usage="Usage: $0 hook function\nValid hooks are:\n  $hooktypes"
369     local opt
370     local -a autoopts
371     integer del list help
372     while getopts "dDhLUzk" opt; do
373         case $opt in
374         (d) del=1 ;;
375         (D) del=2 ;;
376         (h) help=1 ;;
377         (L) list=1 ;;
378         ([Uzk]) autoopts+=(-$opt) ;;
379         (*) return 1 ;;
380         esac
381     done
382     shift $(( OPTIND - 1 ))
383     if (( list )); then
384         typeset -mp "(${1:-${(@j:|:)hooktypes}})_functions"
385         return $?
386     elif (( help || $# != 2 || ${hooktypes[(I)$1]} == 0 )); then
387         print -u$(( 2 - help )) $usage
388         return $(( 1 - help ))
389     fi
390     local hook="${1}_functions"
391     local fn="$2"
392     if (( del )); then
393         # delete, if hook is set
394         if (( ${(P)+hook} )); then
395             if (( del == 2 )); then
396                 set -A $hook ${(P)hook:#${~fn}}
397             else
398                 set -A $hook ${(P)hook:#$fn}
399             fi
400             # unset if no remaining entries --- this can give better
401             # performance in some cases
402             if (( ! ${(P)#hook} )); then
403                 unset $hook
404             fi
405         fi
406     else
407         if (( ${(P)+hook} )); then
408             if (( ${${(P)hook}[(I)$fn]} == 0 )); then
409                 set -A $hook ${(P)hook} $fn
410             fi
411         else
412             set -A $hook $fn
413         fi
414         autoload $autoopts -- $fn
415     fi
416 }
417
418 # Load is-at-least() for more precise version checks Note that this test will
419 # *always* fail, if the is-at-least function could not be marked for
420 # autoloading.
421 zrcautoload is-at-least || function is-at-least () { return 1 }
422
423 # set some important options (as early as possible)
424
425 # append history list to the history file; this is the default but we make sure
426 # because it's required for share_history.
427 setopt append_history
428
429 # import new commands from the history file also in other zsh-session
430 is4 && setopt share_history
431
432 # save each command's beginning timestamp and the duration to the history file
433 setopt extended_history
434
435 # remove command lines from the history list when the first character on the
436 # line is a space
437 setopt histignorespace
438
439 # if a command is issued that can't be executed as a normal command, and the
440 # command is the name of a directory, perform the cd command to that directory.
441 setopt auto_cd
442
443 # in order to use #, ~ and ^ for filename generation grep word
444 # *~(*.gz|*.bz|*.bz2|*.zip|*.Z) -> searches for word not in compressed files
445 # don't forget to quote '^', '~' and '#'!
446 setopt extended_glob
447
448 # display PID when suspending processes as well
449 setopt longlistjobs
450
451 # report the status of backgrounds jobs immediately
452 setopt notify
453
454 # whenever a command completion is attempted, make sure the entire command path
455 # is hashed first.
456 setopt hash_list_all
457
458 # not just at the end
459 setopt completeinword
460
461 # Don't send SIGHUP to background processes when the shell exits.
462 setopt nohup
463
464 # make cd push the old directory onto the directory stack.
465 setopt auto_pushd
466
467 # avoid "beep"ing
468 setopt nobeep
469
470 # don't push the same dir twice.
471 setopt pushd_ignore_dups
472
473 # * shouldn't match dotfiles. ever.
474 setopt noglobdots
475
476 # use zsh style word splitting
477 setopt noshwordsplit
478
479 # don't error out when unset parameters are used
480 setopt unset
481
482 # setting some default values
483 NOCOR=${NOCOR:-0}
484 NOETCHOSTS=${NOETCHOSTS:-0}
485 NOMENU=${NOMENU:-0}
486 NOPRECMD=${NOPRECMD:-0}
487 COMMAND_NOT_FOUND=${COMMAND_NOT_FOUND:-0}
488 GRML_ZSH_CNF_HANDLER=${GRML_ZSH_CNF_HANDLER:-/usr/share/command-not-found/command-not-found}
489 GRML_DISPLAY_BATTERY=${GRML_DISPLAY_BATTERY:-${BATTERY:-0}}
490 GRMLSMALL_SPECIFIC=${GRMLSMALL_SPECIFIC:-1}
491 ZSH_NO_DEFAULT_LOCALE=${ZSH_NO_DEFAULT_LOCALE:-0}
492
493 typeset -ga ls_options
494 typeset -ga grep_options
495
496 # Colors on GNU ls(1)
497 if ls --color=auto / >/dev/null 2>&1; then
498     ls_options+=( --color=auto )
499 # Colors on FreeBSD and OSX ls(1)
500 elif ls -G / >/dev/null 2>&1; then
501     ls_options+=( -G )
502 fi
503
504 # Natural sorting order on GNU ls(1)
505 # OSX and IllumOS have a -v option that is not natural sorting
506 if ls --version |& grep -q 'GNU' >/dev/null 2>&1 && ls -v / >/dev/null 2>&1; then
507     ls_options+=( -v )
508 fi
509
510 # Color on GNU and FreeBSD grep(1)
511 if grep --color=auto -q "a" <<< "a" >/dev/null 2>&1; then
512     grep_options+=( --color=auto )
513 fi
514
515 # utility functions
516 # this function checks if a command exists and returns either true
517 # or false. This avoids using 'which' and 'whence', which will
518 # avoid problems with aliases for which on certain weird systems. :-)
519 # Usage: check_com [-c|-g] word
520 #   -c  only checks for external commands
521 #   -g  does the usual tests and also checks for global aliases
522 function check_com () {
523     emulate -L zsh
524     local -i comonly gatoo
525     comonly=0
526     gatoo=0
527
528     if [[ $1 == '-c' ]] ; then
529         comonly=1
530         shift 1
531     elif [[ $1 == '-g' ]] ; then
532         gatoo=1
533         shift 1
534     fi
535
536     if (( ${#argv} != 1 )) ; then
537         printf 'usage: check_com [-c|-g] <command>\n' >&2
538         return 1
539     fi
540
541     if (( comonly > 0 )) ; then
542         (( ${+commands[$1]}  )) && return 0
543         return 1
544     fi
545
546     if     (( ${+commands[$1]}    )) \
547         || (( ${+functions[$1]}   )) \
548         || (( ${+aliases[$1]}     )) \
549         || (( ${+reswords[(r)$1]} )) ; then
550         return 0
551     fi
552
553     if (( gatoo > 0 )) && (( ${+galiases[$1]} )) ; then
554         return 0
555     fi
556
557     return 1
558 }
559
560 # creates an alias and precedes the command with
561 # sudo if $EUID is not zero.
562 function salias () {
563     emulate -L zsh
564     local only=0 ; local multi=0
565     local key val
566     while getopts ":hao" opt; do
567         case $opt in
568             o) only=1 ;;
569             a) multi=1 ;;
570             h)
571                 printf 'usage: salias [-hoa] <alias-expression>\n'
572                 printf '  -h      shows this help text.\n'
573                 printf '  -a      replace '\'' ; '\'' sequences with '\'' ; sudo '\''.\n'
574                 printf '          be careful using this option.\n'
575                 printf '  -o      only sets an alias if a preceding sudo would be needed.\n'
576                 return 0
577                 ;;
578             *) salias -h >&2; return 1 ;;
579         esac
580     done
581     shift "$((OPTIND-1))"
582
583     if (( ${#argv} > 1 )) ; then
584         printf 'Too many arguments %s\n' "${#argv}"
585         return 1
586     fi
587
588     key="${1%%\=*}" ;  val="${1#*\=}"
589     if (( EUID == 0 )) && (( only == 0 )); then
590         alias -- "${key}=${val}"
591     elif (( EUID > 0 )) ; then
592         (( multi > 0 )) && val="${val// ; / ; sudo }"
593         alias -- "${key}=sudo ${val}"
594     fi
595
596     return 0
597 }
598
599 # Check if we can read given files and source those we can.
600 function xsource () {
601     if (( ${#argv} < 1 )) ; then
602         printf 'usage: xsource FILE(s)...\n' >&2
603         return 1
604     fi
605
606     while (( ${#argv} > 0 )) ; do
607         [[ -r "$1" ]] && source "$1"
608         shift
609     done
610     return 0
611 }
612
613 # Check if we can read a given file and 'cat(1)' it.
614 function xcat () {
615     emulate -L zsh
616     if (( ${#argv} != 1 )) ; then
617         printf 'usage: xcat FILE\n' >&2
618         return 1
619     fi
620
621     [[ -r $1 ]] && cat $1
622     return 0
623 }
624
625 # Remove these functions again, they are of use only in these
626 # setup files. This should be called at the end of .zshrc.
627 function xunfunction () {
628     emulate -L zsh
629     local -a funcs
630     local func
631     funcs=(salias xcat xsource xunfunction zrcautoload zrcautozle)
632     for func in $funcs ; do
633         [[ -n ${functions[$func]} ]] \
634             && unfunction $func
635     done
636     return 0
637 }
638
639 # this allows us to stay in sync with grml's zshrc and put own
640 # modifications in ~/.zshrc.local
641 function zrclocal () {
642     xsource "/etc/zsh/zshrc.local"
643     xsource "${ZDOTDIR:-${HOME}}/.zshrc.local"
644     return 0
645 }
646
647 # locale setup
648 if (( ZSH_NO_DEFAULT_LOCALE == 0 )); then
649     xsource "/etc/default/locale"
650 fi
651
652 for var in LANG LC_ALL LC_MESSAGES ; do
653     [[ -n ${(P)var} ]] && export $var
654 done
655 builtin unset -v var
656
657 # set some variables
658 if check_com -c vim ; then
659 #v#
660     export EDITOR=${EDITOR:-vim}
661 else
662     export EDITOR=${EDITOR:-vi}
663 fi
664
665 #v#
666 export PAGER=${PAGER:-less}
667
668 #v#
669 export MAIL=${MAIL:-/var/mail/$USER}
670
671 # color setup for ls:
672 check_com -c dircolors && eval $(dircolors -b)
673 # color setup for ls on OS X / FreeBSD:
674 isdarwin && export CLICOLOR=1
675 isfreebsd && export CLICOLOR=1
676
677 # do MacPorts setup on darwin
678 if isdarwin && [[ -d /opt/local ]]; then
679     # Note: PATH gets set in /etc/zprofile on Darwin, so this can't go into
680     # zshenv.
681     PATH="/opt/local/bin:/opt/local/sbin:$PATH"
682     MANPATH="/opt/local/share/man:$MANPATH"
683 fi
684 # do Fink setup on darwin
685 isdarwin && xsource /sw/bin/init.sh
686
687 # load our function and completion directories
688 for fdir in /usr/share/grml/zsh/completion /usr/share/grml/zsh/functions; do
689     fpath=( ${fdir} ${fdir}/**/*(/N) ${fpath} )
690 done
691 typeset -aU ffiles
692 ffiles=(/usr/share/grml/zsh/functions/**/[^_]*[^~](N.:t))
693 (( ${#ffiles} > 0 )) && autoload -U "${ffiles[@]}"
694 unset -v fdir ffiles
695
696 # support colors in less
697 export LESS_TERMCAP_mb=$'\E[01;31m'
698 export LESS_TERMCAP_md=$'\E[01;31m'
699 export LESS_TERMCAP_me=$'\E[0m'
700 export LESS_TERMCAP_se=$'\E[0m'
701 export LESS_TERMCAP_so=$'\E[01;44;33m'
702 export LESS_TERMCAP_ue=$'\E[0m'
703 export LESS_TERMCAP_us=$'\E[01;32m'
704
705 # mailchecks
706 MAILCHECK=30
707
708 # report about cpu-/system-/user-time of command if running longer than
709 # 5 seconds
710 REPORTTIME=5
711
712 # watch for everyone but me and root
713 watch=(notme root)
714
715 # automatically remove duplicates from these arrays
716 typeset -U path PATH cdpath CDPATH fpath FPATH manpath MANPATH
717
718 # Load a few modules
719 is4 && \
720 for mod in parameter complist deltochar mathfunc ; do
721     zmodload -i zsh/${mod} 2>/dev/null
722     grml_status_feature mod:$mod $?
723 done && builtin unset -v mod
724
725 # autoload zsh modules when they are referenced
726 if is4 ; then
727     zmodload -a  zsh/stat    zstat
728     zmodload -a  zsh/zpty    zpty
729     zmodload -ap zsh/mapfile mapfile
730 fi
731
732 # completion system
733 COMPDUMPFILE=${COMPDUMPFILE:-${ZDOTDIR:-${HOME}}/.zcompdump}
734 if zrcautoload compinit ; then
735     typeset -a tmp
736     zstyle -a ':grml:completion:compinit' arguments tmp
737     compinit -d ${COMPDUMPFILE} "${tmp[@]}"
738     grml_status_feature compinit $?
739     unset tmp
740 else
741     grml_status_feature compinit 1
742     function compdef { }
743 fi
744
745 # completion system
746
747 # called later (via is4 && grmlcomp)
748 # note: use 'zstyle' for getting current settings
749 #         press ^xh (control-x h) for getting tags in context; ^x? (control-x ?) to run complete_debug with trace output
750 function grmlcomp () {
751     # TODO: This could use some additional information
752
753     # Make sure the completion system is initialised
754     (( ${+_comps} )) || return 1
755
756     # allow one error for every three characters typed in approximate completer
757     zstyle ':completion:*:approximate:'    max-errors 'reply=( $((($#PREFIX+$#SUFFIX)/3 )) numeric )'
758
759     # don't complete backup files as executables
760     zstyle ':completion:*:complete:-command-::commands' ignored-patterns '(aptitude-*|*\~)'
761
762     # start menu completion only if it could find no unambiguous initial string
763     zstyle ':completion:*:correct:*'       insert-unambiguous true
764     zstyle ':completion:*:corrections'     format $'%{\e[0;31m%}%d (errors: %e)%{\e[0m%}'
765     zstyle ':completion:*:correct:*'       original true
766
767     # activate color-completion
768     zstyle ':completion:*:default'         list-colors ${(s.:.)LS_COLORS}
769
770     # format on completion
771     zstyle ':completion:*:descriptions'    format $'%{\e[0;31m%}completing %B%d%b%{\e[0m%}'
772
773     # automatically complete 'cd -<tab>' and 'cd -<ctrl-d>' with menu
774     # zstyle ':completion:*:*:cd:*:directory-stack' menu yes select
775
776     # insert all expansions for expand completer
777     zstyle ':completion:*:expand:*'        tag-order all-expansions
778     zstyle ':completion:*:history-words'   list false
779
780     # activate menu
781     zstyle ':completion:*:history-words'   menu yes
782
783     # ignore duplicate entries
784     zstyle ':completion:*:history-words'   remove-all-dups yes
785     zstyle ':completion:*:history-words'   stop yes
786
787     # match uppercase from lowercase
788     zstyle ':completion:*'                 matcher-list 'm:{a-z}={A-Z}'
789
790     # separate matches into groups
791     zstyle ':completion:*:matches'         group 'yes'
792     zstyle ':completion:*'                 group-name ''
793
794     if [[ "$NOMENU" -eq 0 ]] ; then
795         # if there are more than 5 options allow selecting from a menu
796         zstyle ':completion:*'               menu select=5
797     else
798         # don't use any menus at all
799         setopt no_auto_menu
800     fi
801
802     zstyle ':completion:*:messages'        format '%d'
803     zstyle ':completion:*:options'         auto-description '%d'
804
805     # describe options in full
806     zstyle ':completion:*:options'         description 'yes'
807
808     # on processes completion complete all user processes
809     zstyle ':completion:*:processes'       command 'ps -au$USER'
810
811     # offer indexes before parameters in subscripts
812     zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters
813
814     # provide verbose completion information
815     zstyle ':completion:*'                 verbose true
816
817     # recent (as of Dec 2007) zsh versions are able to provide descriptions
818     # for commands (read: 1st word in the line) that it will list for the user
819     # to choose from. The following disables that, because it's not exactly fast.
820     zstyle ':completion:*:-command-:*:'    verbose false
821
822     # set format for warnings
823     zstyle ':completion:*:warnings'        format $'%{\e[0;31m%}No matches for:%{\e[0m%} %d'
824
825     # define files to ignore for zcompile
826     zstyle ':completion:*:*:zcompile:*'    ignored-patterns '(*~|*.zwc)'
827     zstyle ':completion:correct:'          prompt 'correct to: %e'
828
829     # Ignore completion functions for commands you don't have:
830     zstyle ':completion::(^approximate*):*:functions' ignored-patterns '_*'
831
832     # Provide more processes in completion of programs like killall:
833     zstyle ':completion:*:processes-names' command 'ps c -u ${USER} -o command | uniq'
834
835     # complete manual by their section
836     zstyle ':completion:*:manuals'    separate-sections true
837     zstyle ':completion:*:manuals.*'  insert-sections   true
838     zstyle ':completion:*:man:*'      menu yes select
839
840     # Search path for sudo completion
841     zstyle ':completion:*:sudo:*' command-path /usr/local/sbin \
842                                                /usr/local/bin  \
843                                                /usr/sbin       \
844                                                /usr/bin        \
845                                                /sbin           \
846                                                /bin            \
847                                                /usr/X11R6/bin
848
849     # provide .. as a completion
850     zstyle ':completion:*' special-dirs ..
851
852     # run rehash on completion so new installed program are found automatically:
853     function _force_rehash () {
854         (( CURRENT == 1 )) && rehash
855         return 1
856     }
857
858     ## correction
859     # some people don't like the automatic correction - so run 'NOCOR=1 zsh' to deactivate it
860     if [[ "$NOCOR" -gt 0 ]] ; then
861         zstyle ':completion:*' completer _oldlist _expand _force_rehash _complete _files _ignored
862         setopt nocorrect
863     else
864         # try to be smart about when to use what completer...
865         setopt correct
866         zstyle -e ':completion:*' completer '
867             if [[ $_last_try != "$HISTNO$BUFFER$CURSOR" ]] ; then
868                 _last_try="$HISTNO$BUFFER$CURSOR"
869                 reply=(_complete _match _ignored _prefix _files)
870             else
871                 if [[ $words[1] == (rm|mv) ]] ; then
872                     reply=(_complete _files)
873                 else
874                     reply=(_oldlist _expand _force_rehash _complete _ignored _correct _approximate _files)
875                 fi
876             fi'
877     fi
878
879     # command for process lists, the local web server details and host completion
880     zstyle ':completion:*:urls' local 'www' '/var/www/' 'public_html'
881
882     # Some functions, like _apt and _dpkg, are very slow. We can use a cache in
883     # order to speed things up
884     if [[ ${GRML_COMP_CACHING:-yes} == yes ]]; then
885         GRML_COMP_CACHE_DIR=${GRML_COMP_CACHE_DIR:-${ZDOTDIR:-$HOME}/.cache}
886         if [[ ! -d ${GRML_COMP_CACHE_DIR} ]]; then
887             command mkdir -p "${GRML_COMP_CACHE_DIR}"
888         fi
889         zstyle ':completion:*' use-cache  yes
890         zstyle ':completion:*:complete:*' cache-path "${GRML_COMP_CACHE_DIR}"
891     fi
892
893     # host completion
894     if is42 ; then
895         [[ -r ~/.ssh/config ]] && _ssh_config_hosts=(${${(s: :)${(ps:\t:)${${(@M)${(f)"$(<$HOME/.ssh/config)"}:#Host *}#Host }}}:#*[*?]*}) || _ssh_config_hosts=()
896         [[ -r ~/.ssh/known_hosts ]] && _ssh_hosts=(${${${${(f)"$(<$HOME/.ssh/known_hosts)"}:#[\|]*}%%\ *}%%,*}) || _ssh_hosts=()
897         [[ -r /etc/hosts ]] && [[ "$NOETCHOSTS" -eq 0 ]] && : ${(A)_etc_hosts:=${(s: :)${(ps:\t:)${${(f)~~"$(grep -v '^0\.0\.0.\0\|^127\.0\.0\.1\|^::1 ' /etc/hosts)"}%%\#*}##[:blank:]#[^[:blank:]]#}}} || _etc_hosts=()
898     else
899         _ssh_config_hosts=()
900         _ssh_hosts=()
901         _etc_hosts=()
902     fi
903
904     local localname
905     localname="$(uname -n)"
906     hosts=(
907         "${localname}"
908         "$_ssh_config_hosts[@]"
909         "$_ssh_hosts[@]"
910         "$_etc_hosts[@]"
911         localhost
912     )
913     zstyle ':completion:*:hosts' hosts $hosts
914     # TODO: so, why is this here?
915     #  zstyle '*' hosts $hosts
916
917     # use generic completion system for programs not yet defined; (_gnu_generic works
918     # with commands that provide a --help option with "standard" gnu-like output.)
919     for compcom in cp deborphan df feh fetchipac gpasswd head hnb ipacsum mv \
920                    pal stow uname ; do
921         [[ -z ${_comps[$compcom]} ]] && compdef _gnu_generic ${compcom}
922     done; unset compcom
923
924     # see upgrade function in this file
925     compdef _hosts upgrade
926 }
927
928 # Keyboard setup: The following is based on the same code, we wrote for
929 # debian's setup. It ensures the terminal is in the right mode, when zle is
930 # active, so the values from $terminfo are valid. Therefore, this setup should
931 # work on all systems, that have support for `terminfo'. It also requires the
932 # zsh in use to have the `zsh/terminfo' module built.
933 #
934 # If you are customising your `zle-line-init()' or `zle-line-finish()'
935 # functions, make sure you call the following utility functions in there:
936 #
937 #     - zle-line-init():      zle-smkx
938 #     - zle-line-finish():    zle-rmkx
939
940 # Use emacs-like key bindings by default:
941 bindkey -e
942
943 # Custom widgets:
944
945 ## beginning-of-line OR beginning-of-buffer OR beginning of history
946 ## by: Bart Schaefer <schaefer@brasslantern.com>, Bernhard Tittelbach
947 function beginning-or-end-of-somewhere () {
948     local hno=$HISTNO
949     if [[ ( "${LBUFFER[-1]}" == $'\n' && "${WIDGET}" == beginning-of* ) || \
950       ( "${RBUFFER[1]}" == $'\n' && "${WIDGET}" == end-of* ) ]]; then
951         zle .${WIDGET:s/somewhere/buffer-or-history/} "$@"
952     else
953         zle .${WIDGET:s/somewhere/line-hist/} "$@"
954         if (( HISTNO != hno )); then
955             zle .${WIDGET:s/somewhere/buffer-or-history/} "$@"
956         fi
957     fi
958 }
959 zle -N beginning-of-somewhere beginning-or-end-of-somewhere
960 zle -N end-of-somewhere beginning-or-end-of-somewhere
961
962 # add a command line to the shells history without executing it
963 function commit-to-history () {
964     print -rs ${(z)BUFFER}
965     zle send-break
966 }
967 zle -N commit-to-history
968
969 # only slash should be considered as a word separator:
970 function slash-backward-kill-word () {
971     local WORDCHARS="${WORDCHARS:s@/@}"
972     # zle backward-word
973     zle backward-kill-word
974 }
975 zle -N slash-backward-kill-word
976
977 # a generic accept-line wrapper
978
979 # This widget can prevent unwanted autocorrections from command-name
980 # to _command-name, rehash automatically on enter and call any number
981 # of builtin and user-defined widgets in different contexts.
982 #
983 # For a broader description, see:
984 # <http://bewatermyfriend.org/posts/2007/12-26.11-50-38-tooltime.html>
985 #
986 # The code is imported from the file 'zsh/functions/accept-line' from
987 # <http://ft.bewatermyfriend.org/comp/zsh/zsh-dotfiles.tar.bz2>, which
988 # distributed under the same terms as zsh itself.
989
990 # A newly added command will may not be found or will cause false
991 # correction attempts, if you got auto-correction set. By setting the
992 # following style, we force accept-line() to rehash, if it cannot
993 # find the first word on the command line in the $command[] hash.
994 zstyle ':acceptline:*' rehash true
995
996 function Accept-Line () {
997     setopt localoptions noksharrays
998     local -a subs
999     local -xi aldone
1000     local sub
1001     local alcontext=${1:-$alcontext}
1002
1003     zstyle -a ":acceptline:${alcontext}" actions subs
1004
1005     (( ${#subs} < 1 )) && return 0
1006
1007     (( aldone = 0 ))
1008     for sub in ${subs} ; do
1009         [[ ${sub} == 'accept-line' ]] && sub='.accept-line'
1010         zle ${sub}
1011
1012         (( aldone > 0 )) && break
1013     done
1014 }
1015
1016 function Accept-Line-getdefault () {
1017     emulate -L zsh
1018     local default_action
1019
1020     zstyle -s ":acceptline:${alcontext}" default_action default_action
1021     case ${default_action} in
1022         ((accept-line|))
1023             printf ".accept-line"
1024             ;;
1025         (*)
1026             printf ${default_action}
1027             ;;
1028     esac
1029 }
1030
1031 function Accept-Line-HandleContext () {
1032     zle Accept-Line
1033
1034     default_action=$(Accept-Line-getdefault)
1035     zstyle -T ":acceptline:${alcontext}" call_default \
1036         && zle ${default_action}
1037 }
1038
1039 function accept-line () {
1040     setopt localoptions noksharrays
1041     local -a cmdline
1042     local -x alcontext
1043     local buf com fname format msg default_action
1044
1045     alcontext='default'
1046     buf="${BUFFER}"
1047     cmdline=(${(z)BUFFER})
1048     com="${cmdline[1]}"
1049     fname="_${com}"
1050
1051     Accept-Line 'preprocess'
1052
1053     zstyle -t ":acceptline:${alcontext}" rehash \
1054         && [[ -z ${commands[$com]} ]]           \
1055         && rehash
1056
1057     if    [[ -n ${com}               ]] \
1058        && [[ -n ${reswords[(r)$com]} ]] \
1059        || [[ -n ${aliases[$com]}     ]] \
1060        || [[ -n ${functions[$com]}   ]] \
1061        || [[ -n ${builtins[$com]}    ]] \
1062        || [[ -n ${commands[$com]}    ]] ; then
1063
1064         # there is something sensible to execute, just do it.
1065         alcontext='normal'
1066         Accept-Line-HandleContext
1067
1068         return
1069     fi
1070
1071     if    [[ -o correct              ]] \
1072        || [[ -o correctall           ]] \
1073        && [[ -n ${functions[$fname]} ]] ; then
1074
1075         # nothing there to execute but there is a function called
1076         # _command_name; a completion widget. Makes no sense to
1077         # call it on the commandline, but the correct{,all} options
1078         # will ask for it nevertheless, so warn the user.
1079         if [[ ${LASTWIDGET} == 'accept-line' ]] ; then
1080             # Okay, we warned the user before, he called us again,
1081             # so have it his way.
1082             alcontext='force'
1083             Accept-Line-HandleContext
1084
1085             return
1086         fi
1087
1088         if zstyle -t ":acceptline:${alcontext}" nocompwarn ; then
1089             alcontext='normal'
1090             Accept-Line-HandleContext
1091         else
1092             # prepare warning message for the user, configurable via zstyle.
1093             zstyle -s ":acceptline:${alcontext}" compwarnfmt msg
1094
1095             if [[ -z ${msg} ]] ; then
1096                 msg="%c will not execute and completion %f exists."
1097             fi
1098
1099             zformat -f msg "${msg}" "c:${com}" "f:${fname}"
1100
1101             zle -M -- "${msg}"
1102         fi
1103         return
1104     elif [[ -n ${buf//[$' \t\n']##/} ]] ; then
1105         # If we are here, the commandline contains something that is not
1106         # executable, which is neither subject to _command_name correction
1107         # and is not empty. might be a variable assignment
1108         alcontext='misc'
1109         Accept-Line-HandleContext
1110
1111         return
1112     fi
1113
1114     # If we got this far, the commandline only contains whitespace, or is empty.
1115     alcontext='empty'
1116     Accept-Line-HandleContext
1117 }
1118
1119 zle -N accept-line
1120 zle -N Accept-Line
1121 zle -N Accept-Line-HandleContext
1122
1123 # power completion / abbreviation expansion / buffer expansion
1124 # see http://zshwiki.org/home/examples/zleiab for details
1125 # less risky than the global aliases but powerful as well
1126 # just type the abbreviation key and afterwards 'ctrl-x .' to expand it
1127 declare -A abk
1128 setopt extendedglob
1129 setopt interactivecomments
1130 abk=(
1131 #   key   # value                  (#d additional doc string)
1132 #A# start
1133     '...'  '../..'
1134     '....' '../../..'
1135     'BG'   '& exit'
1136     'C'    '| wc -l'
1137     'G'    '|& grep '${grep_options:+"${grep_options[*]}"}
1138     'H'    '| head'
1139     'Hl'   ' --help |& less -r'    #d (Display help in pager)
1140     'L'    '| less'
1141     'LL'   '|& less -r'
1142     'M'    '| most'
1143     'N'    '&>/dev/null'           #d (No Output)
1144     'R'    '| tr A-z N-za-m'       #d (ROT13)
1145     'SL'   '| sort | less'
1146     'S'    '| sort -u'
1147     'T'    '| tail'
1148     'V'    '|& vim -'
1149 #A# end
1150     'co'   './configure && make && sudo make install'
1151 )
1152
1153 function zleiab () {
1154     emulate -L zsh
1155     setopt extendedglob
1156     local MATCH
1157
1158     LBUFFER=${LBUFFER%%(#m)[.\-+:|_a-zA-Z0-9]#}
1159     LBUFFER+=${abk[$MATCH]:-$MATCH}
1160 }
1161
1162 zle -N zleiab
1163
1164 function help-show-abk () {
1165   zle -M "$(print "Available abbreviations for expansion:"; print -a -C 2 ${(kv)abk})"
1166 }
1167
1168 zle -N help-show-abk
1169
1170 # press "ctrl-x d" to insert the actual date in the form yyyy-mm-dd
1171 function insert-datestamp () { LBUFFER+=${(%):-'%D{%Y-%m-%d}'}; }
1172 zle -N insert-datestamp
1173
1174 # press esc-m for inserting last typed word again (thanks to caphuso!)
1175 function insert-last-typed-word () { zle insert-last-word -- 0 -1 };
1176 zle -N insert-last-typed-word;
1177
1178 function grml-zsh-fg () {
1179   if (( ${#jobstates} )); then
1180     zle .push-input
1181     [[ -o hist_ignore_space ]] && BUFFER=' ' || BUFFER=''
1182     BUFFER="${BUFFER}fg"
1183     zle .accept-line
1184   else
1185     zle -M 'No background jobs. Doing nothing.'
1186   fi
1187 }
1188 zle -N grml-zsh-fg
1189
1190 # run command line as user root via sudo:
1191 function sudo-command-line () {
1192     [[ -z $BUFFER ]] && zle up-history
1193     local cmd="sudo "
1194     if [[ ${BUFFER} == ${cmd}* ]]; then
1195         CURSOR=$(( CURSOR-${#cmd} ))
1196         BUFFER="${BUFFER#$cmd}"
1197     else
1198         BUFFER="${cmd}${BUFFER}"
1199         CURSOR=$(( CURSOR+${#cmd} ))
1200     fi
1201     zle reset-prompt
1202 }
1203 zle -N sudo-command-line
1204
1205 ### jump behind the first word on the cmdline.
1206 ### useful to add options.
1207 function jump_after_first_word () {
1208     local words
1209     words=(${(z)BUFFER})
1210
1211     if (( ${#words} <= 1 )) ; then
1212         CURSOR=${#BUFFER}
1213     else
1214         CURSOR=${#${words[1]}}
1215     fi
1216 }
1217 zle -N jump_after_first_word
1218
1219 #f5# Create directory under cursor or the selected area
1220 function inplaceMkDirs () {
1221     # Press ctrl-xM to create the directory under the cursor or the selected area.
1222     # To select an area press ctrl-@ or ctrl-space and use the cursor.
1223     # Use case: you type "mv abc ~/testa/testb/testc/" and remember that the
1224     # directory does not exist yet -> press ctrl-XM and problem solved
1225     local PATHTOMKDIR
1226     if ((REGION_ACTIVE==1)); then
1227         local F=$MARK T=$CURSOR
1228         if [[ $F -gt $T ]]; then
1229             F=${CURSOR}
1230             T=${MARK}
1231         fi
1232         # get marked area from buffer and eliminate whitespace
1233         PATHTOMKDIR=${BUFFER[F+1,T]%%[[:space:]]##}
1234         PATHTOMKDIR=${PATHTOMKDIR##[[:space:]]##}
1235     else
1236         local bufwords iword
1237         bufwords=(${(z)LBUFFER})
1238         iword=${#bufwords}
1239         bufwords=(${(z)BUFFER})
1240         PATHTOMKDIR="${(Q)bufwords[iword]}"
1241     fi
1242     [[ -z "${PATHTOMKDIR}" ]] && return 1
1243     PATHTOMKDIR=${~PATHTOMKDIR}
1244     if [[ -e "${PATHTOMKDIR}" ]]; then
1245         zle -M " path already exists, doing nothing"
1246     else
1247         zle -M "$(mkdir -p -v "${PATHTOMKDIR}")"
1248         zle end-of-line
1249     fi
1250 }
1251
1252 zle -N inplaceMkDirs
1253
1254 #v1# set number of lines to display per page
1255 HELP_LINES_PER_PAGE=20
1256 #v1# set location of help-zle cache file
1257 HELP_ZLE_CACHE_FILE=~/.cache/zsh_help_zle_lines.zsh
1258 # helper function for help-zle, actually generates the help text
1259 function help_zle_parse_keybindings () {
1260     emulate -L zsh
1261     setopt extendedglob
1262     unsetopt ksharrays  #indexing starts at 1
1263
1264     #v1# choose files that help-zle will parse for keybindings
1265     ((${+HELPZLE_KEYBINDING_FILES})) || HELPZLE_KEYBINDING_FILES=( /etc/zsh/zshrc ~/.zshrc.pre ~/.zshrc ~/.zshrc.local )
1266
1267     if [[ -r $HELP_ZLE_CACHE_FILE ]]; then
1268         local load_cache=0
1269         local f
1270         for f ($HELPZLE_KEYBINDING_FILES) [[ $f -nt $HELP_ZLE_CACHE_FILE ]] && load_cache=1
1271         [[ $load_cache -eq 0 ]] && . $HELP_ZLE_CACHE_FILE && return
1272     fi
1273
1274     #fill with default keybindings, possibly to be overwritten in a file later
1275     #Note that due to zsh inconsistency on escaping assoc array keys, we encase the key in '' which we will remove later
1276     local -A help_zle_keybindings
1277     help_zle_keybindings['<Ctrl>@']="set MARK"
1278     help_zle_keybindings['<Ctrl>x<Ctrl>j']="vi-join lines"
1279     help_zle_keybindings['<Ctrl>x<Ctrl>b']="jump to matching brace"
1280     help_zle_keybindings['<Ctrl>x<Ctrl>u']="undo"
1281     help_zle_keybindings['<Ctrl>_']="undo"
1282     help_zle_keybindings['<Ctrl>x<Ctrl>f<c>']="find <c> in cmdline"
1283     help_zle_keybindings['<Ctrl>a']="goto beginning of line"
1284     help_zle_keybindings['<Ctrl>e']="goto end of line"
1285     help_zle_keybindings['<Ctrl>t']="transpose charaters"
1286     help_zle_keybindings['<Alt>t']="transpose words"
1287     help_zle_keybindings['<Alt>s']="spellcheck word"
1288     help_zle_keybindings['<Ctrl>k']="backward kill buffer"
1289     help_zle_keybindings['<Ctrl>u']="forward kill buffer"
1290     help_zle_keybindings['<Ctrl>y']="insert previously killed word/string"
1291     help_zle_keybindings["<Alt>'"]="quote line"
1292     help_zle_keybindings['<Alt>"']="quote from mark to cursor"
1293     help_zle_keybindings['<Alt><arg>']="repeat next cmd/char <arg> times (<Alt>-<Alt>1<Alt>0a -> -10 times 'a')"
1294     help_zle_keybindings['<Alt>u']="make next word Uppercase"
1295     help_zle_keybindings['<Alt>l']="make next word lowercase"
1296     help_zle_keybindings['<Ctrl>xG']="preview expansion under cursor"
1297     help_zle_keybindings['<Alt>q']="push current CL into background, freeing it. Restore on next CL"
1298     help_zle_keybindings['<Alt>.']="insert (and interate through) last word from prev CLs"
1299     help_zle_keybindings['<Alt>,']="complete word from newer history (consecutive hits)"
1300     help_zle_keybindings['<Alt>m']="repeat last typed word on current CL"
1301     help_zle_keybindings['<Ctrl>v']="insert next keypress symbol literally (e.g. for bindkey)"
1302     help_zle_keybindings['!!:n*<Tab>']="insert last n arguments of last command"
1303     help_zle_keybindings['!!:n-<Tab>']="insert arguments n..N-2 of last command (e.g. mv s s d)"
1304     help_zle_keybindings['<Alt>h']="show help/manpage for current command"
1305
1306     #init global variables
1307     unset help_zle_lines help_zle_sln
1308     typeset -g -a help_zle_lines
1309     typeset -g help_zle_sln=1
1310
1311     local k v f cline
1312     local lastkeybind_desc contents     #last description starting with #k# that we found
1313     local num_lines_elapsed=0            #number of lines between last description and keybinding
1314     #search config files in the order they a called (and thus the order in which they overwrite keybindings)
1315     for f in $HELPZLE_KEYBINDING_FILES; do
1316         [[ -r "$f" ]] || continue   #not readable ? skip it
1317         contents="$(<$f)"
1318         for cline in "${(f)contents}"; do
1319             #zsh pattern: matches lines like: #k# ..............
1320             if [[ "$cline" == (#s)[[:space:]]#\#k\#[[:space:]]##(#b)(*)[[:space:]]#(#e) ]]; then
1321                 lastkeybind_desc="$match[*]"
1322                 num_lines_elapsed=0
1323             #zsh pattern: matches lines that set a keybinding using bind2map, bindkey or compdef -k
1324             #             ignores lines that are commentend out
1325             #             grabs first in '' or "" enclosed string with length between 1 and 6 characters
1326             elif [[ "$cline" == [^#]#(bind2maps[[:space:]](*)-s|bindkey|compdef -k)[[:space:]](*)(#b)(\"((?)(#c1,6))\"|\'((?)(#c1,6))\')(#B)(*)  ]]; then
1327                 #description previously found ? description not more than 2 lines away ? keybinding not empty ?
1328                 if [[ -n $lastkeybind_desc && $num_lines_elapsed -lt 2 && -n $match[1] ]]; then
1329                     #substitute keybinding string with something readable
1330                     k=${${${${${${${match[1]/\\e\^h/<Alt><BS>}/\\e\^\?/<Alt><BS>}/\\e\[5~/<PageUp>}/\\e\[6~/<PageDown>}//(\\e|\^\[)/<Alt>}//\^/<Ctrl>}/3~/<Alt><Del>}
1331                     #put keybinding in assoc array, possibly overwriting defaults or stuff found in earlier files
1332                     #Note that we are extracting the keybinding-string including the quotes (see Note at beginning)
1333                     help_zle_keybindings[${k}]=$lastkeybind_desc
1334                 fi
1335                 lastkeybind_desc=""
1336             else
1337               ((num_lines_elapsed++))
1338             fi
1339         done
1340     done
1341     unset contents
1342     #calculate length of keybinding column
1343     local kstrlen=0
1344     for k (${(k)help_zle_keybindings[@]}) ((kstrlen < ${#k})) && kstrlen=${#k}
1345     #convert the assoc array into preformated lines, which we are able to sort
1346     for k v in ${(kv)help_zle_keybindings[@]}; do
1347         #pad keybinding-string to kstrlen chars and remove outermost characters (i.e. the quotes)
1348         help_zle_lines+=("${(r:kstrlen:)k[2,-2]}${v}")
1349     done
1350     #sort lines alphabetically
1351     help_zle_lines=("${(i)help_zle_lines[@]}")
1352     [[ -d ${HELP_ZLE_CACHE_FILE:h} ]] || mkdir -p "${HELP_ZLE_CACHE_FILE:h}"
1353     echo "help_zle_lines=(${(q)help_zle_lines[@]})" >| $HELP_ZLE_CACHE_FILE
1354     zcompile $HELP_ZLE_CACHE_FILE
1355 }
1356 typeset -g help_zle_sln
1357 typeset -g -a help_zle_lines
1358
1359 # Provides (partially autogenerated) help on keybindings and the zsh line editor
1360 function help-zle () {
1361     emulate -L zsh
1362     unsetopt ksharrays  #indexing starts at 1
1363     #help lines already generated ? no ? then do it
1364     [[ ${+functions[help_zle_parse_keybindings]} -eq 1 ]] && {help_zle_parse_keybindings && unfunction help_zle_parse_keybindings}
1365     #already displayed all lines ? go back to the start
1366     [[ $help_zle_sln -gt ${#help_zle_lines} ]] && help_zle_sln=1
1367     local sln=$help_zle_sln
1368     #note that help_zle_sln is a global var, meaning we remember the last page we viewed
1369     help_zle_sln=$((help_zle_sln + HELP_LINES_PER_PAGE))
1370     zle -M "${(F)help_zle_lines[sln,help_zle_sln-1]}"
1371 }
1372 zle -N help-zle
1373
1374 ## complete word from currently visible Screen or Tmux buffer.
1375 if check_com -c screen || check_com -c tmux; then
1376     function _complete_screen_display () {
1377         [[ "$TERM" != "screen" ]] && return 1
1378
1379         local TMPFILE=$(mktemp)
1380         local -U -a _screen_display_wordlist
1381         trap "rm -f $TMPFILE" EXIT
1382
1383         # fill array with contents from screen hardcopy
1384         if ((${+TMUX})); then
1385             #works, but crashes tmux below version 1.4
1386             #luckily tmux -V option to ask for version, was also added in 1.4
1387             tmux -V &>/dev/null || return
1388             tmux -q capture-pane \; save-buffer -b 0 $TMPFILE \; delete-buffer -b 0
1389         else
1390             screen -X hardcopy $TMPFILE
1391             # screen sucks, it dumps in latin1, apparently always. so recode it
1392             # to system charset
1393             check_com recode && recode latin1 $TMPFILE
1394         fi
1395         _screen_display_wordlist=( ${(QQ)$(<$TMPFILE)} )
1396         # remove PREFIX to be completed from that array
1397         _screen_display_wordlist[${_screen_display_wordlist[(i)$PREFIX]}]=""
1398         compadd -a _screen_display_wordlist
1399     }
1400     #m# k CTRL-x\,\,\,S Complete word from GNU screen buffer
1401     bindkey -r "^xS"
1402     compdef -k _complete_screen_display complete-word '^xS'
1403 fi
1404
1405 # Load a few more functions and tie them to widgets, so they can be bound:
1406
1407 function zrcautozle () {
1408     emulate -L zsh
1409     local fnc=$1
1410     zrcautoload $fnc && zle -N $fnc
1411 }
1412
1413 function zrcgotwidget () {
1414     (( ${+widgets[$1]} ))
1415 }
1416
1417 function zrcgotkeymap () {
1418     [[ -n ${(M)keymaps:#$1} ]]
1419 }
1420
1421 zrcautozle insert-files
1422 zrcautozle edit-command-line
1423 zrcautozle insert-unicode-char
1424 if zrcautoload history-search-end; then
1425     zle -N history-beginning-search-backward-end history-search-end
1426     zle -N history-beginning-search-forward-end  history-search-end
1427 fi
1428 zle -C hist-complete complete-word _generic
1429 zstyle ':completion:hist-complete:*' completer _history
1430
1431 # The actual terminal setup hooks and bindkey-calls:
1432
1433 # An array to note missing features to ease diagnosis in case of problems.
1434 typeset -ga grml_missing_features
1435
1436 function zrcbindkey () {
1437     if (( ARGC )) && zrcgotwidget ${argv[-1]}; then
1438         bindkey "$@"
1439     fi
1440 }
1441
1442 function bind2maps () {
1443     local i sequence widget
1444     local -a maps
1445
1446     while [[ "$1" != "--" ]]; do
1447         maps+=( "$1" )
1448         shift
1449     done
1450     shift
1451
1452     if [[ "$1" == "-s" ]]; then
1453         shift
1454         sequence="$1"
1455     else
1456         sequence="${key[$1]}"
1457     fi
1458     widget="$2"
1459
1460     [[ -z "$sequence" ]] && return 1
1461
1462     for i in "${maps[@]}"; do
1463         zrcbindkey -M "$i" "$sequence" "$widget"
1464     done
1465 }
1466
1467 if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
1468     function zle-smkx () {
1469         emulate -L zsh
1470         printf '%s' ${terminfo[smkx]}
1471     }
1472     function zle-rmkx () {
1473         emulate -L zsh
1474         printf '%s' ${terminfo[rmkx]}
1475     }
1476     function zle-line-init () {
1477         zle-smkx
1478     }
1479     function zle-line-finish () {
1480         zle-rmkx
1481     }
1482     zle -N zle-line-init
1483     zle -N zle-line-finish
1484 else
1485     for i in {s,r}mkx; do
1486         (( ${+terminfo[$i]} )) || grml_missing_features+=($i)
1487     done
1488     unset i
1489 fi
1490
1491 typeset -A key
1492 key=(
1493     Home     "${terminfo[khome]}"
1494     End      "${terminfo[kend]}"
1495     Insert   "${terminfo[kich1]}"
1496     Delete   "${terminfo[kdch1]}"
1497     Up       "${terminfo[kcuu1]}"
1498     Down     "${terminfo[kcud1]}"
1499     Left     "${terminfo[kcub1]}"
1500     Right    "${terminfo[kcuf1]}"
1501     PageUp   "${terminfo[kpp]}"
1502     PageDown "${terminfo[knp]}"
1503     BackTab  "${terminfo[kcbt]}"
1504 )
1505
1506 # Guidelines for adding key bindings:
1507 #
1508 #   - Do not add hardcoded escape sequences, to enable non standard key
1509 #     combinations such as Ctrl-Meta-Left-Cursor. They are not easily portable.
1510 #
1511 #   - Adding Ctrl characters, such as '^b' is okay; note that '^b' and '^B' are
1512 #     the same key.
1513 #
1514 #   - All keys from the $key[] mapping are obviously okay.
1515 #
1516 #   - Most terminals send "ESC x" when Meta-x is pressed. Thus, sequences like
1517 #     '\ex' are allowed in here as well.
1518
1519 bind2maps emacs             -- Home   beginning-of-somewhere
1520 bind2maps       viins vicmd -- Home   vi-beginning-of-line
1521 bind2maps emacs             -- End    end-of-somewhere
1522 bind2maps       viins vicmd -- End    vi-end-of-line
1523 bind2maps emacs viins       -- Insert overwrite-mode
1524 bind2maps             vicmd -- Insert vi-insert
1525 bind2maps emacs             -- Delete delete-char
1526 bind2maps       viins vicmd -- Delete vi-delete-char
1527 bind2maps emacs viins vicmd -- Up     up-line-or-search
1528 bind2maps emacs viins vicmd -- Down   down-line-or-search
1529 bind2maps emacs             -- Left   backward-char
1530 bind2maps       viins vicmd -- Left   vi-backward-char
1531 bind2maps emacs             -- Right  forward-char
1532 bind2maps       viins vicmd -- Right  vi-forward-char
1533 #k# Perform abbreviation expansion
1534 bind2maps emacs viins       -- -s '^x.' zleiab
1535 #k# Display list of abbreviations that would expand
1536 bind2maps emacs viins       -- -s '^xb' help-show-abk
1537 #k# mkdir -p <dir> from string under cursor or marked area
1538 bind2maps emacs viins       -- -s '^xM' inplaceMkDirs
1539 #k# display help for keybindings and ZLE
1540 bind2maps emacs viins       -- -s '^xz' help-zle
1541 #k# Insert files and test globbing
1542 bind2maps emacs viins       -- -s "^xf" insert-files
1543 #k# Edit the current line in \kbd{\$EDITOR}
1544 bind2maps emacs viins       -- -s '\ee' edit-command-line
1545 #k# search history backward for entry beginning with typed text
1546 bind2maps emacs viins       -- -s '^xp' history-beginning-search-backward-end
1547 #k# search history forward for entry beginning with typed text
1548 bind2maps emacs viins       -- -s '^xP' history-beginning-search-forward-end
1549 #k# search history backward for entry beginning with typed text
1550 bind2maps emacs viins       -- PageUp history-beginning-search-backward-end
1551 #k# search history forward for entry beginning with typed text
1552 bind2maps emacs viins       -- PageDown history-beginning-search-forward-end
1553 bind2maps emacs viins       -- -s "^x^h" commit-to-history
1554 #k# Kill left-side word or everything up to next slash
1555 bind2maps emacs viins       -- -s '\ev' slash-backward-kill-word
1556 #k# Kill left-side word or everything up to next slash
1557 bind2maps emacs viins       -- -s '\e^h' slash-backward-kill-word
1558 #k# Kill left-side word or everything up to next slash
1559 bind2maps emacs viins       -- -s '\e^?' slash-backward-kill-word
1560 # Do history expansion on space:
1561 bind2maps emacs viins       -- -s ' ' magic-space
1562 #k# Trigger menu-complete
1563 bind2maps emacs viins       -- -s '\ei' menu-complete  # menu completion via esc-i
1564 #k# Insert a timestamp on the command line (yyyy-mm-dd)
1565 bind2maps emacs viins       -- -s '^xd' insert-datestamp
1566 #k# Insert last typed word
1567 bind2maps emacs viins       -- -s "\em" insert-last-typed-word
1568 #k# A smart shortcut for \kbd{fg<enter>}
1569 bind2maps emacs viins       -- -s '^z' grml-zsh-fg
1570 #k# prepend the current command with "sudo"
1571 bind2maps emacs viins       -- -s "^os" sudo-command-line
1572 #k# jump to after first word (for adding options)
1573 bind2maps emacs viins       -- -s '^x1' jump_after_first_word
1574 #k# complete word from history with menu
1575 bind2maps emacs viins       -- -s "^x^x" hist-complete
1576
1577 # insert unicode character
1578 # usage example: 'ctrl-x i' 00A7 'ctrl-x i' will give you an Â§
1579 # See for example http://unicode.org/charts/ for unicode characters code
1580 #k# Insert Unicode character
1581 bind2maps emacs viins       -- -s '^xi' insert-unicode-char
1582
1583 # use the new *-pattern-* widgets for incremental history search
1584 if zrcgotwidget history-incremental-pattern-search-backward; then
1585     for seq wid in '^r' history-incremental-pattern-search-backward \
1586                    '^s' history-incremental-pattern-search-forward
1587     do
1588         bind2maps emacs viins vicmd -- -s $seq $wid
1589     done
1590     builtin unset -v seq wid
1591 fi
1592
1593 if zrcgotkeymap menuselect; then
1594     #m# k Shift-tab Perform backwards menu completion
1595     bind2maps menuselect -- BackTab reverse-menu-complete
1596
1597     #k# menu selection: pick item but stay in the menu
1598     bind2maps menuselect -- -s '\e^M' accept-and-menu-complete
1599     # also use + and INSERT since it's easier to press repeatedly
1600     bind2maps menuselect -- -s '+' accept-and-menu-complete
1601     bind2maps menuselect -- Insert accept-and-menu-complete
1602
1603     # accept a completion and try to complete again by using menu
1604     # completion; very useful with completing directories
1605     # by using 'undo' one's got a simple file browser
1606     bind2maps menuselect -- -s '^o' accept-and-infer-next-history
1607 fi
1608
1609 # Finally, here are still a few hardcoded escape sequences; Special sequences
1610 # like Ctrl-<Cursor-key> etc do suck a fair bit, because they are not
1611 # standardised and most of the time are not available in a terminals terminfo
1612 # entry.
1613 #
1614 # While we do not encourage adding bindings like these, we will keep these for
1615 # backward compatibility.
1616
1617 ## use Ctrl-left-arrow and Ctrl-right-arrow for jumping to word-beginnings on
1618 ## the command line.
1619 # URxvt sequences:
1620 bind2maps emacs viins vicmd -- -s '\eOc' forward-word
1621 bind2maps emacs viins vicmd -- -s '\eOd' backward-word
1622 # These are for xterm:
1623 bind2maps emacs viins vicmd -- -s '\e[1;5C' forward-word
1624 bind2maps emacs viins vicmd -- -s '\e[1;5D' backward-word
1625 ## the same for alt-left-arrow and alt-right-arrow
1626 # URxvt again:
1627 bind2maps emacs viins vicmd -- -s '\e\e[C' forward-word
1628 bind2maps emacs viins vicmd -- -s '\e\e[D' backward-word
1629 # Xterm again:
1630 bind2maps emacs viins vicmd -- -s '^[[1;3C' forward-word
1631 bind2maps emacs viins vicmd -- -s '^[[1;3D' backward-word
1632 # Also try ESC Left/Right:
1633 bind2maps emacs viins vicmd -- -s '\e'${key[Right]} forward-word
1634 bind2maps emacs viins vicmd -- -s '\e'${key[Left]}  backward-word
1635
1636 # autoloading
1637
1638 zrcautoload zmv
1639 zrcautoload zed
1640
1641 # we don't want to quote/espace URLs on our own...
1642 # if autoload -U url-quote-magic ; then
1643 #    zle -N self-insert url-quote-magic
1644 #    zstyle ':url-quote-magic:*' url-metas '*?[]^()~#{}='
1645 # else
1646 #    print 'Notice: no url-quote-magic available :('
1647 # fi
1648 if is51 ; then
1649   # url-quote doesn't work without bracketed-paste-magic since Zsh 5.1
1650   alias url-quote='autoload -U bracketed-paste-magic url-quote-magic;
1651                    zle -N bracketed-paste bracketed-paste-magic; zle -N self-insert url-quote-magic'
1652 else
1653   alias url-quote='autoload -U url-quote-magic ; zle -N self-insert url-quote-magic'
1654 fi
1655
1656 #m# k ESC-h Call \kbd{run-help} for the 1st word on the command line
1657 alias run-help >&/dev/null && unalias run-help
1658 for rh in run-help{,-git,-ip,-openssl,-p4,-sudo,-svk,-svn}; do
1659     zrcautoload $rh
1660 done; unset rh
1661
1662 # command not found handling
1663
1664 (( ${COMMAND_NOT_FOUND} == 1 )) &&
1665 function command_not_found_handler () {
1666     emulate -L zsh
1667     if [[ -x ${GRML_ZSH_CNF_HANDLER} ]] ; then
1668         ${GRML_ZSH_CNF_HANDLER} $1
1669     fi
1670     return 1
1671 }
1672
1673 # history
1674
1675 #v#
1676 HISTFILE=${HISTFILE:-${ZDOTDIR:-${HOME}}/.zsh_history}
1677 isgrmlcd && HISTSIZE=500  || HISTSIZE=5000
1678 isgrmlcd && SAVEHIST=1000 || SAVEHIST=10000 # useful for setopt append_history
1679
1680 # dirstack handling
1681
1682 DIRSTACKSIZE=${DIRSTACKSIZE:-20}
1683 DIRSTACKFILE=${DIRSTACKFILE:-${ZDOTDIR:-${HOME}}/.zdirs}
1684
1685 if zstyle -T ':grml:chpwd:dirstack' enable; then
1686     typeset -gaU GRML_PERSISTENT_DIRSTACK
1687     function grml_dirstack_filter () {
1688         local -a exclude
1689         local filter entry
1690         if zstyle -s ':grml:chpwd:dirstack' filter filter; then
1691             $filter $1 && return 0
1692         fi
1693         if zstyle -a ':grml:chpwd:dirstack' exclude exclude; then
1694             for entry in "${exclude[@]}"; do
1695                 [[ $1 == ${~entry} ]] && return 0
1696             done
1697         fi
1698         return 1
1699     }
1700
1701     function chpwd () {
1702         (( ZSH_SUBSHELL )) && return
1703         (( $DIRSTACKSIZE <= 0 )) && return
1704         [[ -z $DIRSTACKFILE ]] && return
1705         grml_dirstack_filter $PWD && return
1706         GRML_PERSISTENT_DIRSTACK=(
1707             $PWD "${(@)GRML_PERSISTENT_DIRSTACK[1,$DIRSTACKSIZE]}"
1708         )
1709         builtin print -l ${GRML_PERSISTENT_DIRSTACK} >! ${DIRSTACKFILE}
1710     }
1711
1712     if [[ -f ${DIRSTACKFILE} ]]; then
1713         # Enabling NULL_GLOB via (N) weeds out any non-existing
1714         # directories from the saved dir-stack file.
1715         dirstack=( ${(f)"$(< $DIRSTACKFILE)"}(N) )
1716         # "cd -" won't work after login by just setting $OLDPWD, so
1717         [[ -d $dirstack[1] ]] && cd -q $dirstack[1] && cd -q $OLDPWD
1718     fi
1719
1720     if zstyle -t ':grml:chpwd:dirstack' filter-on-load; then
1721         for i in "${dirstack[@]}"; do
1722             if ! grml_dirstack_filter "$i"; then
1723                 GRML_PERSISTENT_DIRSTACK=(
1724                     "${GRML_PERSISTENT_DIRSTACK[@]}"
1725                     $i
1726                 )
1727             fi
1728         done
1729     else
1730         GRML_PERSISTENT_DIRSTACK=( "${dirstack[@]}" )
1731     fi
1732 fi
1733
1734 # directory based profiles
1735
1736 if is433 ; then
1737
1738 # chpwd_profiles(): Directory Profiles, Quickstart:
1739 #
1740 # In .zshrc.local:
1741 #
1742 #   zstyle ':chpwd:profiles:/usr/src/grml(|/|/*)'   profile grml
1743 #   zstyle ':chpwd:profiles:/usr/src/debian(|/|/*)' profile debian
1744 #   chpwd_profiles
1745 #
1746 # For details see the `grmlzshrc.5' manual page.
1747 function chpwd_profiles () {
1748     local profile context
1749     local -i reexecute
1750
1751     context=":chpwd:profiles:$PWD"
1752     zstyle -s "$context" profile profile || profile='default'
1753     zstyle -T "$context" re-execute && reexecute=1 || reexecute=0
1754
1755     if (( ${+parameters[CHPWD_PROFILE]} == 0 )); then
1756         typeset -g CHPWD_PROFILE
1757         local CHPWD_PROFILES_INIT=1
1758         (( ${+functions[chpwd_profiles_init]} )) && chpwd_profiles_init
1759     elif [[ $profile != $CHPWD_PROFILE ]]; then
1760         (( ${+functions[chpwd_leave_profile_$CHPWD_PROFILE]} )) \
1761             && chpwd_leave_profile_${CHPWD_PROFILE}
1762     fi
1763     if (( reexecute )) || [[ $profile != $CHPWD_PROFILE ]]; then
1764         (( ${+functions[chpwd_profile_$profile]} )) && chpwd_profile_${profile}
1765     fi
1766
1767     CHPWD_PROFILE="${profile}"
1768     return 0
1769 }
1770
1771 chpwd_functions=( ${chpwd_functions} chpwd_profiles )
1772
1773 fi # is433
1774
1775 # Prompt setup for grml:
1776
1777 # set colors for use in prompts (modern zshs allow for the use of %F{red}foo%f
1778 # in prompts to get a red "foo" embedded, but it's good to keep these for
1779 # backwards compatibility).
1780 if is437; then
1781     BLUE="%F{blue}"
1782     RED="%F{red}"
1783     GREEN="%F{green}"
1784     CYAN="%F{cyan}"
1785     MAGENTA="%F{magenta}"
1786     YELLOW="%F{yellow}"
1787     WHITE="%F{white}"
1788     NO_COLOR="%f"
1789 elif zrcautoload colors && colors 2>/dev/null ; then
1790     BLUE="%{${fg[blue]}%}"
1791     RED="%{${fg_bold[red]}%}"
1792     GREEN="%{${fg[green]}%}"
1793     CYAN="%{${fg[cyan]}%}"
1794     MAGENTA="%{${fg[magenta]}%}"
1795     YELLOW="%{${fg[yellow]}%}"
1796     WHITE="%{${fg[white]}%}"
1797     NO_COLOR="%{${reset_color}%}"
1798 else
1799     BLUE=$'%{\e[1;34m%}'
1800     RED=$'%{\e[1;31m%}'
1801     GREEN=$'%{\e[1;32m%}'
1802     CYAN=$'%{\e[1;36m%}'
1803     WHITE=$'%{\e[1;37m%}'
1804     MAGENTA=$'%{\e[1;35m%}'
1805     YELLOW=$'%{\e[1;33m%}'
1806     NO_COLOR=$'%{\e[0m%}'
1807 fi
1808
1809 # First, the easy ones: PS2..4:
1810
1811 # secondary prompt, printed when the shell needs more information to complete a
1812 # command.
1813 PS2='\`%_> '
1814 # selection prompt used within a select loop.
1815 PS3='?# '
1816 # the execution trace prompt (setopt xtrace). default: '+%N:%i>'
1817 PS4='+%N:%i:%_> '
1818
1819 # Some additional features to use with our prompt:
1820 #
1821 #    - battery status
1822 #    - debian_chroot
1823 #    - vcs_info setup and version specific fixes
1824
1825 # display battery status on right side of prompt using 'GRML_DISPLAY_BATTERY=1' in .zshrc.pre
1826
1827 function battery () {
1828 if [[ $GRML_DISPLAY_BATTERY -gt 0 ]] ; then
1829     if islinux ; then
1830         batterylinux
1831     elif isopenbsd ; then
1832         batteryopenbsd
1833     elif isfreebsd ; then
1834         batteryfreebsd
1835     elif isdarwin ; then
1836         batterydarwin
1837     else
1838         #not yet supported
1839         GRML_DISPLAY_BATTERY=0
1840     fi
1841 fi
1842 }
1843
1844 function batterylinux () {
1845 GRML_BATTERY_LEVEL=''
1846 local batteries bat capacity
1847 batteries=( /sys/class/power_supply/BAT*(N) )
1848 if (( $#batteries > 0 )) ; then
1849     for bat in $batteries ; do
1850         if [[ -e $bat/capacity ]]; then
1851             capacity=$(< $bat/capacity)
1852         else
1853             typeset -F energy_full=$(< $bat/energy_full)
1854             typeset -F energy_now=$(< $bat/energy_now)
1855             typeset -i capacity=$(( 100 * $energy_now / $energy_full))
1856         fi
1857         case $(< $bat/status) in
1858         Charging)
1859             GRML_BATTERY_LEVEL+=" ^"
1860             ;;
1861         Discharging)
1862             if (( capacity < 20 )) ; then
1863                 GRML_BATTERY_LEVEL+=" !v"
1864             else
1865                 GRML_BATTERY_LEVEL+=" v"
1866             fi
1867             ;;
1868         *) # Full, Unknown
1869             GRML_BATTERY_LEVEL+=" ="
1870             ;;
1871         esac
1872         GRML_BATTERY_LEVEL+="${capacity}%%"
1873     done
1874 fi
1875 }
1876
1877 function batteryopenbsd () {
1878 GRML_BATTERY_LEVEL=''
1879 local bat batfull batwarn batnow num
1880 for num in 0 1 ; do
1881     bat=$(sysctl -n hw.sensors.acpibat${num} 2>/dev/null)
1882     if [[ -n $bat ]]; then
1883         batfull=${"$(sysctl -n hw.sensors.acpibat${num}.amphour0)"%% *}
1884         batwarn=${"$(sysctl -n hw.sensors.acpibat${num}.amphour1)"%% *}
1885         batnow=${"$(sysctl -n hw.sensors.acpibat${num}.amphour3)"%% *}
1886         case "$(sysctl -n hw.sensors.acpibat${num}.raw0)" in
1887             *" discharging"*)
1888                 if (( batnow < batwarn )) ; then
1889                     GRML_BATTERY_LEVEL+=" !v"
1890                 else
1891                     GRML_BATTERY_LEVEL+=" v"
1892                 fi
1893                 ;;
1894             *" charging"*)
1895                 GRML_BATTERY_LEVEL+=" ^"
1896                 ;;
1897             *)
1898                 GRML_BATTERY_LEVEL+=" ="
1899                 ;;
1900         esac
1901         GRML_BATTERY_LEVEL+="${$(( 100 * batnow / batfull ))%%.*}%%"
1902     fi
1903 done
1904 }
1905
1906 function batteryfreebsd () {
1907 GRML_BATTERY_LEVEL=''
1908 local num
1909 local -A table
1910 for num in 0 1 ; do
1911     table=( ${=${${${${${(M)${(f)"$(acpiconf -i $num 2>&1)"}:#(State|Remaining capacity):*}%%( ##|%)}//:[ $'\t']##/@}// /-}//@/ }} )
1912     if [[ -n $table ]] && [[ $table[State] != "not-present" ]] ; then
1913         case $table[State] in
1914             *discharging*)
1915                 if (( $table[Remaining-capacity] < 20 )) ; then
1916                     GRML_BATTERY_LEVEL+=" !v"
1917                 else
1918                     GRML_BATTERY_LEVEL+=" v"
1919                 fi
1920                 ;;
1921             *charging*)
1922                 GRML_BATTERY_LEVEL+=" ^"
1923                 ;;
1924             *)
1925                 GRML_BATTERY_LEVEL+=" ="
1926                 ;;
1927         esac
1928         GRML_BATTERY_LEVEL+="$table[Remaining-capacity]%%"
1929     fi
1930 done
1931 }
1932
1933 function batterydarwin () {
1934 GRML_BATTERY_LEVEL=''
1935 local -a table
1936 table=( ${$(pmset -g ps)[(w)8,9]%%(\%|);} )
1937 if [[ -n $table[2] ]] ; then
1938     case $table[2] in
1939         charging)
1940             GRML_BATTERY_LEVEL+=" ^"
1941             ;;
1942         discharging)
1943             if (( $table[1] < 20 )) ; then
1944                 GRML_BATTERY_LEVEL+=" !v"
1945             else
1946                 GRML_BATTERY_LEVEL+=" v"
1947             fi
1948             ;;
1949         *)
1950             GRML_BATTERY_LEVEL+=" ="
1951             ;;
1952     esac
1953     GRML_BATTERY_LEVEL+="$table[1]%%"
1954 fi
1955 }
1956
1957 # set variable debian_chroot if running in a chroot with /etc/debian_chroot
1958 if [[ -z "$debian_chroot" ]] && [[ -r /etc/debian_chroot ]] ; then
1959     debian_chroot=$(</etc/debian_chroot)
1960 fi
1961
1962 # gather version control information for inclusion in a prompt
1963
1964 if zrcautoload vcs_info; then
1965     # `vcs_info' in zsh versions 4.3.10 and below have a broken `_realpath'
1966     # function, which can cause a lot of trouble with our directory-based
1967     # profiles. So:
1968     if [[ ${ZSH_VERSION} == 4.3.<-10> ]] ; then
1969         function VCS_INFO_realpath () {
1970             setopt localoptions NO_shwordsplit chaselinks
1971             ( builtin cd -q $1 2> /dev/null && pwd; )
1972         }
1973     fi
1974
1975     zstyle ':vcs_info:*' max-exports 2
1976
1977     if [[ -o restricted ]]; then
1978         zstyle ':vcs_info:*' enable NONE
1979     fi
1980 fi
1981
1982 typeset -A grml_vcs_coloured_formats
1983 typeset -A grml_vcs_plain_formats
1984
1985 grml_vcs_plain_formats=(
1986     format "(%s%)-[%b] "    "zsh: %r"
1987     actionformat "(%s%)-[%b|%a] " "zsh: %r"
1988     rev-branchformat "%b:%r"
1989 )
1990
1991 grml_vcs_coloured_formats=(
1992     format "${MAGENTA}(${NO_COLOR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${MAGENTA}]${NO_COLOR} "
1993     actionformat "${MAGENTA}(${NO_COLOR}%s${MAGENTA})${YELLOW}-${MAGENTA}[${GREEN}%b${YELLOW}|${RED}%a${MAGENTA}]${NO_COLOR} "
1994     rev-branchformat "%b${RED}:${YELLOW}%r"
1995 )
1996
1997 typeset GRML_VCS_COLOUR_MODE=xxx
1998
1999 function grml_vcs_info_toggle_colour () {
2000     emulate -L zsh
2001     if [[ $GRML_VCS_COLOUR_MODE == plain ]]; then
2002         grml_vcs_info_set_formats coloured
2003     else
2004         grml_vcs_info_set_formats plain
2005     fi
2006     return 0
2007 }
2008
2009 function grml_vcs_info_set_formats () {
2010     emulate -L zsh
2011     #setopt localoptions xtrace
2012     local mode=$1 AF F BF
2013     if [[ $mode == coloured ]]; then
2014         AF=${grml_vcs_coloured_formats[actionformat]}
2015         F=${grml_vcs_coloured_formats[format]}
2016         BF=${grml_vcs_coloured_formats[rev-branchformat]}
2017         GRML_VCS_COLOUR_MODE=coloured
2018     else
2019         AF=${grml_vcs_plain_formats[actionformat]}
2020         F=${grml_vcs_plain_formats[format]}
2021         BF=${grml_vcs_plain_formats[rev-branchformat]}
2022         GRML_VCS_COLOUR_MODE=plain
2023     fi
2024
2025     zstyle ':vcs_info:*'              actionformats "$AF" "zsh: %r"
2026     zstyle ':vcs_info:*'              formats       "$F"  "zsh: %r"
2027     zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat  "$BF"
2028     return 0
2029 }
2030
2031 # Change vcs_info formats for the grml prompt. The 2nd format sets up
2032 # $vcs_info_msg_1_ to contain "zsh: repo-name" used to set our screen title.
2033 if [[ "$TERM" == dumb ]] ; then
2034     grml_vcs_info_set_formats plain
2035 else
2036     grml_vcs_info_set_formats coloured
2037 fi
2038
2039 # Now for the fun part: The grml prompt themes in `promptsys' mode of operation
2040
2041 # This actually defines three prompts:
2042 #
2043 #    - grml
2044 #    - grml-large
2045 #    - grml-chroot
2046 #
2047 # They all share the same code and only differ with respect to which items they
2048 # contain. The main source of documentation is the `prompt_grml_help' function
2049 # below, which gets called when the user does this: prompt -h grml
2050
2051 function prompt_grml_help () {
2052     <<__EOF0__
2053   prompt grml
2054
2055     This is the prompt as used by the grml-live system <http://grml.org>. It is
2056     a rather simple one-line prompt, that by default looks something like this:
2057
2058         <user>@<host> <current-working-directory>[ <vcs_info-data>]%
2059
2060     The prompt itself integrates with zsh's prompt themes system (as you are
2061     witnessing right now) and is configurable to a certain degree. In
2062     particular, these aspects are customisable:
2063
2064         - The items used in the prompt (e.g. you can remove \`user' from
2065           the list of activated items, which will cause the user name to
2066           be omitted from the prompt string).
2067
2068         - The attributes used with the items are customisable via strings
2069           used before and after the actual item.
2070
2071     The available items are: at, battery, change-root, date, grml-chroot,
2072     history, host, jobs, newline, path, percent, rc, rc-always, sad-smiley,
2073     shell-level, time, user, vcs
2074
2075     The actual configuration is done via zsh's \`zstyle' mechanism. The
2076     context, that is used while looking up styles is:
2077
2078         ':prompt:grml:<left-or-right>:<subcontext>'
2079
2080     Here <left-or-right> is either \`left' or \`right', signifying whether the
2081     style should affect the left or the right prompt. <subcontext> is either
2082     \`setup' or 'items:<item>', where \`<item>' is one of the available items.
2083
2084     The styles:
2085
2086         - use-rprompt (boolean): If \`true' (the default), print a sad smiley
2087           in $RPROMPT if the last command a returned non-successful error code.
2088           (This in only valid if <left-or-right> is "right"; ignored otherwise)
2089
2090         - items (list): The list of items used in the prompt. If \`vcs' is
2091           present in the list, the theme's code invokes \`vcs_info'
2092           accordingly. Default (left): rc change-root user at host path vcs
2093           percent; Default (right): sad-smiley
2094
2095         - strip-sensitive-characters (boolean): If the \`prompt_subst' option
2096           is active in zsh, the shell performs lots of expansions on prompt
2097           variable strings, including command substitution. So if you don't
2098           control where some of your prompt strings is coming from, this is
2099           an exploitable weakness. Grml's zsh setup does not set this option
2100           and it is off in the shell in zsh-mode by default. If it *is* turned
2101           on however, this style becomes active, and there are two flavours of
2102           it: On per default is a global variant in the '*:setup' context. This
2103           strips characters after the whole prompt string was constructed. There
2104           is a second variant in the '*:items:<item>', that is off by default.
2105           It allows fine grained control over which items' data is stripped.
2106           The characters that are stripped are: \$ and \`.
2107
2108     Available styles in 'items:<item>' are: pre, post. These are strings that
2109     are inserted before (pre) and after (post) the item in question. Thus, the
2110     following would cause the user name to be printed in red instead of the
2111     default blue:
2112
2113         zstyle ':prompt:grml:*:items:user' pre '%F{red}'
2114
2115     Note, that the \`post' style may remain at its default value, because its
2116     default value is '%f', which turns the foreground text attribute off (which
2117     is exactly, what is still required with the new \`pre' value).
2118 __EOF0__
2119 }
2120
2121 function prompt_grml-chroot_help () {
2122     <<__EOF0__
2123   prompt grml-chroot
2124
2125     This is a variation of the grml prompt, see: prompt -h grml
2126
2127     The main difference is the default value of the \`items' style. The rest
2128     behaves exactly the same. Here are the defaults for \`grml-chroot':
2129
2130         - left: grml-chroot user at host path percent
2131         - right: (empty list)
2132 __EOF0__
2133 }
2134
2135 function prompt_grml-large_help () {
2136     <<__EOF0__
2137   prompt grml-large
2138
2139     This is a variation of the grml prompt, see: prompt -h grml
2140
2141     The main difference is the default value of the \`items' style. In
2142     particular, this theme uses _two_ lines instead of one with the plain
2143     \`grml' theme. The rest behaves exactly the same. Here are the defaults
2144     for \`grml-large':
2145
2146         - left: rc jobs history shell-level change-root time date newline user
2147                 at host path vcs percent
2148         - right: sad-smiley
2149 __EOF0__
2150 }
2151
2152 function grml_prompt_setup () {
2153     emulate -L zsh
2154     autoload -Uz vcs_info
2155     # The following autoload is disabled for now, since this setup includes a
2156     # static version of the â€˜add-zsh-hook’ function above. It needs to be
2157     # re-enabled as soon as that static definition is removed again.
2158     #autoload -Uz add-zsh-hook
2159     add-zsh-hook precmd prompt_$1_precmd
2160 }
2161
2162 function prompt_grml_setup () {
2163     grml_prompt_setup grml
2164 }
2165
2166 function prompt_grml-chroot_setup () {
2167     grml_prompt_setup grml-chroot
2168 }
2169
2170 function prompt_grml-large_setup () {
2171     grml_prompt_setup grml-large
2172 }
2173
2174 # These maps define default tokens and pre-/post-decoration for items to be
2175 # used within the themes. All defaults may be customised in a context sensitive
2176 # matter by using zsh's `zstyle' mechanism.
2177 typeset -gA grml_prompt_pre_default \
2178             grml_prompt_post_default \
2179             grml_prompt_token_default \
2180             grml_prompt_token_function
2181
2182 grml_prompt_pre_default=(
2183     at                ''
2184     battery           ' '
2185     change-root       ''
2186     date              '%F{blue}'
2187     grml-chroot       '%F{red}'
2188     history           '%F{green}'
2189     host              ''
2190     jobs              '%F{cyan}'
2191     newline           ''
2192     path              '%B'
2193     percent           ''
2194     rc                '%B%F{red}'
2195     rc-always         ''
2196     sad-smiley        ''
2197     shell-level       '%F{red}'
2198     time              '%F{blue}'
2199     user              '%B%F{blue}'
2200     vcs               ''
2201 )
2202
2203 grml_prompt_post_default=(
2204     at                ''
2205     battery           ''
2206     change-root       ''
2207     date              '%f'
2208     grml-chroot       '%f '
2209     history           '%f'
2210     host              ''
2211     jobs              '%f'
2212     newline           ''
2213     path              '%b'
2214     percent           ''
2215     rc                '%f%b'
2216     rc-always         ''
2217     sad-smiley        ''
2218     shell-level       '%f'
2219     time              '%f'
2220     user              '%f%b'
2221     vcs               ''
2222 )
2223
2224 grml_prompt_token_default=(
2225     at                '@'
2226     battery           'GRML_BATTERY_LEVEL'
2227     change-root       'debian_chroot'
2228     date              '%D{%Y-%m-%d}'
2229     grml-chroot       'GRML_CHROOT'
2230     history           '{history#%!} '
2231     host              '%m '
2232     jobs              '[%j running job(s)] '
2233     newline           $'\n'
2234     path              '%40<..<%~%<< '
2235     percent           '%# '
2236     rc                '%(?..%? )'
2237     rc-always         '%?'
2238     sad-smiley        '%(?..:()'
2239     shell-level       '%(3L.+ .)'
2240     time              '%D{%H:%M:%S} '
2241     user              '%n'
2242     vcs               '0'
2243 )
2244
2245 function grml_theme_has_token () {
2246     if (( ARGC != 1 )); then
2247         printf 'usage: grml_theme_has_token <name>\n'
2248         return 1
2249     fi
2250     (( ${+grml_prompt_token_default[$1]} ))
2251 }
2252
2253 function GRML_theme_add_token_usage () {
2254     <<__EOF0__
2255   Usage: grml_theme_add_token <name> [-f|-i] <token/function> [<pre> <post>]
2256
2257     <name> is the name for the newly added token. If the \`-f' or \`-i' options
2258     are used, <token/function> is the name of the function (see below for
2259     details). Otherwise it is the literal token string to be used. <pre> and
2260     <post> are optional.
2261
2262   Options:
2263
2264     -f <function>   Use a function named \`<function>' each time the token
2265                     is to be expanded.
2266
2267     -i <function>   Use a function named \`<function>' to initialise the
2268                     value of the token _once_ at runtime.
2269
2270     The functions are called with one argument: the token's new name. The
2271     return value is expected in the \$REPLY parameter. The use of these
2272     options is mutually exclusive.
2273
2274     There is a utility function \`grml_theme_has_token', which you can use
2275     to test if a token exists before trying to add it. This can be a guard
2276     for situations in which a \`grml_theme_add_token' call may happen more
2277     than once.
2278
2279   Example:
2280
2281     To add a new token \`day' that expands to the current weekday in the
2282     current locale in green foreground colour, use this:
2283
2284       grml_theme_add_token day '%D{%A}' '%F{green}' '%f'
2285
2286     Another example would be support for \$VIRTUAL_ENV:
2287
2288       function virtual_env_prompt () {
2289         REPLY=\${VIRTUAL_ENV+\${VIRTUAL_ENV:t} }
2290       }
2291       grml_theme_add_token virtual-env -f virtual_env_prompt
2292
2293     After that, you will be able to use a changed \`items' style to
2294     assemble your prompt.
2295 __EOF0__
2296 }
2297
2298 function grml_theme_add_token () {
2299     emulate -L zsh
2300     local name token pre post
2301     local -i init funcall
2302
2303     if (( ARGC == 0 )); then
2304         GRML_theme_add_token_usage
2305         return 0
2306     fi
2307
2308     init=0
2309     funcall=0
2310     pre=''
2311     post=''
2312     name=$1
2313     shift
2314     if [[ $1 == '-f' ]]; then
2315         funcall=1
2316         shift
2317     elif [[ $1 == '-i' ]]; then
2318         init=1
2319         shift
2320     fi
2321
2322     if (( ARGC == 0 )); then
2323         printf '
2324 grml_theme_add_token: No token-string/function-name provided!\n\n'
2325         GRML_theme_add_token_usage
2326         return 1
2327     fi
2328     token=$1
2329     shift
2330     if (( ARGC != 0 && ARGC != 2 )); then
2331         printf '
2332 grml_theme_add_token: <pre> and <post> need to by specified _both_!\n\n'
2333         GRML_theme_add_token_usage
2334         return 1
2335     fi
2336     if (( ARGC )); then
2337         pre=$1
2338         post=$2
2339         shift 2
2340     fi
2341
2342     if grml_theme_has_token $name; then
2343         printf '
2344 grml_theme_add_token: Token `%s'\'' exists! Giving up!\n\n' $name
2345         GRML_theme_add_token_usage
2346         return 2
2347     fi
2348     if (( init )); then
2349         REPLY=''
2350         $token $name
2351         token=$REPLY
2352     fi
2353     grml_prompt_pre_default[$name]=$pre
2354     grml_prompt_post_default[$name]=$post
2355     if (( funcall )); then
2356         grml_prompt_token_function[$name]=$token
2357         grml_prompt_token_default[$name]=23
2358     else
2359         grml_prompt_token_default[$name]=$token
2360     fi
2361 }
2362
2363 function grml_wrap_reply () {
2364     emulate -L zsh
2365     local target="$1"
2366     local new="$2"
2367     local left="$3"
2368     local right="$4"
2369
2370     if (( ${+parameters[$new]} )); then
2371         REPLY="${left}${(P)new}${right}"
2372     else
2373         REPLY=''
2374     fi
2375 }
2376
2377 function grml_prompt_addto () {
2378     emulate -L zsh
2379     local target="$1"
2380     local lr it apre apost new v REPLY
2381     local -a items
2382     shift
2383
2384     [[ $target == PS1 ]] && lr=left || lr=right
2385     zstyle -a ":prompt:${grmltheme}:${lr}:setup" items items || items=( "$@" )
2386     typeset -g "${target}="
2387     for it in "${items[@]}"; do
2388         zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" pre apre \
2389             || apre=${grml_prompt_pre_default[$it]}
2390         zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" post apost \
2391             || apost=${grml_prompt_post_default[$it]}
2392         zstyle -s ":prompt:${grmltheme}:${lr}:items:$it" token new \
2393             || new=${grml_prompt_token_default[$it]}
2394         if (( ${+grml_prompt_token_function[$it]} )); then
2395             REPLY=''
2396             ${grml_prompt_token_function[$it]} $it
2397         else
2398             case $it in
2399             battery)
2400                 grml_wrap_reply $target $new '' ''
2401                 ;;
2402             change-root)
2403                 grml_wrap_reply $target $new '(' ')'
2404                 ;;
2405             grml-chroot)
2406                 if [[ -n ${(P)new} ]]; then
2407                     REPLY="$CHROOT"
2408                 else
2409                     REPLY=''
2410                 fi
2411                 ;;
2412             vcs)
2413                 v="vcs_info_msg_${new}_"
2414                 if (( ! vcscalled )); then
2415                     vcs_info
2416                     vcscalled=1
2417                 fi
2418                 if (( ${+parameters[$v]} )) && [[ -n "${(P)v}" ]]; then
2419                     REPLY="${(P)v}"
2420                 else
2421                     REPLY=''
2422                 fi
2423                 ;;
2424             *) REPLY="$new" ;;
2425             esac
2426         fi
2427         # Strip volatile characters per item. This is off by default. See the
2428         # global stripping code a few lines below for details.
2429         if [[ -o prompt_subst ]] && zstyle -t ":prompt:${grmltheme}:${lr}:items:$it" \
2430                                            strip-sensitive-characters
2431         then
2432             REPLY="${REPLY//[$\`]/}"
2433         fi
2434         typeset -g "${target}=${(P)target}${apre}${REPLY}${apost}"
2435     done
2436
2437     # Per default, strip volatile characters (in the prompt_subst case)
2438     # globally. If the option is off, the style has no effect. For more
2439     # control, this can be turned off and stripping can be configured on a
2440     # per-item basis (see above).
2441     if [[ -o prompt_subst ]] && zstyle -T ":prompt:${grmltheme}:${lr}:setup" \
2442                                        strip-sensitive-characters
2443     then
2444         typeset -g "${target}=${${(P)target}//[$\`]/}"
2445     fi
2446 }
2447
2448 function prompt_grml_precmd () {
2449     emulate -L zsh
2450     local grmltheme=grml
2451     local -a left_items right_items
2452     left_items=(rc change-root user at host path vcs percent)
2453     right_items=(sad-smiley)
2454
2455     prompt_grml_precmd_worker
2456 }
2457
2458 function prompt_grml-chroot_precmd () {
2459     emulate -L zsh
2460     local grmltheme=grml-chroot
2461     local -a left_items right_items
2462     left_items=(grml-chroot user at host path percent)
2463     right_items=()
2464
2465     prompt_grml_precmd_worker
2466 }
2467
2468 function prompt_grml-large_precmd () {
2469     emulate -L zsh
2470     local grmltheme=grml-large
2471     local -a left_items right_items
2472     left_items=(rc jobs history shell-level change-root time date newline
2473                 user at host path vcs percent)
2474     right_items=(sad-smiley)
2475
2476     prompt_grml_precmd_worker
2477 }
2478
2479 function prompt_grml_precmd_worker () {
2480     emulate -L zsh
2481     local -i vcscalled=0
2482
2483     grml_prompt_addto PS1 "${left_items[@]}"
2484     if zstyle -T ":prompt:${grmltheme}:right:setup" use-rprompt; then
2485         grml_prompt_addto RPS1 "${right_items[@]}"
2486     fi
2487 }
2488
2489 function grml_prompt_fallback () {
2490     setopt prompt_subst
2491     local p0 p1
2492
2493     p0="${RED}%(?..%? )${WHITE}${debian_chroot:+($debian_chroot)}"
2494     p1="${BLUE}%n${NO_COLOR}@%m %40<...<%B%~%b%<< "'${vcs_info_msg_0_}'"%# "
2495     if (( EUID == 0 )); then
2496         PROMPT="${BLUE}${p0}${RED}${p1}"
2497     else
2498         PROMPT="${RED}${p0}${BLUE}${p1}"
2499     fi
2500 }
2501
2502 if zrcautoload promptinit && promptinit 2>/dev/null ; then
2503     grml_status_feature promptinit 0
2504     # Since we define the required functions in here and not in files in
2505     # $fpath, we need to stick the theme's name into `$prompt_themes'
2506     # ourselves, since promptinit does not pick them up otherwise.
2507     prompt_themes+=( grml grml-chroot grml-large )
2508     # Also, keep the array sorted...
2509     prompt_themes=( "${(@on)prompt_themes}" )
2510 else
2511     grml_status_feature promptinit 1
2512     grml_prompt_fallback
2513     function precmd () { (( ${+functions[vcs_info]} )) && vcs_info; }
2514 fi
2515
2516 if is437; then
2517     # The prompt themes use modern features of zsh, that require at least
2518     # version 4.3.7 of the shell. Use the fallback otherwise.
2519     if [[ $GRML_DISPLAY_BATTERY -gt 0 ]]; then
2520         zstyle ':prompt:grml:right:setup' items sad-smiley battery
2521         add-zsh-hook precmd battery
2522     fi
2523     if [[ "$TERM" == dumb ]] ; then
2524         zstyle ":prompt:grml(|-large|-chroot):*:items:grml-chroot" pre ''
2525         zstyle ":prompt:grml(|-large|-chroot):*:items:grml-chroot" post ' '
2526         for i in rc user path jobs history date time shell-level; do
2527             zstyle ":prompt:grml(|-large|-chroot):*:items:$i" pre ''
2528             zstyle ":prompt:grml(|-large|-chroot):*:items:$i" post ''
2529         done
2530         unset i
2531         zstyle ':prompt:grml(|-large|-chroot):right:setup' use-rprompt false
2532     elif (( EUID == 0 )); then
2533         zstyle ':prompt:grml(|-large|-chroot):*:items:user' pre '%B%F{red}'
2534     fi
2535
2536     # Finally enable one of the prompts.
2537     if [[ -n $GRML_CHROOT ]]; then
2538         prompt grml-chroot
2539     elif [[ $GRMLPROMPT -gt 0 ]]; then
2540         prompt grml-large
2541     else
2542         prompt grml
2543     fi
2544 else
2545     grml_prompt_fallback
2546     function precmd () { (( ${+functions[vcs_info]} )) && vcs_info; }
2547 fi
2548
2549 # make sure to use right prompt only when not running a command
2550 is41 && setopt transient_rprompt
2551
2552 # Terminal-title wizardry
2553
2554 function ESC_print () {
2555     info_print $'\ek' $'\e\\' "$@"
2556 }
2557 function set_title () {
2558     info_print  $'\e]0;' $'\a' "$@"
2559 }
2560
2561 function info_print () {
2562     local esc_begin esc_end
2563     esc_begin="$1"
2564     esc_end="$2"
2565     shift 2
2566     printf '%s' ${esc_begin}
2567     printf '%s' "$*"
2568     printf '%s' "${esc_end}"
2569 }
2570
2571 function grml_reset_screen_title () {
2572     # adjust title of xterm
2573     # see http://www.faqs.org/docs/Linux-mini/Xterm-Title.html
2574     [[ ${NOTITLE:-} -gt 0 ]] && return 0
2575     case $TERM in
2576         (xterm*|rxvt*|alacritty|foot)
2577             set_title ${(%):-"%n@%m: %~"}
2578             ;;
2579     esac
2580 }
2581
2582 function grml_vcs_to_screen_title () {
2583     if [[ $TERM == screen* ]] ; then
2584         if [[ -n ${vcs_info_msg_1_} ]] ; then
2585             ESC_print ${vcs_info_msg_1_}
2586         else
2587             ESC_print "zsh"
2588         fi
2589     fi
2590 }
2591
2592 function grml_maintain_name () {
2593     local localname
2594     localname="$(uname -n)"
2595
2596     # set hostname if not running on local machine
2597     if [[ -n "$HOSTNAME" ]] && [[ "$HOSTNAME" != "${localname}" ]] ; then
2598        NAME="@$HOSTNAME"
2599     fi
2600 }
2601
2602 function grml_cmd_to_screen_title () {
2603     # get the name of the program currently running and hostname of local
2604     # machine set screen window title if running in a screen
2605     if [[ "$TERM" == screen* ]] ; then
2606         local CMD="${1[(wr)^(*=*|sudo|ssh|-*)]}$NAME"
2607         ESC_print ${CMD}
2608     fi
2609 }
2610
2611 function grml_control_xterm_title () {
2612     case $TERM in
2613         (xterm*|rxvt*|alacritty|foot)
2614             set_title "${(%):-"%n@%m:"}" "$2"
2615             ;;
2616     esac
2617 }
2618
2619 # The following autoload is disabled for now, since this setup includes a
2620 # static version of the â€˜add-zsh-hook’ function above. It needs to be
2621 # re-enabled as soon as that static definition is removed again.
2622 #zrcautoload add-zsh-hook || add-zsh-hook () { :; }
2623 if [[ $NOPRECMD -eq 0 ]]; then
2624     add-zsh-hook precmd grml_reset_screen_title
2625     add-zsh-hook precmd grml_vcs_to_screen_title
2626     add-zsh-hook preexec grml_maintain_name
2627     add-zsh-hook preexec grml_cmd_to_screen_title
2628     if [[ $NOTITLE -eq 0 ]]; then
2629         add-zsh-hook preexec grml_control_xterm_title
2630     fi
2631 fi
2632
2633 # 'hash' some often used directories
2634 #d# start
2635 hash -d deb=/var/cache/apt/archives
2636 hash -d doc=/usr/share/doc
2637 hash -d linux=/lib/modules/$(command uname -r)/build/
2638 hash -d log=/var/log
2639 hash -d slog=/var/log/syslog
2640 hash -d src=/usr/src
2641 hash -d www=/var/www
2642 #d# end
2643
2644 # some aliases
2645 if check_com -c screen ; then
2646     if [[ $UID -eq 0 ]] ; then
2647         if [[ -r /etc/grml/screenrc ]]; then
2648             alias screen='screen -c /etc/grml/screenrc'
2649         fi
2650     elif [[ ! -r $HOME/.screenrc ]] ; then
2651         if [[ -r /etc/grml/screenrc_grml ]]; then
2652             alias screen='screen -c /etc/grml/screenrc_grml'
2653         else
2654             if [[ -r /etc/grml/screenrc ]]; then
2655                 alias screen='screen -c /etc/grml/screenrc'
2656             fi
2657         fi
2658     fi
2659 fi
2660
2661 # do we have GNU ls with color-support?
2662 if [[ "$TERM" != dumb ]]; then
2663     #a1# List files with colors (\kbd{ls \ldots})
2664     alias ls="command ls ${ls_options:+${ls_options[*]}}"
2665     #a1# List all files, with colors (\kbd{ls -la \ldots})
2666     alias la="command ls -la ${ls_options:+${ls_options[*]}}"
2667     #a1# List files with long colored list, without dotfiles (\kbd{ls -l \ldots})
2668     alias ll="command ls -l ${ls_options:+${ls_options[*]}}"
2669     #a1# List files with long colored list, human readable sizes (\kbd{ls -hAl \ldots})
2670     alias lh="command ls -hAl ${ls_options:+${ls_options[*]}}"
2671     #a1# List files with long colored list, append qualifier to filenames (\kbd{ls -l \ldots})\\&\quad(\kbd{/} for directories, \kbd{@} for symlinks ...)
2672     alias l="command ls -l ${ls_options:+${ls_options[*]}}"
2673 else
2674     alias la='command ls -la'
2675     alias ll='command ls -l'
2676     alias lh='command ls -hAl'
2677     alias l='command ls -l'
2678 fi
2679
2680 # use ip from iproute2 with color support
2681 if ip --color=auto addr show dev lo >/dev/null 2>&1; then
2682     alias ip='command ip --color=auto'
2683 fi
2684
2685 if [[ -r /proc/mdstat ]]; then
2686     alias mdstat='cat /proc/mdstat'
2687 fi
2688
2689 alias ...='cd ../../'
2690
2691 # generate alias named "$KERNELVERSION-reboot" so you can use boot with kexec:
2692 if [[ -x /sbin/kexec ]] && [[ -r /proc/cmdline ]] ; then
2693     alias "$(uname -r)-reboot"="kexec -l --initrd=/boot/initrd.img-"$(uname -r)" --command-line=\"$(cat /proc/cmdline)\" /boot/vmlinuz-"$(uname -r)""
2694 fi
2695
2696 # see http://www.cl.cam.ac.uk/~mgk25/unicode.html#term for details
2697 alias term2iso="echo 'Setting terminal to iso mode' ; print -n '\e%@'"
2698 alias term2utf="echo 'Setting terminal to utf-8 mode'; print -n '\e%G'"
2699
2700 # make sure it is not assigned yet
2701 [[ -n ${aliases[utf2iso]} ]] && unalias utf2iso
2702 function utf2iso () {
2703     if isutfenv ; then
2704         local ENV
2705         for ENV in $(env | command grep -i '.utf') ; do
2706             eval export "$(echo $ENV | sed 's/UTF-8/iso885915/ ; s/utf8/iso885915/')"
2707         done
2708     fi
2709 }
2710
2711 # make sure it is not assigned yet
2712 [[ -n ${aliases[iso2utf]} ]] && unalias iso2utf
2713 function iso2utf () {
2714     if ! isutfenv ; then
2715         local ENV
2716         for ENV in $(env | command grep -i '\.iso') ; do
2717             eval export "$(echo $ENV | sed 's/iso.*/UTF-8/ ; s/ISO.*/UTF-8/')"
2718         done
2719     fi
2720 }
2721
2722 # especially for roadwarriors using GNU screen and ssh:
2723 if ! check_com asc &>/dev/null ; then
2724   function asc () { autossh -t "$@" 'screen -RdU' }
2725   compdef asc=ssh
2726 fi
2727
2728 #f1# Hints for the use of zsh on grml
2729 function zsh-help () {
2730     print "$bg[white]$fg[black]
2731 zsh-help - hints for use of zsh on grml
2732 =======================================$reset_color"
2733
2734     print '
2735 Main configuration of zsh happens in /etc/zsh/zshrc.
2736 That file is part of the package grml-etc-core, if you want to
2737 use them on a non-grml-system just get the tar.gz from
2738 http://deb.grml.org/ or (preferably) get it from the git repository:
2739
2740   http://git.grml.org/f/grml-etc-core/etc/zsh/zshrc
2741
2742 This version of grml'\''s zsh setup does not use skel/.zshrc anymore.
2743 The file is still there, but it is empty for backwards compatibility.
2744
2745 For your own changes use these two files:
2746     $HOME/.zshrc.pre
2747     $HOME/.zshrc.local
2748
2749 The former is sourced very early in our zshrc, the latter is sourced
2750 very lately.
2751
2752 System wide configuration without touching configuration files of grml
2753 can take place in /etc/zsh/zshrc.local.
2754
2755 For information regarding zsh start at http://grml.org/zsh/
2756
2757 Take a look at grml'\''s zsh refcard:
2758 % xpdf =(zcat /usr/share/doc/grml-docs/zsh/grml-zsh-refcard.pdf.gz)
2759
2760 Check out the main zsh refcard:
2761 % '$BROWSER' http://www.bash2zsh.com/zsh_refcard/refcard.pdf
2762
2763 And of course visit the zsh-lovers:
2764 % man zsh-lovers
2765
2766 You can adjust some options through environment variables when
2767 invoking zsh without having to edit configuration files.
2768 Basically meant for bash users who are not used to the power of
2769 the zsh yet. :)
2770
2771   "NOCOR=1    zsh" => deactivate automatic correction
2772   "NOMENU=1   zsh" => do not use auto menu completion
2773                       (note: use ctrl-d for completion instead!)
2774   "NOPRECMD=1 zsh" => disable the precmd + preexec commands (set GNU screen title)
2775   "NOTITLE=1  zsh" => disable setting the title of xterms without disabling
2776                       preexec() and precmd() completely
2777   "GRML_DISPLAY_BATTERY=1  zsh"
2778                    => activate battery status on right side of prompt (WIP)
2779   "COMMAND_NOT_FOUND=1 zsh"
2780                    => Enable a handler if an external command was not found
2781                       The command called in the handler can be altered by setting
2782                       the GRML_ZSH_CNF_HANDLER variable, the default is:
2783                       "/usr/share/command-not-found/command-not-found"
2784
2785 A value greater than 0 is enables a feature; a value equal to zero
2786 disables it. If you like one or the other of these settings, you can
2787 add them to ~/.zshrc.pre to ensure they are set when sourcing grml'\''s
2788 zshrc.'
2789
2790     print "
2791 $bg[white]$fg[black]
2792 Please report wishes + bugs to the grml-team: http://grml.org/bugs/
2793 Enjoy your grml system with the zsh!$reset_color"
2794 }
2795
2796 # debian stuff
2797 if [[ -r /etc/debian_version ]] ; then
2798     if [[ -z "$GRML_NO_APT_ALIASES" ]]; then
2799         #a3# Execute \kbd{apt-cache policy}
2800         alias acp='apt-cache policy'
2801         if check_com -c apt ; then
2802           #a3# Execute \kbd{apt search}
2803           alias acs='apt search'
2804           #a3# Execute \kbd{apt show}
2805           alias acsh='apt show'
2806           #a3# Execute \kbd{apt dist-upgrade}
2807           salias adg="apt dist-upgrade"
2808           #a3# Execute \kbd{apt upgrade}
2809           salias ag="apt upgrade"
2810           #a3# Execute \kbd{apt install}
2811           salias agi="apt install"
2812           #a3# Execute \kbd{apt update}
2813           salias au="apt update"
2814         else
2815           alias acs='apt-cache search'
2816           alias acsh='apt-cache show'
2817           salias adg="apt-get dist-upgrade"
2818           salias ag="apt-get upgrade"
2819           salias agi="apt-get install"
2820           salias au="apt-get update"
2821         fi
2822         #a3# Execute \kbd{aptitude install}
2823         salias ati="aptitude install"
2824         #a3# Execute \kbd{aptitude update ; aptitude safe-upgrade}
2825         salias -a up="aptitude update ; aptitude safe-upgrade"
2826         #a3# Execute \kbd{dpkg-buildpackage}
2827         alias dbp='dpkg-buildpackage'
2828         #a3# Execute \kbd{grep-excuses}
2829         alias ge='grep-excuses'
2830     fi
2831
2832     # get a root shell as normal user in live-cd mode:
2833     if isgrmlcd && [[ $UID -ne 0 ]] ; then
2834        alias su="sudo su"
2835     fi
2836
2837 fi
2838
2839 # use /var/log/syslog iff present, fallback to journalctl otherwise
2840 if [ -e /var/log/syslog ] ; then
2841   #a1# Take a look at the syslog: \kbd{\$PAGER /var/log/syslog || journalctl}
2842   salias llog="$PAGER /var/log/syslog"     # take a look at the syslog
2843   #a1# Take a look at the syslog: \kbd{tail -f /var/log/syslog || journalctl}
2844   salias tlog="tail -f /var/log/syslog"    # follow the syslog
2845 elif check_com -c journalctl ; then
2846   salias llog="journalctl"
2847   salias tlog="journalctl -f"
2848 fi
2849
2850 # sort installed Debian-packages by size
2851 if check_com -c dpkg-query ; then
2852     #a3# List installed Debian-packages sorted by size
2853     alias debs-by-size="dpkg-query -Wf 'x \${Installed-Size} \${Package} \${Status}\n' | sed -ne '/^x  /d' -e '/^x \(.*\) install ok installed$/s//\1/p' | sort -nr"
2854 fi
2855
2856 # if cdrecord is a symlink (to wodim) or isn't present at all warn:
2857 if [[ -L /usr/bin/cdrecord ]] || ! check_com -c cdrecord; then
2858     if check_com -c wodim; then
2859         function cdrecord () {
2860             <<__EOF0__
2861 cdrecord is not provided under its original name by Debian anymore.
2862 See #377109 in the BTS of Debian for more details.
2863
2864 Please use the wodim binary instead
2865 __EOF0__
2866             return 1
2867         }
2868     fi
2869 fi
2870
2871 if isgrmlcd; then
2872     # No core dumps: important for a live-cd-system
2873     limit -s core 0
2874 fi
2875
2876 # grmlstuff
2877 function grmlstuff () {
2878 # people should use 'grml-x'!
2879     if check_com -c 915resolution; then
2880         function 855resolution () {
2881             echo "Please use 915resolution as resolution modifying tool for Intel \
2882 graphic chipset."
2883             return -1
2884         }
2885     fi
2886
2887     #a1# Output version of running grml
2888     alias grml-version='cat /etc/grml_version'
2889
2890     if check_com -c grml-debootstrap ; then
2891         function debian2hd () {
2892             echo "Installing debian to harddisk is possible by using grml-debootstrap."
2893             return 1
2894         }
2895     fi
2896
2897     if check_com -c tmate && check_com -c qrencode ; then
2898         function grml-remote-support() {
2899             tmate -L grml-remote-support new -s grml-remote-support -d
2900             tmate -L grml-remote-support wait tmate-ready
2901             tmate -L grml-remote-support display -p '#{tmate_ssh}' | qrencode -t ANSI
2902             echo "tmate session: $(tmate -L grml-remote-support display -p '#{tmate_ssh}')"
2903             echo
2904             echo "Scan this QR code and send it to your support team."
2905         }
2906     fi
2907 }
2908
2909 # now run the functions
2910 isgrml && checkhome
2911 is4    && isgrml    && grmlstuff
2912 is4    && grmlcomp
2913
2914 # keephack
2915 is4 && xsource "/etc/zsh/keephack"
2916
2917 # wonderful idea of using "e" glob qualifier by Peter Stephenson
2918 # You use it as follows:
2919 # $ NTREF=/reference/file
2920 # $ ls -l *(e:nt:)
2921 # This lists all the files in the current directory newer than the reference file.
2922 # You can also specify the reference file inline; note quotes:
2923 # $ ls -l *(e:'nt ~/.zshenv':)
2924 is4 && function nt () {
2925     if [[ -n $1 ]] ; then
2926         local NTREF=${~1}
2927     fi
2928     [[ $REPLY -nt $NTREF ]]
2929 }
2930
2931 # shell functions
2932
2933 #f1# Reload an autoloadable function
2934 function freload () { while (( $# )); do; unfunction $1; autoload -U $1; shift; done }
2935 compdef _functions freload
2936
2937 #
2938 # Usage:
2939 #
2940 #      e.g.:   a -> b -> c -> d  ....
2941 #
2942 #      sll a
2943 #
2944 #
2945 #      if parameter is given with leading '=', lookup $PATH for parameter and resolve that
2946 #
2947 #      sll =java
2948 #
2949 #      Note: limit for recursive symlinks on linux:
2950 #            http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/namei.c?id=refs/heads/master#l808
2951 #            This limits recursive symlink follows to 8,
2952 #            while limiting consecutive symlinks to 40.
2953 #
2954 #      When resolving and displaying information about symlinks, no check is made
2955 #      that the displayed information does make any sense on your OS.
2956 #      We leave that decission to the user.
2957 #
2958 #      The zstat module is used to detect symlink loops. zstat is available since zsh4.
2959 #      With an older zsh you will need to abort with <C-c> in that case.
2960 #      When a symlink loop is detected, a warning ist printed and further processing is stopped.
2961 #
2962 #      Module zstat is loaded by default in grml zshrc, no extra action needed for that.
2963 #
2964 #      Known bugs:
2965 #      If you happen to come across a symlink that points to a destination on another partition
2966 #      with the same inode number, that will be marked as symlink loop though it is not.
2967 #      Two hints for this situation:
2968 #      I)  Play lottery the same day, as you seem to be rather lucky right now.
2969 #      II) Send patches.
2970 #
2971 #      return status:
2972 #      0 upon success
2973 #      1 file/dir not accesible
2974 #      2 symlink loop detected
2975 #
2976 #f1# List symlinks in detail (more detailed version of 'readlink -f', 'whence -s' and 'namei -l')
2977 function sll () {
2978     if [[ -z ${1} ]] ; then
2979         printf 'Usage: %s <symlink(s)>\n' "${0}"
2980         return 1
2981     fi
2982
2983     local file jumpd curdir
2984     local -i 10 RTN LINODE i
2985     local -a    SEENINODES
2986     curdir="${PWD}"
2987     RTN=0
2988
2989     for file in "${@}" ; do
2990         SEENINODES=()
2991         ls -l "${file:a}"   || RTN=1
2992
2993         while [[ -h "$file" ]] ; do
2994             if is4 ; then
2995                 LINODE=$(zstat -L +inode "${file}")
2996                 for i in ${SEENINODES} ; do
2997                     if (( ${i} == ${LINODE} )) ; then
2998                         builtin cd -q "${curdir}"
2999                         print 'link loop detected, aborting!'
3000                         return 2
3001                     fi
3002                 done
3003                 SEENINODES+=${LINODE}
3004             fi
3005             jumpd="${file:h}"
3006             file="${file:t}"
3007
3008             if [[ -d ${jumpd} ]] ; then
3009                 builtin cd -q "${jumpd}"  || RTN=1
3010             fi
3011             file=$(readlink "$file")
3012
3013             jumpd="${file:h}"
3014             file="${file:t}"
3015
3016             if [[ -d ${jumpd} ]] ; then
3017                 builtin cd -q "${jumpd}"  || RTN=1
3018             fi
3019
3020             ls -l "${PWD}/${file}"     || RTN=1
3021         done
3022         shift 1
3023         if (( ${#} >= 1 )) ; then
3024             print ""
3025         fi
3026         builtin cd -q "${curdir}"
3027     done
3028     return ${RTN}
3029 }
3030
3031 if check_com -c $PAGER ; then
3032     #f3# View Debian's changelog of given package(s)
3033     function dchange () {
3034         emulate -L zsh
3035         [[ -z "$1" ]] && printf 'Usage: %s <package_name(s)>\n' "$0" && return 1
3036
3037         local package
3038
3039         # `less` as $PAGER without e.g. `|lesspipe %s` inside $LESSOPEN can't properly
3040         # read *.gz files, try to detect this to use vi instead iff available
3041         local viewer
3042
3043         if [[ ${$(typeset -p PAGER)[2]} = -a ]] ; then
3044           viewer=($PAGER)    # support PAGER=(less -Mr) but leave array untouched
3045         else
3046           viewer=(${=PAGER}) # support PAGER='less -Mr'
3047         fi
3048
3049         if [[ ${viewer[1]:t} = less ]] && [[ -z "${LESSOPEN}" ]] && check_com vi ; then
3050           viewer='vi'
3051         fi
3052
3053         for package in "$@" ; do
3054             if [[ -r /usr/share/doc/${package}/changelog.Debian.gz ]] ; then
3055                 $viewer /usr/share/doc/${package}/changelog.Debian.gz
3056             elif [[ -r /usr/share/doc/${package}/changelog.gz ]] ; then
3057                 $viewer /usr/share/doc/${package}/changelog.gz
3058             elif [[ -r /usr/share/doc/${package}/changelog ]] ; then
3059                 $viewer /usr/share/doc/${package}/changelog
3060             else
3061                 if check_com -c aptitude ; then
3062                     echo "No changelog for package $package found, using aptitude to retrieve it."
3063                     aptitude changelog "$package"
3064                 elif check_com -c apt-get ; then
3065                     echo "No changelog for package $package found, using apt-get to retrieve it."
3066                     apt-get changelog "$package"
3067                 else
3068                     echo "No changelog for package $package found, sorry."
3069                 fi
3070             fi
3071         done
3072     }
3073     function _dchange () { _files -W /usr/share/doc -/ }
3074     compdef _dchange dchange
3075
3076     #f3# View Debian's NEWS of a given package
3077     function dnews () {
3078         emulate -L zsh
3079         if [[ -r /usr/share/doc/$1/NEWS.Debian.gz ]] ; then
3080             $PAGER /usr/share/doc/$1/NEWS.Debian.gz
3081         else
3082             if [[ -r /usr/share/doc/$1/NEWS.gz ]] ; then
3083                 $PAGER /usr/share/doc/$1/NEWS.gz
3084             else
3085                 echo "No NEWS file for package $1 found, sorry."
3086                 return 1
3087             fi
3088         fi
3089     }
3090     function _dnews () { _files -W /usr/share/doc -/ }
3091     compdef _dnews dnews
3092
3093     #f3# View Debian's copyright of a given package
3094     function dcopyright () {
3095         emulate -L zsh
3096         if [[ -r /usr/share/doc/$1/copyright ]] ; then
3097             $PAGER /usr/share/doc/$1/copyright
3098         else
3099             echo "No copyright file for package $1 found, sorry."
3100             return 1
3101         fi
3102     }
3103     function _dcopyright () { _files -W /usr/share/doc -/ }
3104     compdef _dcopyright dcopyright
3105
3106     #f3# View upstream's changelog of a given package
3107     function uchange () {
3108         emulate -L zsh
3109         if [[ -r /usr/share/doc/$1/changelog.gz ]] ; then
3110             $PAGER /usr/share/doc/$1/changelog.gz
3111         else
3112             echo "No changelog for package $1 found, sorry."
3113             return 1
3114         fi
3115     }
3116     function _uchange () { _files -W /usr/share/doc -/ }
3117     compdef _uchange uchange
3118 fi
3119
3120 # zsh profiling
3121 function profile () {
3122     ZSH_PROFILE_RC=1 zsh "$@"
3123 }
3124
3125 #f1# Edit an alias via zle
3126 function edalias () {
3127     [[ -z "$1" ]] && { echo "Usage: edalias <alias_to_edit>" ; return 1 } || vared aliases'[$1]' ;
3128 }
3129 compdef _aliases edalias
3130
3131 #f1# Edit a function via zle
3132 function edfunc () {
3133     [[ -z "$1" ]] && { echo "Usage: edfunc <function_to_edit>" ; return 1 } || zed -f "$1" ;
3134 }
3135 compdef _functions edfunc
3136
3137 # use it e.g. via 'Restart apache2'
3138 #m# f6 Start() \kbd{service \em{process}}\quad\kbd{start}
3139 #m# f6 Restart() \kbd{service \em{process}}\quad\kbd{restart}
3140 #m# f6 Stop() \kbd{service \em{process}}\quad\kbd{stop}
3141 #m# f6 Reload() \kbd{service \em{process}}\quad\kbd{reload}
3142 #m# f6 Force-Reload() \kbd{service \em{process}}\quad\kbd{force-reload}
3143 #m# f6 Status() \kbd{service \em{process}}\quad\kbd{status}
3144 if [[ -d /etc/init.d || -d /etc/service ]] ; then
3145     function __start_stop () {
3146         local action_="${1:l}"  # e.g Start/Stop/Restart
3147         local service_="$2"
3148         local param_="$3"
3149
3150         local service_target_="$(readlink /etc/init.d/$service_)"
3151         if [[ $service_target_ == "/usr/bin/sv" ]]; then
3152             # runit
3153             case "${action_}" in
3154                 start) if [[ ! -e /etc/service/$service_ ]]; then
3155                            $SUDO ln -s "/etc/sv/$service_" "/etc/service/"
3156                        else
3157                            $SUDO "/etc/init.d/$service_" "${action_}" "$param_"
3158                        fi ;;
3159                 # there is no reload in runits sysv emulation
3160                 reload) $SUDO "/etc/init.d/$service_" "force-reload" "$param_" ;;
3161                 *) $SUDO "/etc/init.d/$service_" "${action_}" "$param_" ;;
3162             esac
3163         else
3164             # sysv/sysvinit-utils, upstart
3165             if check_com -c service ; then
3166               $SUDO service "$service_" "${action_}" "$param_"
3167             else
3168               $SUDO "/etc/init.d/$service_" "${action_}" "$param_"
3169             fi
3170         fi
3171     }
3172
3173     function _grmlinitd () {
3174         local -a scripts
3175         scripts=( /etc/init.d/*(x:t) )
3176         _describe "service startup script" scripts
3177     }
3178
3179     for i in Start Restart Stop Force-Reload Reload Status ; do
3180         eval "function $i () { __start_stop $i \"\$1\" \"\$2\" ; }"
3181         compdef _grmlinitd $i
3182     done
3183     builtin unset -v i
3184 fi
3185
3186 #f1# Provides useful information on globbing
3187 function H-Glob () {
3188     echo -e "
3189     /      directories
3190     .      plain files
3191     @      symbolic links
3192     =      sockets
3193     p      named pipes (FIFOs)
3194     *      executable plain files (0100)
3195     %      device files (character or block special)
3196     %b     block special files
3197     %c     character special files
3198     r      owner-readable files (0400)
3199     w      owner-writable files (0200)
3200     x      owner-executable files (0100)
3201     A      group-readable files (0040)
3202     I      group-writable files (0020)
3203     E      group-executable files (0010)
3204     R      world-readable files (0004)
3205     W      world-writable files (0002)
3206     X      world-executable files (0001)
3207     s      setuid files (04000)
3208     S      setgid files (02000)
3209     t      files with the sticky bit (01000)
3210
3211   print *(m-1)          # Files modified up to a day ago
3212   print *(a1)           # Files accessed a day ago
3213   print *(@)            # Just symlinks
3214   print *(Lk+50)        # Files bigger than 50 kilobytes
3215   print *(Lk-50)        # Files smaller than 50 kilobytes
3216   print **/*.c          # All *.c files recursively starting in \$PWD
3217   print **/*.c~file.c   # Same as above, but excluding 'file.c'
3218   print (foo|bar).*     # Files starting with 'foo' or 'bar'
3219   print *~*.*           # All Files that do not contain a dot
3220   chmod 644 *(.^x)      # make all plain non-executable files publically readable
3221   print -l *(.c|.h)     # Lists *.c and *.h
3222   print **/*(g:users:)  # Recursively match all files that are owned by group 'users'
3223   echo /proc/*/cwd(:h:t:s/self//) # Analogous to >ps ax | awk '{print $1}'<"
3224 }
3225 alias help-zshglob=H-Glob
3226
3227 # grep for running process, like: 'any vim'
3228 function any () {
3229     emulate -L zsh
3230     unsetopt KSH_ARRAYS
3231     if [[ -z "$1" ]] ; then
3232         echo "any - grep for process(es) by keyword" >&2
3233         echo "Usage: any <keyword>" >&2 ; return 1
3234     else
3235         ps xauwww | grep -i "${grep_options[@]}" "[${1[1]}]${1[2,-1]}"
3236     fi
3237 }
3238
3239
3240 # After resuming from suspend, system is paging heavily, leading to very bad interactivity.
3241 # taken from $LINUX-KERNELSOURCE/Documentation/power/swsusp.txt
3242 [[ -r /proc/1/maps ]] && \
3243 function deswap () {
3244     print 'Reading /proc/[0-9]*/maps and sending output to /dev/null, this might take a while.'
3245     cat $(sed -ne 's:.* /:/:p' /proc/[0-9]*/maps | sort -u | grep -v '^/dev/')  > /dev/null
3246     print 'Finished, running "swapoff -a; swapon -a" may also be useful.'
3247 }
3248
3249 # a wrapper for vim, that deals with title setting
3250 #   VIM_OPTIONS
3251 #       set this array to a set of options to vim you always want
3252 #       to have set when calling vim (in .zshrc.local), like:
3253 #           VIM_OPTIONS=( -p )
3254 #       This will cause vim to send every file given on the
3255 #       commandline to be send to it's own tab (needs vim7).
3256 if check_com vim; then
3257     function vim () {
3258         VIM_PLEASE_SET_TITLE='yes' command vim ${VIM_OPTIONS} "$@"
3259     }
3260 fi
3261
3262 ssl_hashes=( sha512 sha256 sha1 md5 )
3263
3264 for sh in ${ssl_hashes}; do
3265     eval 'ssl-cert-'${sh}'() {
3266         emulate -L zsh
3267         if [[ -z $1 ]] ; then
3268             printf '\''usage: %s <file>\n'\'' "ssh-cert-'${sh}'"
3269             return 1
3270         fi
3271         openssl x509 -noout -fingerprint -'${sh}' -in $1
3272     }'
3273 done; unset sh
3274
3275 function ssl-cert-fingerprints () {
3276     emulate -L zsh
3277     local i
3278     if [[ -z $1 ]] ; then
3279         printf 'usage: ssl-cert-fingerprints <file>\n'
3280         return 1
3281     fi
3282     for i in ${ssl_hashes}
3283         do ssl-cert-$i $1;
3284     done
3285 }
3286
3287 function ssl-cert-info () {
3288     emulate -L zsh
3289     if [[ -z $1 ]] ; then
3290         printf 'usage: ssl-cert-info <file>\n'
3291         return 1
3292     fi
3293     openssl x509 -noout -text -in $1
3294     ssl-cert-fingerprints $1
3295 }
3296
3297 # make sure our environment is clean regarding colors
3298 builtin unset -v BLUE RED GREEN CYAN YELLOW MAGENTA WHITE NO_COLOR
3299
3300 # "persistent history"
3301 # just write important commands you always need to $GRML_IMPORTANT_COMMANDS
3302 # defaults for backward compatibility to ~/.important_commands
3303 if [[ -r ~/.important_commands ]] ; then
3304     GRML_IMPORTANT_COMMANDS=~/.important_commands
3305 else
3306     GRML_IMPORTANT_COMMANDS=${GRML_IMPORTANT_COMMANDS:-${ZDOTDIR:-${HOME}}/.important_commands}
3307 fi
3308 [[ -r ${GRML_IMPORTANT_COMMANDS} ]] && builtin fc -R ${GRML_IMPORTANT_COMMANDS}
3309
3310 # load the lookup subsystem if it's available on the system
3311 zrcautoload lookupinit && lookupinit
3312
3313 # variables
3314
3315 # set terminal property (used e.g. by msgid-chooser)
3316 case "${COLORTERM}" in
3317   truecolor)
3318     # do not overwrite
3319     ;;
3320   *)
3321     export COLORTERM="yes"
3322     ;;
3323 esac
3324
3325 # aliases
3326
3327 # general
3328 #a2# Execute \kbd{du -sch}
3329 [[ -n "$GRML_NO_SMALL_ALIASES" ]] || alias da='du -sch'
3330
3331 # listing stuff
3332 #a2# Execute \kbd{ls -lSrah}
3333 alias dir="command ls -lSrah"
3334 #a2# Only show dot-directories
3335 alias lad='command ls -d .*(/)'
3336 #a2# Only show dot-files
3337 alias lsa='command ls -a .*(.)'
3338 #a2# Only files with setgid/setuid/sticky flag
3339 alias lss='command ls -l *(s,S,t)'
3340 #a2# Only show symlinks
3341 alias lsl='command ls -l *(@)'
3342 #a2# Display only executables
3343 alias lsx='command ls -l *(*)'
3344 #a2# Display world-{readable,writable,executable} files
3345 alias lsw='command ls -ld *(R,W,X.^ND/)'
3346 #a2# Display the ten biggest files
3347 alias lsbig="command ls -flh *(.OL[1,10])"
3348 #a2# Only show directories
3349 alias lsd='command ls -d *(/)'
3350 #a2# Only show empty directories
3351 alias lse='command ls -d *(/^F)'
3352 #a2# Display the ten newest files
3353 alias lsnew="command ls -rtlh *(D.om[1,10])"
3354 #a2# Display the ten oldest files
3355 alias lsold="command ls -rtlh *(D.Om[1,10])"
3356 #a2# Display the ten smallest files
3357 alias lssmall="command ls -Srl *(.oL[1,10])"
3358 #a2# Display the ten newest directories and ten newest .directories
3359 alias lsnewdir="command ls -rthdl *(/om[1,10]) .*(D/om[1,10])"
3360 #a2# Display the ten oldest directories and ten oldest .directories
3361 alias lsolddir="command ls -rthdl *(/Om[1,10]) .*(D/Om[1,10])"
3362
3363 # some useful aliases
3364 #a2# Remove current empty directory. Execute \kbd{cd ..; rmdir \$OLDCWD}
3365 alias rmcdir='cd ..; rmdir $OLDPWD || cd $OLDPWD'
3366
3367 #a2# ssh with StrictHostKeyChecking=no \\&\quad and UserKnownHostsFile unset
3368 alias insecssh='ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null"'
3369 #a2# scp with StrictHostKeyChecking=no \\&\quad and UserKnownHostsFile unset
3370 alias insecscp='scp -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null"'
3371
3372 # work around non utf8 capable software in utf environment via $LANG and luit
3373 if check_com isutfenv && check_com luit ; then
3374     if check_com -c mrxvt ; then
3375         isutfenv && [[ -n "$LANG" ]] && \
3376             alias mrxvt="LANG=${LANG/(#b)(*)[.@]*/$match[1].iso885915} luit mrxvt"
3377     fi
3378
3379     if check_com -c aterm ; then
3380         isutfenv && [[ -n "$LANG" ]] && \
3381             alias aterm="LANG=${LANG/(#b)(*)[.@]*/$match[1].iso885915} luit aterm"
3382     fi
3383
3384     if check_com -c centericq ; then
3385         isutfenv && [[ -n "$LANG" ]] && \
3386             alias centericq="LANG=${LANG/(#b)(*)[.@]*/$match[1].iso885915} luit centericq"
3387     fi
3388 fi
3389
3390 # useful functions
3391
3392 #f5# Backup \kbd{file_or_folder {\rm to} file_or_folder\_timestamp}
3393 function bk () {
3394     emulate -L zsh
3395     local current_date=$(date -u "+%Y%m%dT%H%M%SZ")
3396     local clean keep move verbose result all to_bk
3397     setopt extended_glob
3398     keep=1
3399     while getopts ":hacmrv" opt; do
3400         case $opt in
3401             a) (( all++ ));;
3402             c) unset move clean && (( ++keep ));;
3403             m) unset keep clean && (( ++move ));;
3404             r) unset move keep && (( ++clean ));;
3405             v) verbose="-v";;
3406             h) <<__EOF0__
3407 bk [-hcmv] FILE [FILE ...]
3408 bk -r [-av] [FILE [FILE ...]]
3409 Backup a file or folder in place and append the timestamp
3410 Remove backups of a file or folder, or all backups in the current directory
3411
3412 Usage:
3413 -h    Display this help text
3414 -c    Keep the file/folder as is, create a copy backup using cp(1) (default)
3415 -m    Move the file/folder, using mv(1)
3416 -r    Remove backups of the specified file or directory, using rm(1). If none
3417       is provided, remove all backups in the current directory.
3418 -a    Remove all (even hidden) backups.
3419 -v    Verbose
3420
3421 The -c, -r and -m options are mutually exclusive. If specified at the same time,
3422 the last one is used.
3423
3424 The return code is the sum of all cp/mv/rm return codes.
3425 __EOF0__
3426 return 0;;
3427             \?) bk -h >&2; return 1;;
3428         esac
3429     done
3430     shift "$((OPTIND-1))"
3431     if (( keep > 0 )); then
3432         if islinux || isfreebsd; then
3433             for to_bk in "$@"; do
3434                 cp $verbose -a "${to_bk%/}" "${to_bk%/}_$current_date"
3435                 (( result += $? ))
3436             done
3437         else
3438             for to_bk in "$@"; do
3439                 cp $verbose -pR "${to_bk%/}" "${to_bk%/}_$current_date"
3440                 (( result += $? ))
3441             done
3442         fi
3443     elif (( move > 0 )); then
3444         while (( $# > 0 )); do
3445             mv $verbose "${1%/}" "${1%/}_$current_date"
3446             (( result += $? ))
3447             shift
3448         done
3449     elif (( clean > 0 )); then
3450         if (( $# > 0 )); then
3451             for to_bk in "$@"; do
3452                 rm $verbose -rf "${to_bk%/}"_[0-9](#c8)T([0-1][0-9]|2[0-3])([0-5][0-9])(#c2)Z
3453                 (( result += $? ))
3454             done
3455         else
3456             if (( all > 0 )); then
3457                 rm $verbose -rf *_[0-9](#c8)T([0-1][0-9]|2[0-3])([0-5][0-9])(#c2)Z(D)
3458             else
3459                 rm $verbose -rf *_[0-9](#c8)T([0-1][0-9]|2[0-3])([0-5][0-9])(#c2)Z
3460             fi
3461             (( result += $? ))
3462         fi
3463     fi
3464     return $result
3465 }
3466
3467 #f5# cd to directory and list files
3468 function cl () {
3469     emulate -L zsh
3470     cd $1 && ls -a
3471 }
3472
3473 # smart cd function, allows switching to /etc when running 'cd /etc/fstab'
3474 function cd () {
3475     if (( ${#argv} == 1 )) && [[ -f ${1} ]]; then
3476         [[ ! -e ${1:h} ]] && return 1
3477         print "Correcting ${1} to ${1:h}"
3478         builtin cd ${1:h}
3479     else
3480         builtin cd "$@"
3481     fi
3482 }
3483
3484 #f5# Create Directory and \kbd{cd} to it
3485 function mkcd () {
3486     if (( ARGC != 1 )); then
3487         printf 'usage: mkcd <new-directory>\n'
3488         return 1;
3489     fi
3490     if [[ ! -d "$1" ]]; then
3491         command mkdir -p "$1"
3492     else
3493         printf '`%s'\'' already exists: cd-ing.\n' "$1"
3494     fi
3495     builtin cd "$1"
3496 }
3497
3498 #f5# Create temporary directory and \kbd{cd} to it
3499 function cdt () {
3500     builtin cd "$(mktemp -d)"
3501     builtin pwd
3502 }
3503
3504 #f5# List files which have been accessed within the last {\it n} days, {\it n} defaults to 1
3505 function accessed () {
3506     emulate -L zsh
3507     print -l -- *(a-${1:-1})
3508 }
3509
3510 #f5# List files which have been changed within the last {\it n} days, {\it n} defaults to 1
3511 function changed () {
3512     emulate -L zsh
3513     print -l -- *(c-${1:-1})
3514 }
3515
3516 #f5# List files which have been modified within the last {\it n} days, {\it n} defaults to 1
3517 function modified () {
3518     emulate -L zsh
3519     print -l -- *(m-${1:-1})
3520 }
3521 # modified() was named new() in earlier versions, add an alias for backwards compatibility
3522 check_com new || alias new=modified
3523
3524 # use colors when GNU grep with color-support
3525 if (( $#grep_options > 0 )); then
3526     o=${grep_options:+"${grep_options[*]}"}
3527     #a2# Execute \kbd{grep -{}-color=auto}
3528     alias grep='grep '$o
3529     alias egrep='egrep '$o
3530     unset o
3531 fi
3532
3533 # Translate DE<=>EN
3534 # 'translate' looks up a word in a file with language-to-language
3535 # translations (field separator should be " : "). A typical wordlist looks
3536 # like the following:
3537 #  | english-word : german-translation
3538 # It's also only possible to translate english to german but not reciprocal.
3539 # Use the following oneliner to reverse the sort order:
3540 #  $ awk -F ':' '{ print $2" : "$1" "$3 }' \
3541 #    /usr/local/lib/words/en-de.ISO-8859-1.vok > ~/.translate/de-en.ISO-8859-1.vok
3542 #f5# Translates a word
3543 function trans () {
3544     emulate -L zsh
3545     case "$1" in
3546         -[dD]*)
3547             translate -l de-en $2
3548             ;;
3549         -[eE]*)
3550             translate -l en-de $2
3551             ;;
3552         *)
3553             echo "Usage: $0 { -D | -E }"
3554             echo "         -D == German to English"
3555             echo "         -E == English to German"
3556     esac
3557 }
3558
3559 # Usage: simple-extract <file>
3560 # Using option -d deletes the original archive file.
3561 #f5# Smart archive extractor
3562 function simple-extract () {
3563     emulate -L zsh
3564     setopt extended_glob noclobber
3565     local ARCHIVE DELETE_ORIGINAL DECOMP_CMD USES_STDIN USES_STDOUT GZTARGET WGET_CMD
3566     local RC=0
3567     zparseopts -D -E "d=DELETE_ORIGINAL"
3568     for ARCHIVE in "${@}"; do
3569         case $ARCHIVE in
3570             *(tar.bz2|tbz2|tbz))
3571                 DECOMP_CMD="tar -xvjf -"
3572                 USES_STDIN=true
3573                 USES_STDOUT=false
3574                 ;;
3575             *(tar.gz|tgz))
3576                 DECOMP_CMD="tar -xvzf -"
3577                 USES_STDIN=true
3578                 USES_STDOUT=false
3579                 ;;
3580             *(tar.xz|txz|tar.lzma))
3581                 DECOMP_CMD="tar -xvJf -"
3582                 USES_STDIN=true
3583                 USES_STDOUT=false
3584                 ;;
3585             *tar.zst)
3586                 DECOMP_CMD="tar --zstd -xvf -"
3587                 USES_STDIN=true
3588                 USES_STDOUT=false
3589                 ;;
3590             *tar.lrz)
3591                 DECOMP_CMD="lrzuntar"
3592                 USES_STDIN=false
3593                 USES_STDOUT=false
3594                 ;;
3595             *tar)
3596                 DECOMP_CMD="tar -xvf -"
3597                 USES_STDIN=true
3598                 USES_STDOUT=false
3599                 ;;
3600             *rar)
3601                 DECOMP_CMD="unrar x"
3602                 USES_STDIN=false
3603                 USES_STDOUT=false
3604                 ;;
3605             *lzh)
3606                 DECOMP_CMD="lha x"
3607                 USES_STDIN=false
3608                 USES_STDOUT=false
3609                 ;;
3610             *7z)
3611                 DECOMP_CMD="7z x"
3612                 USES_STDIN=false
3613                 USES_STDOUT=false
3614                 ;;
3615             *(zip|jar))
3616                 DECOMP_CMD="unzip"
3617                 USES_STDIN=false
3618                 USES_STDOUT=false
3619                 ;;
3620             *deb)
3621                 DECOMP_CMD="ar -x"
3622                 USES_STDIN=false
3623                 USES_STDOUT=false
3624                 ;;
3625             *bz2)
3626                 DECOMP_CMD="bzip2 -d -c -"
3627                 USES_STDIN=true
3628                 USES_STDOUT=true
3629                 ;;
3630             *(gz|Z))
3631                 DECOMP_CMD="gzip -d -c -"
3632                 USES_STDIN=true
3633                 USES_STDOUT=true
3634                 ;;
3635             *(xz|lzma))
3636                 DECOMP_CMD="xz -d -c -"
3637                 USES_STDIN=true
3638                 USES_STDOUT=true
3639                 ;;
3640             *zst)
3641                 DECOMP_CMD="zstd -d -c -"
3642                 USES_STDIN=true
3643                 USES_STDOUT=true
3644                 ;;
3645             *lrz)
3646                 DECOMP_CMD="lrunzip -"
3647                 USES_STDIN=true
3648                 USES_STDOUT=true
3649                 ;;
3650             *)
3651                 print "ERROR: '$ARCHIVE' has unrecognized archive type." >&2
3652                 RC=$((RC+1))
3653                 continue
3654                 ;;
3655         esac
3656
3657         if ! check_com ${DECOMP_CMD[(w)1]}; then
3658             echo "ERROR: ${DECOMP_CMD[(w)1]} not installed." >&2
3659             RC=$((RC+2))
3660             continue
3661         fi
3662
3663         GZTARGET="${ARCHIVE:t:r}"
3664         if [[ -f $ARCHIVE ]] ; then
3665
3666             print "Extracting '$ARCHIVE' ..."
3667             if $USES_STDIN; then
3668                 if $USES_STDOUT; then
3669                     ${=DECOMP_CMD} < "$ARCHIVE" > $GZTARGET
3670                 else
3671                     ${=DECOMP_CMD} < "$ARCHIVE"
3672                 fi
3673             else
3674                 if $USES_STDOUT; then
3675                     ${=DECOMP_CMD} "$ARCHIVE" > $GZTARGET
3676                 else
3677                     ${=DECOMP_CMD} "$ARCHIVE"
3678                 fi
3679             fi
3680             [[ $? -eq 0 && -n "$DELETE_ORIGINAL" ]] && rm -f "$ARCHIVE"
3681
3682         elif [[ "$ARCHIVE" == (#s)(https|http|ftp)://* ]] ; then
3683             if check_com curl; then
3684                 WGET_CMD="curl -L -s -o -"
3685             elif check_com wget; then
3686                 WGET_CMD="wget -q -O -"
3687             elif check_com fetch; then
3688                 WGET_CMD="fetch -q -o -"
3689             else
3690                 print "ERROR: neither wget, curl nor fetch is installed" >&2
3691                 RC=$((RC+4))
3692                 continue
3693             fi
3694             print "Downloading and Extracting '$ARCHIVE' ..."
3695             if $USES_STDIN; then
3696                 if $USES_STDOUT; then
3697                     ${=WGET_CMD} "$ARCHIVE" | ${=DECOMP_CMD} > $GZTARGET
3698                     RC=$((RC+$?))
3699                 else
3700                     ${=WGET_CMD} "$ARCHIVE" | ${=DECOMP_CMD}
3701                     RC=$((RC+$?))
3702                 fi
3703             else
3704                 if $USES_STDOUT; then
3705                     ${=DECOMP_CMD} =(${=WGET_CMD} "$ARCHIVE") > $GZTARGET
3706                 else
3707                     ${=DECOMP_CMD} =(${=WGET_CMD} "$ARCHIVE")
3708                 fi
3709             fi
3710
3711         else
3712             print "ERROR: '$ARCHIVE' is neither a valid file nor a supported URI." >&2
3713             RC=$((RC+8))
3714         fi
3715     done
3716     return $RC
3717 }
3718
3719 function __archive_or_uri () {
3720     _alternative \
3721         'files:Archives:_files -g "*.(#l)(tar.bz2|tbz2|tbz|tar.gz|tgz|tar.xz|txz|tar.lzma|tar|rar|lzh|7z|zip|jar|deb|bz2|gz|Z|xz|lzma)"' \
3722         '_urls:Remote Archives:_urls'
3723 }
3724
3725 function _simple_extract () {
3726     _arguments \
3727         '-d[delete original archivefile after extraction]' \
3728         '*:Archive Or Uri:__archive_or_uri'
3729 }
3730 compdef _simple_extract simple-extract
3731 [[ -n "$GRML_NO_SMALL_ALIASES" ]] || alias se=simple-extract
3732
3733 #f5# Change the xterm title from within GNU-screen
3734 function xtrename () {
3735     emulate -L zsh
3736     if [[ $1 != "-f" ]] ; then
3737         if [[ -z ${DISPLAY} ]] ; then
3738             printf 'xtrename only makes sense in X11.\n'
3739             return 1
3740         fi
3741     else
3742         shift
3743     fi
3744     if [[ -z $1 ]] ; then
3745         printf 'usage: xtrename [-f] "title for xterm"\n'
3746         printf '  renames the title of xterm from _within_ screen.\n'
3747         printf '  also works without screen.\n'
3748         printf '  will not work if DISPLAY is unset, use -f to override.\n'
3749         return 0
3750     fi
3751     print -n "\eP\e]0;${1}\C-G\e\\"
3752     return 0
3753 }
3754
3755 # Create small urls via http://goo.gl using curl(1).
3756 # API reference: https://code.google.com/apis/urlshortener/
3757 function zurl () {
3758     emulate -L zsh
3759     setopt extended_glob
3760
3761     if [[ -z $1 ]]; then
3762         print "USAGE: zurl <URL>"
3763         return 1
3764     fi
3765
3766     local PN url prog api json contenttype item
3767     local -a data
3768     PN=$0
3769     url=$1
3770
3771     # Prepend 'http://' to given URL where necessary for later output.
3772     if [[ ${url} != http(s|)://* ]]; then
3773         url='http://'${url}
3774     fi
3775
3776     if check_com -c curl; then
3777         prog=curl
3778     else
3779         print "curl is not available, but mandatory for ${PN}. Aborting."
3780         return 1
3781     fi
3782     api='https://www.googleapis.com/urlshortener/v1/url'
3783     contenttype="Content-Type: application/json"
3784     json="{\"longUrl\": \"${url}\"}"
3785     data=(${(f)"$($prog --silent -H ${contenttype} -d ${json} $api)"})
3786     # Parse the response
3787     for item in "${data[@]}"; do
3788         case "$item" in
3789             ' '#'"id":'*)
3790                 item=${item#*: \"}
3791                 item=${item%\",*}
3792                 printf '%s\n' "$item"
3793                 return 0
3794                 ;;
3795         esac
3796     done
3797     return 1
3798 }
3799
3800 #f2# Find history events by search pattern and list them by date.
3801 function whatwhen () {
3802     emulate -L zsh
3803     local usage help ident format_l format_s first_char remain first last
3804     usage='USAGE: whatwhen [options] <searchstring> <search range>'
3805     help='Use `whatwhen -h'\'' for further explanations.'
3806     ident=${(l,${#${:-Usage: }},, ,)}
3807     format_l="${ident}%s\t\t\t%s\n"
3808     format_s="${format_l//(\\t)##/\\t}"
3809     # Make the first char of the word to search for case
3810     # insensitive; e.g. [aA]
3811     first_char=[${(L)1[1]}${(U)1[1]}]
3812     remain=${1[2,-1]}
3813     # Default search range is `-100'.
3814     first=${2:-\-100}
3815     # Optional, just used for `<first> <last>' given.
3816     last=$3
3817     case $1 in
3818         ("")
3819             printf '%s\n\n' 'ERROR: No search string specified. Aborting.'
3820             printf '%s\n%s\n\n' ${usage} ${help} && return 1
3821         ;;
3822         (-h)
3823             printf '%s\n\n' ${usage}
3824             print 'OPTIONS:'
3825             printf $format_l '-h' 'show help text'
3826             print '\f'
3827             print 'SEARCH RANGE:'
3828             printf $format_l "'0'" 'the whole history,'
3829             printf $format_l '-<n>' 'offset to the current history number; (default: -100)'
3830             printf $format_s '<[-]first> [<last>]' 'just searching within a give range'
3831             printf '\n%s\n' 'EXAMPLES:'
3832             printf ${format_l/(\\t)/} 'whatwhen grml' '# Range is set to -100 by default.'
3833             printf $format_l 'whatwhen zsh -250'
3834             printf $format_l 'whatwhen foo 1 99'
3835         ;;
3836         (\?)
3837             printf '%s\n%s\n\n' ${usage} ${help} && return 1
3838         ;;
3839         (*)
3840             # -l list results on stout rather than invoking $EDITOR.
3841             # -i Print dates as in YYYY-MM-DD.
3842             # -m Search for a - quoted - pattern within the history.
3843             fc -li -m "*${first_char}${remain}*" $first $last
3844         ;;
3845     esac
3846 }
3847
3848 # mercurial related stuff
3849 if check_com -c hg ; then
3850     # gnu like diff for mercurial
3851     # http://www.selenic.com/mercurial/wiki/index.cgi/TipsAndTricks
3852     #f5# GNU like diff for mercurial
3853     function hgdi () {
3854         emulate -L zsh
3855         local i
3856         for i in $(hg status -marn "$@") ; diff -ubwd <(hg cat "$i") "$i"
3857     }
3858
3859     # build debian package
3860     #a2# Alias for \kbd{hg-buildpackage}
3861     alias hbp='hg-buildpackage'
3862
3863     # execute commands on the versioned patch-queue from the current repos
3864     [[ -n "$GRML_NO_SMALL_ALIASES" ]] || alias mq='hg -R $(readlink -f $(hg root)/.hg/patches)'
3865
3866     # diffstat for specific version of a mercurial repository
3867     #   hgstat      => display diffstat between last revision and tip
3868     #   hgstat 1234 => display diffstat between revision 1234 and tip
3869     #f5# Diffstat for specific version of a mercurial repos
3870     function hgstat () {
3871         emulate -L zsh
3872         [[ -n "$1" ]] && hg diff -r $1 -r tip | diffstat || hg export tip | diffstat
3873     }
3874
3875 fi # end of check whether we have the 'hg'-executable
3876
3877 # disable bracketed paste mode for dumb terminals
3878 [[ "$TERM" == dumb ]] && unset zle_bracketed_paste
3879
3880 # grml-small cleanups and workarounds
3881
3882 # The following is used to remove zsh-config-items that do not work
3883 # in grml-small by default.
3884 # If you do not want these adjustments (for whatever reason), set
3885 # $GRMLSMALL_SPECIFIC to 0 in your .zshrc.pre file (which this configuration
3886 # sources if it is there).
3887
3888 if (( GRMLSMALL_SPECIFIC > 0 )) && isgrmlsmall ; then
3889
3890     # Clean up
3891
3892     unset "abk[V]"
3893     unalias    'V'      &> /dev/null
3894     unfunction vman     &> /dev/null
3895     unfunction viless   &> /dev/null
3896     unfunction 2html    &> /dev/null
3897
3898     # manpages are not in grmlsmall
3899     unfunction manzsh   &> /dev/null
3900     unfunction man2     &> /dev/null
3901
3902     # Workarounds
3903
3904     # See https://github.com/grml/grml/issues/56
3905     if ! [[ -x ${commands[dig]} ]]; then
3906         function dig_after_all () {
3907             unfunction dig
3908             unfunction _dig
3909             autoload -Uz _dig
3910             unfunction dig_after_all
3911         }
3912         function dig () {
3913             if [[ -x ${commands[dig]} ]]; then
3914                 dig_after_all
3915                 command dig "$@"
3916                 return "$!"
3917             fi
3918             printf 'This installation does not include `dig'\'' for size reasons.\n'
3919             printf 'Try `drill'\'' as a light weight alternative.\n'
3920             return 0
3921         }
3922         function _dig () {
3923             if [[ -x ${commands[dig]} ]]; then
3924                 dig_after_all
3925                 zle -M 'Found `dig'\'' installed. '
3926             else
3927                 zle -M 'Try `drill'\'' instead of `dig'\''.'
3928             fi
3929         }
3930         compdef _dig dig
3931     fi
3932 fi
3933
3934 zrclocal
3935
3936 unfunction grml_status_feature
3937
3938 ## genrefcard.pl settings
3939
3940 ### doc strings for external functions from files
3941 #m# f5 grml-wallpaper() Sets a wallpaper (try completion for possible values)
3942
3943 ### example: split functions-search 8,16,24,32
3944 #@# split functions-search 8
3945
3946 ## END OF FILE #################################################################
3947 # vim:filetype=zsh foldmethod=marker autoindent expandtab shiftwidth=4
3948 # Local variables:
3949 # mode: sh
3950 # End: