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