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