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