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