zshrc: no longer set histignorealldups
[grml-etc-core.git] / etc / zsh / zshrc
1 # Filename:      /etc/zsh/zshrc
2 # Purpose:       config file for zsh (z shell)
3 # Authors:       grml-team (grml.org), (c) Michael Prokop <mika@grml.org>
4 # Bug-Reports:   see http://grml.org/bugs/
5 # License:       This file is licensed under the GPL v2.
6 ################################################################################
7 # This file is sourced only for interactive shells. It
8 # should contain commands to set up aliases, functions,
9 # options, key bindings, etc.
10 #
11 # Global Order: zshenv, zprofile, zshrc, zlogin
12 ################################################################################
13
14 # USAGE
15 # If you are using this file as your ~/.zshrc file, please use ~/.zshrc.pre
16 # and ~/.zshrc.local for your own customisations. The former file is read
17 # before ~/.zshrc, the latter is read after it. Also, consider reading the
18 # refcard and the reference manual for this setup, both available from:
19 #     <http://grml.org/zsh/>
20
21 # Contributing:
22 # If you want to help to improve grml's zsh setup, clone the grml-etc-core
23 # repository from git.grml.org:
24 #   git clone git://git.grml.org/grml-etc-core.git
25 #
26 # Make your changes, commit them; use 'git format-patch' to create a series
27 # of patches and send those to the following address via 'git send-email':
28 #   grml-etc-core@grml.org
29 #
30 # Doing so makes sure the right people get your patches for review and
31 # possibly inclusion.
32
33 # zsh-refcard-tag documentation:
34 #   You may notice strange looking comments in this file.
35 #   These are there for a purpose. grml's zsh-refcard can now be
36 #   automatically generated from the contents of the actual configuration
37 #   file. However, we need a little extra information on which comments
38 #   and what lines of code to take into account (and for what purpose).
39 #
40 # Here is what they mean:
41 #
42 # List of tags (comment types) used:
43 #   #a#     Next line contains an important alias, that should
44 #           be included in the grml-zsh-refcard.
45 #           (placement tag: @@INSERT-aliases@@)
46 #   #f#     Next line contains the beginning of an important function.
47 #           (placement tag: @@INSERT-functions@@)
48 #   #v#     Next line contains an important variable.
49 #           (placement tag: @@INSERT-variables@@)
50 #   #k#     Next line contains an important keybinding.
51 #           (placement tag: @@INSERT-keybindings@@)
52 #   #d#     Hashed directories list generation:
53 #               start   denotes the start of a list of 'hash -d'
54 #                       definitions.
55 #               end     denotes its end.
56 #           (placement tag: @@INSERT-hasheddirs@@)
57 #   #A#     Abbreviation expansion list generation:
58 #               start   denotes the beginning of abbreviations.
59 #               end     denotes their end.
60 #           Lines within this section that end in '#d .*' provide
61 #           extra documentation to be included in the refcard.
62 #           (placement tag: @@INSERT-abbrev@@)
63 #   #m#     This tag allows you to manually generate refcard entries
64 #           for code lines that are hard/impossible to parse.
65 #               Example:
66 #                   #m# k ESC-h Call the run-help function
67 #               That would add a refcard entry in the keybindings table
68 #               for 'ESC-h' with the given comment.
69 #           So the syntax is: #m# <section> <argument> <comment>
70 #   #o#     This tag lets you insert entries to the 'other' hash.
71 #           Generally, this should not be used. It is there for
72 #           things that cannot be done easily in another way.
73 #           (placement tag: @@INSERT-other-foobar@@)
74 #
75 #   All of these tags (except for m and o) take two arguments, the first
76 #   within the tag, the other after the tag:
77 #
78 #   #<tag><section># <comment>
79 #
80 #   Where <section> is really just a number, which are defined by the
81 #   @secmap array on top of 'genrefcard.pl'. The reason for numbers
82 #   instead of names is, that for the reader, the tag should not differ
83 #   much from a regular comment. For zsh, it is a regular comment indeed.
84 #   The numbers have got the following meanings:
85 #         0 -> "default"
86 #         1 -> "system"
87 #         2 -> "user"
88 #         3 -> "debian"
89 #         4 -> "search"
90 #         5 -> "shortcuts"
91 #         6 -> "services"
92 #
93 #   So, the following will add an entry to the 'functions' table in the
94 #   'system' section, with a (hopefully) descriptive comment:
95 #       #f1# Edit an alias via zle
96 #       edalias() {
97 #
98 #   It will then show up in the @@INSERT-aliases-system@@ replacement tag
99 #   that can be found in 'grml-zsh-refcard.tex.in'.
100 #   If the section number is omitted, the 'default' section is assumed.
101 #   Furthermore, in 'grml-zsh-refcard.tex.in' @@INSERT-aliases@@ is
102 #   exactly the same as @@INSERT-aliases-default@@. If you want a list of
103 #   *all* aliases, for example, use @@INSERT-aliases-all@@.
104
105 # zsh profiling
106 # just execute 'ZSH_PROFILE_RC=1 zsh' and run 'zprof' to get the details
107 if [[ $ZSH_PROFILE_RC -gt 0 ]] ; then
108     zmodload zsh/zprof
109 fi
110
111 typeset -A GRML_STATUS_FEATURES
112
113 function grml_status_feature () {
114     emulate -L zsh
115     local f=$1
116     local -i success=$2
117     if (( success == 0 )); then
118         GRML_STATUS_FEATURES[$f]=success
119     else
120         GRML_STATUS_FEATURES[$f]=failure
121     fi
122     return 0
123 }
124
125 function grml_status_features () {
126     emulate -L zsh
127     local mode=${1:-+-}
128     local this
129     if [[ $mode == -h ]] || [[ $mode == --help ]]; then
130         cat <<EOF
131 grml_status_features [-h|--help|-|+|+-|FEATURE]
132
133 Prints a summary of features the grml setup is trying to load. The
134 result of loading a feature is recorded. This function lets you query
135 the result.
136
137 The function takes one argument: "-h" or "--help" to display this help
138 text, "+" to display a list of all successfully loaded features, "-" for
139 a list of all features that failed to load. "+-" to show a list of all
140 features with their statuses.
141
142 Any other word is considered to by a feature and prints its status.
143
144 The default mode is "+-".
145 EOF
146         return 0
147     fi
148     if [[ $mode != - ]] && [[ $mode != + ]] && [[ $mode != +- ]]; then
149         this="${GRML_STATUS_FEATURES[$mode]}"
150         if [[ -z $this ]]; then
151             printf 'unknown\n'
152             return 1
153         else
154             printf '%s\n' $this
155         fi
156         return 0
157     fi
158     for key in ${(ok)GRML_STATUS_FEATURES}; do
159         this="${GRML_STATUS_FEATURES[$key]}"
160         if [[ $this == success ]] && [[ $mode == *+* ]]; then
161             printf '%-16s %s\n' $key $this
162         fi
163         if [[ $this == failure ]] && [[ $mode == *-* ]]; then
164             printf '%-16s %s\n' $key $this
165         fi
166     done
167     return 0
168 }
169
170 # load .zshrc.pre to give the user the chance to overwrite the defaults
171 [[ -r ${ZDOTDIR:-${HOME}}/.zshrc.pre ]] && source ${ZDOTDIR:-${HOME}}/.zshrc.pre
172
173 # check for version/system
174 # check for versions (compatibility reasons)
175 function is51 () {
176     [[ $ZSH_VERSION == 5.<1->* ]] && return 0
177     return 1
178 }
179
180 function is4 () {
181     [[ $ZSH_VERSION == <4->* ]] && return 0
182     return 1
183 }
184
185 function is41 () {
186     [[ $ZSH_VERSION == 4.<1->* || $ZSH_VERSION == <5->* ]] && return 0
187     return 1
188 }
189
190 function is42 () {
191     [[ $ZSH_VERSION == 4.<2->* || $ZSH_VERSION == <5->* ]] && return 0
192     return 1
193 }
194
195 function is425 () {
196     [[ $ZSH_VERSION == 4.2.<5->* || $ZSH_VERSION == 4.<3->* || $ZSH_VERSION == <5->* ]] && return 0
197     return 1
198 }
199
200 function is43 () {
201     [[ $ZSH_VERSION == 4.<3->* || $ZSH_VERSION == <5->* ]] && return 0
202     return 1
203 }
204
205 function is433 () {
206     [[ $ZSH_VERSION == 4.3.<3->* || $ZSH_VERSION == 4.<4->* \
207                                  || $ZSH_VERSION == <5->* ]] && return 0
208     return 1
209 }
210
211 function is437 () {
212     [[ $ZSH_VERSION == 4.3.<7->* || $ZSH_VERSION == 4.<4->* \
213                                  || $ZSH_VERSION == <5->* ]] && return 0
214     return 1
215 }
216
217 function is439 () {
218     [[ $ZSH_VERSION == 4.3.<9->* || $ZSH_VERSION == 4.<4->* \
219                                  || $ZSH_VERSION == <5->* ]] && return 0
220     return 1
221 }
222
223 #f1# Checks whether or not you're running grml
224 function isgrml () {
225     [[ -f /etc/grml_version ]] && return 0
226     return 1
227 }
228
229 #f1# Checks whether or not you're running a grml cd
230 function isgrmlcd () {
231     [[ -f /etc/grml_cd ]] && return 0
232     return 1
233 }
234
235 if isgrml ; then
236 #f1# Checks whether or not you're running grml-small
237     function isgrmlsmall () {
238         if [[ ${${${(f)"$(</etc/grml_version)"}%% *}##*-} == 'small' ]]; then
239             return 0
240         fi
241         return 1
242     }
243 else
244     function isgrmlsmall () { return 1 }
245 fi
246
247 GRML_OSTYPE=$(uname -s)
248
249 function islinux () {
250     [[ $GRML_OSTYPE == "Linux" ]]
251 }
252
253 function isdarwin () {
254     [[ $GRML_OSTYPE == "Darwin" ]]
255 }
256
257 function isfreebsd () {
258     [[ $GRML_OSTYPE == "FreeBSD" ]]
259 }
260
261 function isopenbsd () {
262     [[ $GRML_OSTYPE == "OpenBSD" ]]
263 }
264
265 function issolaris () {
266     [[ $GRML_OSTYPE == "SunOS" ]]
267 }
268
269 #f1# are we running within an utf environment?
270 function isutfenv () {
271     case "$LANG $CHARSET $LANGUAGE" in
272         *utf*) return 0 ;;
273         *UTF*) return 0 ;;
274         *)     return 1 ;;
275     esac
276 }
277
278 # check for user, if not running as root set $SUDO to sudo
279 (( EUID != 0 )) && SUDO='sudo' || SUDO=''
280
281 # change directory to home on first invocation of zsh
282 # important for rungetty -> autologin
283 # Thanks go to Bart Schaefer!
284 isgrml && function checkhome () {
285     if [[ -z "$ALREADY_DID_CD_HOME" ]] ; then
286         export ALREADY_DID_CD_HOME=$HOME
287         cd
288     fi
289 }
290
291 # check for zsh v3.1.7+
292
293 if ! [[ ${ZSH_VERSION} == 3.1.<7->*      \
294      || ${ZSH_VERSION} == 3.<2->.<->*    \
295      || ${ZSH_VERSION} == <4->.<->*   ]] ; then
296
297     printf '-!-\n'
298     printf '-!- In this configuration we try to make use of features, that only\n'
299     printf '-!- require version 3.1.7 of the shell; That way this setup can be\n'
300     printf '-!- used with a wide range of zsh versions, while using fairly\n'
301     printf '-!- advanced features in all supported versions.\n'
302     printf '-!-\n'
303     printf '-!- However, you are running zsh version %s.\n' "$ZSH_VERSION"
304     printf '-!-\n'
305     printf '-!- While this *may* work, it might as well fail.\n'
306     printf '-!- Please consider updating to at least version 3.1.7 of zsh.\n'
307     printf '-!-\n'
308     printf '-!- DO NOT EXPECT THIS TO WORK FLAWLESSLY!\n'
309     printf '-!- If it does today, you'\''ve been lucky.\n'
310     printf '-!-\n'
311     printf '-!- Ye been warned!\n'
312     printf '-!-\n'
313
314     function zstyle () { : }
315 fi
316
317 # autoload wrapper - use this one instead of autoload directly
318 # We need to define this function as early as this, because autoloading
319 # 'is-at-least()' needs it.
320 function zrcautoload () {
321     emulate -L zsh
322     setopt extended_glob
323     local fdir ffile
324     local -i ffound
325
326     ffile=$1
327     (( ffound = 0 ))
328     for fdir in ${fpath} ; do
329         [[ -e ${fdir}/${ffile} ]] && (( ffound = 1 ))
330     done
331
332     (( ffound == 0 )) && return 1
333     if [[ $ZSH_VERSION == 3.1.<6-> || $ZSH_VERSION == <4->* ]] ; then
334         autoload -U ${ffile} || return 1
335     else
336         autoload ${ffile} || return 1
337     fi
338     return 0
339 }
340
341 # The following is the â€˜add-zsh-hook’ function from zsh upstream. It is
342 # included here to make the setup work with older versions of zsh (prior to
343 # 4.3.7) in which this function had a bug that triggers annoying errors during
344 # shell startup. This is exactly upstreams code from f0068edb4888a4d8fe94def,
345 # with just a few adjustments in coding style to make the function look more
346 # compact. This definition can be removed as soon as we raise the minimum
347 # version requirement to 4.3.7 or newer.
348 function add-zsh-hook () {
349     # Add to HOOK the given FUNCTION.
350     # HOOK is one of chpwd, precmd, preexec, periodic, zshaddhistory,
351     # zshexit, zsh_directory_name (the _functions subscript is not required).
352     #
353     # With -d, remove the function from the hook instead; delete the hook
354     # variable if it is empty.
355     #
356     # -D behaves like -d, but pattern characters are active in the function
357     # name, so any matching function will be deleted from the hook.
358     #
359     # Without -d, the FUNCTION is marked for autoload; -U is passed down to
360     # autoload if that is given, as are -z and -k. (This is harmless if the
361     # function is actually defined inline.)
362     emulate -L zsh
363     local -a hooktypes
364     hooktypes=(
365         chpwd precmd preexec periodic zshaddhistory zshexit
366         zsh_directory_name
367     )
368     local usage="Usage: $0 hook function\nValid hooks are:\n  $hooktypes"
369     local opt
370     local -a autoopts
371     integer del list help
372     while getopts "dDhLUzk" opt; do
373         case $opt in
374         (d) del=1 ;;
375         (D) del=2 ;;
376         (h) help=1 ;;
377         (L) list=1 ;;
378         ([Uzk]) autoopts+=(-$opt) ;;
379         (*) return 1 ;;
380         esac
381     done
382     shift $(( OPTIND - 1 ))
383     if (( list )); then
384         typeset -mp "(${1:-${(@j:|:)hooktypes}})_functions"
385         return $?
386     elif (( help || $# != 2 || ${hooktypes[(I)$1]} == 0 )); then
387         print -u$(( 2 - help )) $usage
388         return $(( 1 - help ))
389     fi
390     local hook="${1}_functions"
391     local fn="$2"
392     if (( del )); then
393         # delete, if hook is set
394         if (( ${(P)+hook} )); then
395             if (( del == 2 )); then
396                 set -A $hook ${(P)hook:#${~fn}}
397             else
398                 set -A $hook ${(P)hook:#$fn}
399             fi
400             # unset if no remaining entries --- this can give better
401             # performance in some cases
402             if (( ! ${(P)#hook} )); then
403                 unset $hook
404             fi
405         fi
406     else
407         if (( ${(P)+hook} )); then
408             if (( ${${(P)hook}[(I)$fn]} == 0 )); then
409                 set -A $hook ${(P)hook} $fn
410             fi
411         else
412             set -A $hook $fn
413         fi
414         autoload $autoopts -- $fn
415     fi
416 }
417
418 # Load is-at-least() for more precise version checks Note that this test will
419 # *always* fail, if the is-at-least function could not be marked for
420 # autoloading.
421 zrcautoload is-at-least || function is-at-least () { return 1 }
422
423 # set some important options (as early as possible)
424
425 # append history list to the history file; this is the default but we make sure
426 # because it's required for share_history.
427 setopt append_history
428
429 # import new commands from the history file also in other zsh-session
430 is4 && setopt share_history
431
432 # save each command's beginning timestamp and the duration to the history file
433 setopt extended_history
434
435 # remove command lines from the history list when the first character on the
436 # line is a space
437 setopt histignorespace
438
439 # if a command is issued that can't be executed as a normal command, and the
440 # command is the name of a directory, perform the cd command to that directory.
441 setopt auto_cd
442
443 # in order to use #, ~ and ^ for filename generation grep word
444 # *~(*.gz|*.bz|*.bz2|*.zip|*.Z) -> searches for word not in compressed files
445 # don't forget to quote '^', '~' and '#'!
446 setopt extended_glob
447
448 # display PID when suspending processes as well
449 setopt longlistjobs
450
451 # report the status of backgrounds jobs immediately
452 setopt notify
453
454 # whenever a command completion is attempted, make sure the entire command path
455 # is hashed first.
456 setopt hash_list_all
457
458 # not just at the end
459 setopt completeinword
460
461 # Don't send SIGHUP to background processes when the shell exits.
462 setopt nohup
463
464 # make cd push the old directory onto the directory stack.
465 setopt auto_pushd
466
467 # avoid "beep"ing
468 setopt nobeep
469
470 # don't push the same dir twice.
471 setopt pushd_ignore_dups
472
473 # * shouldn't match dotfiles. ever.
474 setopt noglobdots
475
476 # use zsh style word splitting
477 setopt noshwordsplit
478
479 # don't error out when unset parameters are used
480 setopt unset
481
482 # setting some default values
483 NOCOR=${NOCOR:-0}
484 NOMENU=${NOMENU:-0}
485 NOPRECMD=${NOPRECMD:-0}
486 COMMAND_NOT_FOUND=${COMMAND_NOT_FOUND:-0}
487 GRML_ZSH_CNF_HANDLER=${GRML_ZSH_CNF_HANDLER:-/usr/share/command-not-found/command-not-found}
488 GRML_DISPLAY_BATTERY=${GRML_DISPLAY_BATTERY:-${BATTERY:-0}}
489 GRMLSMALL_SPECIFIC=${GRMLSMALL_SPECIFIC:-1}
490 ZSH_NO_DEFAULT_LOCALE=${ZSH_NO_DEFAULT_LOCALE:-0}
491
492 typeset -ga ls_options
493 typeset -ga grep_options
494
495 # Colors on GNU ls(1)
496 if ls --color=auto / >/dev/null 2>&1; then
497     ls_options+=( --color=auto )
498 # Colors on FreeBSD and OSX ls(1)
499 elif ls -G / >/dev/null 2>&1; then
500     ls_options+=( -G )
501 fi
502
503 # Natural sorting order on GNU ls(1)
504 # OSX and IllumOS have a -v option that is not natural sorting
505 if ls --version |& grep -q 'GNU' >/dev/null 2>&1 && ls -v / >/dev/null 2>&1; then
506     ls_options+=( -v )
507 fi
508
509 # Color on GNU and FreeBSD grep(1)
510 if grep --color=auto -q "a" <<< "a" >/dev/null 2>&1; then
511     grep_options+=( --color=auto )
512 fi
513
514 # utility functions
515 # this function checks if a command exists and returns either true
516 # or false. This avoids using 'which' and 'whence', which will
517 # avoid problems with aliases for which on certain weird systems. :-)
518 # Usage: check_com [-c|-g] word
519 #   -c  only checks for external commands
520 #   -g  does the usual tests and also checks for global aliases
521 function check_com () {
522     emulate -L zsh
523     local -i comonly gatoo
524     comonly=0
525     gatoo=0
526
527     if [[ $1 == '-c' ]] ; then
528         comonly=1
529         shift 1
530     elif [[ $1 == '-g' ]] ; then
531         gatoo=1
532         shift 1
533     fi
534
535     if (( ${#argv} != 1 )) ; then
536         printf 'usage: check_com [-c|-g] <command>\n' >&2
537         return 1
538     fi
539
540     if (( comonly > 0 )) ; then
541         (( ${+commands[$1]}  )) && return 0
542         return 1
543     fi
544
545     if     (( ${+commands[$1]}    )) \
546         || (( ${+functions[$1]}   )) \
547         || (( ${+aliases[$1]}     )) \
548         || (( ${+reswords[(r)$1]} )) ; then
549         return 0
550     fi
551
552     if (( gatoo > 0 )) && (( ${+galiases[$1]} )) ; then
553         return 0
554     fi
555
556     return 1
557 }
558
559 # creates an alias and precedes the command with
560 # sudo if $EUID is not zero.
561 function salias () {
562     emulate -L zsh
563     local only=0 ; local multi=0
564     local key val
565     while getopts ":hao" opt; do
566         case $opt in
567             o) only=1 ;;
568             a) multi=1 ;;
569             h)
570                 printf 'usage: salias [-hoa] <alias-expression>\n'
571                 printf '  -h      shows this help text.\n'
572                 printf '  -a      replace '\'' ; '\'' sequences with '\'' ; sudo '\''.\n'
573                 printf '          be careful using this option.\n'
574                 printf '  -o      only sets an alias if a preceding sudo would be needed.\n'
575                 return 0
576                 ;;
577             *) salias -h >&2; return 1 ;;
578         esac
579     done
580     shift "$((OPTIND-1))"
581
582     if (( ${#argv} > 1 )) ; then
583         printf 'Too many arguments %s\n' "${#argv}"
584         return 1
585     fi
586
587     key="${1%%\=*}" ;  val="${1#*\=}"
588     if (( EUID == 0 )) && (( only == 0 )); then
589         alias -- "${key}=${val}"
590     elif (( EUID > 0 )) ; then
591         (( multi > 0 )) && val="${val// ; / ; sudo }"
592         alias -- "${key}=sudo ${val}"
593     fi
594
595     return 0
596 }
597
598 # Check if we can read given files and source those we can.
599 function xsource () {
600     if (( ${#argv} < 1 )) ; then
601         printf 'usage: xsource FILE(s)...\n' >&2
602         return 1
603     fi
604
605     while (( ${#argv} > 0 )) ; do
606         [[ -r "$1" ]] && source "$1"
607         shift
608     done
609     return 0
610 }
611
612 # Check if we can read a given file and 'cat(1)' it.
613 function xcat () {
614     emulate -L zsh
615     if (( ${#argv} != 1 )) ; then
616         printf 'usage: xcat FILE\n' >&2
617         return 1
618     fi
619
620     [[ -r $1 ]] && cat $1
621     return 0
622 }
623
624 # Remove these functions again, they are of use only in these
625 # setup files. This should be called at the end of .zshrc.
626 function xunfunction () {
627     emulate -L zsh
628     local -a funcs
629     local func
630     funcs=(salias xcat xsource xunfunction zrcautoload zrcautozle)
631     for func in $funcs ; do
632         [[ -n ${functions[$func]} ]] \
633             && unfunction $func
634     done
635     return 0
636 }
637
638 # this allows us to stay in sync with grml's zshrc and put own
639 # modifications in ~/.zshrc.local
640 function zrclocal () {
641     xsource "/etc/zsh/zshrc.local"
642     xsource "${ZDOTDIR:-${HOME}}/.zshrc.local"
643     return 0
644 }
645
646 # locale setup
647 if (( ZSH_NO_DEFAULT_LOCALE == 0 )); then
648     xsource "/etc/default/locale"
649 fi
650
651 for var in LANG LC_ALL LC_MESSAGES ; do
652     [[ -n ${(P)var} ]] && export $var
653 done
654 builtin unset -v var
655
656 # set some variables
657 if check_com -c vim ; then
658 #v#
659     export EDITOR=${EDITOR:-vim}
660 else
661     export EDITOR=${EDITOR:-vi}
662 fi
663
664 #v#
665 export PAGER=${PAGER:-less}
666
667 #v#
668 export MAIL=${MAIL:-/var/mail/$USER}
669
670 # color setup for ls:
671 check_com -c dircolors && eval $(dircolors -b)
672 # color setup for ls on OS X / FreeBSD:
673 isdarwin && export CLICOLOR=1
674 isfreebsd && export CLICOLOR=1
675
676 # do MacPorts setup on darwin
677 if isdarwin && [[ -d /opt/local ]]; then
678     # Note: PATH gets set in /etc/zprofile on Darwin, so this can't go into
679     # zshenv.
680     PATH="/opt/local/bin:/opt/local/sbin:$PATH"
681     MANPATH="/opt/local/share/man:$MANPATH"
682 fi
683 # do Fink setup on darwin
684 isdarwin && xsource /sw/bin/init.sh
685
686 # load our function and completion directories
687 for fdir in /usr/share/grml/zsh/completion /usr/share/grml/zsh/functions; do
688     fpath=( ${fdir} ${fdir}/**/*(/N) ${fpath} )
689 done
690 typeset -aU ffiles
691 ffiles=(/usr/share/grml/zsh/functions/**/[^_]*[^~](N.:t))
692 (( ${#ffiles} > 0 )) && autoload -U "${ffiles[@]}"
693 unset -v fdir ffiles
694
695 # support colors in less
696 export LESS_TERMCAP_mb=$'\E[01;31m'
697 export LESS_TERMCAP_md=$'\E[01;31m'
698 export LESS_TERMCAP_me=$'\E[0m'
699 export LESS_TERMCAP_se=$'\E[0m'
700 export LESS_TERMCAP_so=$'\E[01;44;33m'
701 export LESS_TERMCAP_ue=$'\E[0m'
702 export LESS_TERMCAP_us=$'\E[01;32m'
703
704 # mailchecks
705 MAILCHECK=30
706
707 # report about cpu-/system-/user-time of command if running longer than
708 # 5 seconds
709 REPORTTIME=5
710
711 # watch for everyone but me and root
712 watch=(notme root)
713
714 # automatically remove duplicates from these arrays
715 typeset -U path PATH cdpath CDPATH fpath FPATH manpath MANPATH
716
717 # Load a few modules
718 is4 && \
719 for mod in parameter complist deltochar mathfunc ; do
720     zmodload -i zsh/${mod} 2>/dev/null
721     grml_status_feature mod:$mod $?
722 done && builtin unset -v mod
723
724 # autoload zsh modules when they are referenced
725 if is4 ; then
726     zmodload -a  zsh/stat    zstat
727     zmodload -a  zsh/zpty    zpty
728     zmodload -ap zsh/mapfile mapfile
729 fi
730
731 # completion system
732 COMPDUMPFILE=${COMPDUMPFILE:-${ZDOTDIR:-${HOME}}/.zcompdump}
733 if zrcautoload compinit ; then
734     typeset -a tmp
735     zstyle -a ':grml:completion:compinit' arguments tmp
736     compinit -d ${COMPDUMPFILE} "${tmp[@]}"
737     grml_status_feature compinit $?
738     unset tmp
739 else
740     grml_status_feature compinit 1
741     function compdef { }
742 fi
743
744 # completion system
745
746 # called later (via is4 && grmlcomp)
747 # note: use 'zstyle' for getting current settings
748 #         press ^xh (control-x h) for getting tags in context; ^x? (control-x ?) to run complete_debug with trace output
749 function grmlcomp () {
750     # TODO: This could use some additional information
751
752     # Make sure the completion system is initialised
753     (( ${+_comps} )) || return 1
754
755     # allow one error for every three characters typed in approximate completer
756     zstyle ':completion:*:approximate:'    max-errors 'reply=( $((($#PREFIX+$#SUFFIX)/3 )) numeric )'
757
758     # don't complete backup files as executables
759     zstyle ':completion:*:complete:-command-::commands' ignored-patterns '(aptitude-*|*\~)'
760
761     # start menu completion only if it could find no unambiguous initial string
762     zstyle ':completion:*:correct:*'       insert-unambiguous true
763     zstyle ':completion:*:corrections'     format $'%{\e[0;31m%}%d (errors: %e)%{\e[0m%}'
764     zstyle ':completion:*:correct:*'       original true
765
766     # activate color-completion
767     zstyle ':completion:*:default'         list-colors ${(s.:.)LS_COLORS}
768
769     # format on completion
770     zstyle ':completion:*:descriptions'    format $'%{\e[0;31m%}completing %B%d%b%{\e[0m%}'
771
772     # automatically complete 'cd -<tab>' and 'cd -<ctrl-d>' with menu
773     # zstyle ':completion:*:*:cd:*:directory-stack' menu yes select
774
775     # insert all expansions for expand completer
776     zstyle ':completion:*:expand:*'        tag-order all-expansions
777     zstyle ':completion:*:history-words'   list false
778
779     # activate menu
780     zstyle ':completion:*:history-words'   menu yes
781
782     # ignore duplicate entries
783     zstyle ':completion:*:history-words'   remove-all-dups yes
784     zstyle ':completion:*:history-words'   stop yes
785
786     # match uppercase from lowercase
787     zstyle ':completion:*'                 matcher-list 'm:{a-z}={A-Z}'
788
789     # separate matches into groups
790     zstyle ':completion:*:matches'         group 'yes'
791     zstyle ':completion:*'                 group-name ''
792
793     if [[ "$NOMENU" -eq 0 ]] ; then
794         # if there are more than 5 options allow selecting from a menu
795         zstyle ':completion:*'               menu select=5
796     else
797         # don't use any menus at all
798         setopt no_auto_menu
799     fi
800
801     zstyle ':completion:*:messages'        format '%d'
802     zstyle ':completion:*:options'         auto-description '%d'
803
804     # describe options in full
805     zstyle ':completion:*:options'         description 'yes'
806
807     # on processes completion complete all user processes
808     zstyle ':completion:*:processes'       command 'ps -au$USER'
809
810     # offer indexes before parameters in subscripts
811     zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters
812
813     # provide verbose completion information
814     zstyle ':completion:*'                 verbose true
815
816     # recent (as of Dec 2007) zsh versions are able to provide descriptions
817     # for commands (read: 1st word in the line) that it will list for the user
818     # to choose from. The following disables that, because it's not exactly fast.
819     zstyle ':completion:*:-command-:*:'    verbose false
820
821     # set format for warnings
822     zstyle ':completion:*:warnings'        format $'%{\e[0;31m%}No matches for:%{\e[0m%} %d'
823
824     # define files to ignore for zcompile
825     zstyle ':completion:*:*:zcompile:*'    ignored-patterns '(*~|*.zwc)'
826     zstyle ':completion:correct:'          prompt 'correct to: %e'
827
828     # Ignore completion functions for commands you don't have:
829     zstyle ':completion::(^approximate*):*:functions' ignored-patterns '_*'
830
831     # Provide more processes in completion of programs like killall:
832     zstyle ':completion:*:processes-names' command 'ps c -u ${USER} -o command | uniq'
833
834     # complete manual by their section
835     zstyle ':completion:*:manuals'    separate-sections true
836     zstyle ':completion:*:manuals.*'  insert-sections   true
837     zstyle ':completion:*:man:*'      menu yes select
838
839     # Search path for sudo completion
840     zstyle ':completion:*:sudo:*' command-path /usr/local/sbin \
841                                                /usr/local/bin  \
842                                                /usr/sbin       \
843                                                /usr/bin        \
844                                                /sbin           \
845                                                /bin            \
846                                                /usr/X11R6/bin
847
848     # provide .. as a completion
849     zstyle ':completion:*' special-dirs ..
850
851     # run rehash on completion so new installed program are found automatically:
852     function _force_rehash () {
853         (( CURRENT == 1 )) && rehash
854         return 1
855     }
856
857     ## correction
858     # some people don't like the automatic correction - so run 'NOCOR=1 zsh' to deactivate it
859     if [[ "$NOCOR" -gt 0 ]] ; then
860         zstyle ':completion:*' completer _oldlist _expand _force_rehash _complete _files _ignored
861         setopt nocorrect
862     else
863         # try to be smart about when to use what completer...
864         setopt correct
865         zstyle -e ':completion:*' completer '
866             if [[ $_last_try != "$HISTNO$BUFFER$CURSOR" ]] ; then
867                 _last_try="$HISTNO$BUFFER$CURSOR"
868                 reply=(_complete _match _ignored _prefix _files)
869             else
870                 if [[ $words[1] == (rm|mv) ]] ; then
871                     reply=(_complete _files)
872                 else
873                     reply=(_oldlist _expand _force_rehash _complete _ignored _correct _approximate _files)
874                 fi
875             fi'
876     fi
877
878     # command for process lists, the local web server details and host completion
879     zstyle ':completion:*:urls' local 'www' '/var/www/' 'public_html'
880
881     # Some functions, like _apt and _dpkg, are very slow. We can use a cache in
882     # order to speed things up
883     if [[ ${GRML_COMP_CACHING:-yes} == yes ]]; then
884         GRML_COMP_CACHE_DIR=${GRML_COMP_CACHE_DIR:-${ZDOTDIR:-$HOME}/.cache}
885         if [[ ! -d ${GRML_COMP_CACHE_DIR} ]]; then
886             command mkdir -p "${GRML_COMP_CACHE_DIR}"
887         fi
888         zstyle ':completion:*' use-cache  yes
889         zstyle ':completion:*:complete:*' cache-path "${GRML_COMP_CACHE_DIR}"
890     fi
891
892     # host completion
893     if is42 ; then
894         [[ -r ~/.ssh/config ]] && _ssh_config_hosts=(${${(s: :)${(ps:\t:)${${(@M)${(f)"$(<$HOME/.ssh/config)"}:#Host *}#Host }}}:#*[*?]*}) || _ssh_config_hosts=()
895         [[ -r ~/.ssh/known_hosts ]] && _ssh_hosts=(${${${${(f)"$(<$HOME/.ssh/known_hosts)"}:#[\|]*}%%\ *}%%,*}) || _ssh_hosts=()
896         [[ -r /etc/hosts ]] && : ${(A)_etc_hosts:=${(s: :)${(ps:\t:)${${(f)~~"$(</etc/hosts)"}%%\#*}##[:blank:]#[^[:blank:]]#}}} || _etc_hosts=()
897     else
898         _ssh_config_hosts=()
899         _ssh_hosts=()
900         _etc_hosts=()
901     fi
902
903     local localname
904     if check_com hostname ; then
905       localname=$(hostname)
906     elif check_com hostnamectl ; then
907       localname=$(hostnamectl --static)
908     else
909       localname="$(uname -n)"
910     fi
911
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)
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)
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 if [[ -r /proc/mdstat ]]; then
2687     alias mdstat='cat /proc/mdstat'
2688 fi
2689
2690 alias ...='cd ../../'
2691
2692 # generate alias named "$KERNELVERSION-reboot" so you can use boot with kexec:
2693 if [[ -x /sbin/kexec ]] && [[ -r /proc/cmdline ]] ; then
2694     alias "$(uname -r)-reboot"="kexec -l --initrd=/boot/initrd.img-"$(uname -r)" --command-line=\"$(cat /proc/cmdline)\" /boot/vmlinuz-"$(uname -r)""
2695 fi
2696
2697 # see http://www.cl.cam.ac.uk/~mgk25/unicode.html#term for details
2698 alias term2iso="echo 'Setting terminal to iso mode' ; print -n '\e%@'"
2699 alias term2utf="echo 'Setting terminal to utf-8 mode'; print -n '\e%G'"
2700
2701 # make sure it is not assigned yet
2702 [[ -n ${aliases[utf2iso]} ]] && unalias utf2iso
2703 function utf2iso () {
2704     if isutfenv ; then
2705         local ENV
2706         for ENV in $(env | command grep -i '.utf') ; do
2707             eval export "$(echo $ENV | sed 's/UTF-8/iso885915/ ; s/utf8/iso885915/')"
2708         done
2709     fi
2710 }
2711
2712 # make sure it is not assigned yet
2713 [[ -n ${aliases[iso2utf]} ]] && unalias iso2utf
2714 function iso2utf () {
2715     if ! isutfenv ; then
2716         local ENV
2717         for ENV in $(env | command grep -i '\.iso') ; do
2718             eval export "$(echo $ENV | sed 's/iso.*/UTF-8/ ; s/ISO.*/UTF-8/')"
2719         done
2720     fi
2721 }
2722
2723 # especially for roadwarriors using GNU screen and ssh:
2724 if ! check_com asc &>/dev/null ; then
2725   function asc () { autossh -t "$@" 'screen -RdU' }
2726   compdef asc=ssh
2727 fi
2728
2729 #f1# Hints for the use of zsh on grml
2730 function zsh-help () {
2731     print "$bg[white]$fg[black]
2732 zsh-help - hints for use of zsh on grml
2733 =======================================$reset_color"
2734
2735     print '
2736 Main configuration of zsh happens in /etc/zsh/zshrc.
2737 That file is part of the package grml-etc-core, if you want to
2738 use them on a non-grml-system just get the tar.gz from
2739 http://deb.grml.org/ or (preferably) get it from the git repository:
2740
2741   http://git.grml.org/f/grml-etc-core/etc/zsh/zshrc
2742
2743 This version of grml'\''s zsh setup does not use skel/.zshrc anymore.
2744 The file is still there, but it is empty for backwards compatibility.
2745
2746 For your own changes use these two files:
2747     $HOME/.zshrc.pre
2748     $HOME/.zshrc.local
2749
2750 The former is sourced very early in our zshrc, the latter is sourced
2751 very lately.
2752
2753 System wide configuration without touching configuration files of grml
2754 can take place in /etc/zsh/zshrc.local.
2755
2756 For information regarding zsh start at http://grml.org/zsh/
2757
2758 Take a look at grml'\''s zsh refcard:
2759 % xpdf =(zcat /usr/share/doc/grml-docs/zsh/grml-zsh-refcard.pdf.gz)
2760
2761 Check out the main zsh refcard:
2762 % '$BROWSER' http://www.bash2zsh.com/zsh_refcard/refcard.pdf
2763
2764 And of course visit the zsh-lovers:
2765 % man zsh-lovers
2766
2767 You can adjust some options through environment variables when
2768 invoking zsh without having to edit configuration files.
2769 Basically meant for bash users who are not used to the power of
2770 the zsh yet. :)
2771
2772   "NOCOR=1    zsh" => deactivate automatic correction
2773   "NOMENU=1   zsh" => do not use auto menu completion
2774                       (note: use ctrl-d for completion instead!)
2775   "NOPRECMD=1 zsh" => disable the precmd + preexec commands (set GNU screen title)
2776   "NOTITLE=1  zsh" => disable setting the title of xterms without disabling
2777                       preexec() and precmd() completely
2778   "GRML_DISPLAY_BATTERY=1  zsh"
2779                    => activate battery status on right side of prompt (WIP)
2780   "COMMAND_NOT_FOUND=1 zsh"
2781                    => Enable a handler if an external command was not found
2782                       The command called in the handler can be altered by setting
2783                       the GRML_ZSH_CNF_HANDLER variable, the default is:
2784                       "/usr/share/command-not-found/command-not-found"
2785
2786 A value greater than 0 is enables a feature; a value equal to zero
2787 disables it. If you like one or the other of these settings, you can
2788 add them to ~/.zshrc.pre to ensure they are set when sourcing grml'\''s
2789 zshrc.'
2790
2791     print "
2792 $bg[white]$fg[black]
2793 Please report wishes + bugs to the grml-team: http://grml.org/bugs/
2794 Enjoy your grml system with the zsh!$reset_color"
2795 }
2796
2797 # debian stuff
2798 if [[ -r /etc/debian_version ]] ; then
2799     if [[ -z "$GRML_NO_APT_ALIASES" ]]; then
2800         #a3# Execute \kbd{apt-cache policy}
2801         alias acp='apt-cache policy'
2802         if check_com -c apt ; then
2803           #a3# Execute \kbd{apt search}
2804           alias acs='apt search'
2805           #a3# Execute \kbd{apt show}
2806           alias acsh='apt show'
2807           #a3# Execute \kbd{apt dist-upgrade}
2808           salias adg="apt dist-upgrade"
2809           #a3# Execute \kbd{apt upgrade}
2810           salias ag="apt upgrade"
2811           #a3# Execute \kbd{apt install}
2812           salias agi="apt install"
2813           #a3# Execute \kbd{apt update}
2814           salias au="apt update"
2815         else
2816           alias acs='apt-cache search'
2817           alias acsh='apt-cache show'
2818           salias adg="apt-get dist-upgrade"
2819           salias ag="apt-get upgrade"
2820           salias agi="apt-get install"
2821           salias au="apt-get update"
2822         fi
2823         #a3# Execute \kbd{aptitude install}
2824         salias ati="aptitude install"
2825         #a3# Execute \kbd{aptitude update ; aptitude safe-upgrade}
2826         salias -a up="aptitude update ; aptitude safe-upgrade"
2827         #a3# Execute \kbd{dpkg-buildpackage}
2828         alias dbp='dpkg-buildpackage'
2829         #a3# Execute \kbd{grep-excuses}
2830         alias ge='grep-excuses'
2831     fi
2832
2833     # get a root shell as normal user in live-cd mode:
2834     if isgrmlcd && [[ $UID -ne 0 ]] ; then
2835        alias su="sudo su"
2836     fi
2837
2838 fi
2839
2840 # use /var/log/syslog iff present, fallback to journalctl otherwise
2841 if [ -e /var/log/syslog ] ; then
2842   #a1# Take a look at the syslog: \kbd{\$PAGER /var/log/syslog || journalctl}
2843   salias llog="$PAGER /var/log/syslog"     # take a look at the syslog
2844   #a1# Take a look at the syslog: \kbd{tail -f /var/log/syslog || journalctl}
2845   salias tlog="tail -f /var/log/syslog"    # follow the syslog
2846 elif check_com -c journalctl ; then
2847   salias llog="journalctl"
2848   salias tlog="journalctl -f"
2849 fi
2850
2851 # sort installed Debian-packages by size
2852 if check_com -c dpkg-query ; then
2853     #a3# List installed Debian-packages sorted by size
2854     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"
2855 fi
2856
2857 # if cdrecord is a symlink (to wodim) or isn't present at all warn:
2858 if [[ -L /usr/bin/cdrecord ]] || ! check_com -c cdrecord; then
2859     if check_com -c wodim; then
2860         function cdrecord () {
2861             <<__EOF0__
2862 cdrecord is not provided under its original name by Debian anymore.
2863 See #377109 in the BTS of Debian for more details.
2864
2865 Please use the wodim binary instead
2866 __EOF0__
2867             return 1
2868         }
2869     fi
2870 fi
2871
2872 if isgrmlcd; then
2873     # No core dumps: important for a live-cd-system
2874     limit -s core 0
2875 fi
2876
2877 # grmlstuff
2878 function grmlstuff () {
2879 # people should use 'grml-x'!
2880     if check_com -c 915resolution; then
2881         function 855resolution () {
2882             echo "Please use 915resolution as resolution modifying tool for Intel \
2883 graphic chipset."
2884             return -1
2885         }
2886     fi
2887
2888     #a1# Output version of running grml
2889     alias grml-version='cat /etc/grml_version'
2890
2891     if check_com -c grml-debootstrap ; then
2892         function debian2hd () {
2893             echo "Installing debian to harddisk is possible by using grml-debootstrap."
2894             return 1
2895         }
2896     fi
2897
2898     if check_com -c tmate && check_com -c qrencode ; then
2899         function grml-remote-support() {
2900             tmate -L grml-remote-support new -s grml-remote-support -d
2901             tmate -L grml-remote-support wait tmate-ready
2902             tmate -L grml-remote-support display -p '#{tmate_ssh}' | qrencode -t ANSI
2903             echo "tmate session: $(tmate -L grml-remote-support display -p '#{tmate_ssh}')"
2904             echo
2905             echo "Scan this QR code and send it to your support team."
2906         }
2907     fi
2908 }
2909
2910 # now run the functions
2911 isgrml && checkhome
2912 is4    && isgrml    && grmlstuff
2913 is4    && grmlcomp
2914
2915 # keephack
2916 is4 && xsource "/etc/zsh/keephack"
2917
2918 # wonderful idea of using "e" glob qualifier by Peter Stephenson
2919 # You use it as follows:
2920 # $ NTREF=/reference/file
2921 # $ ls -l *(e:nt:)
2922 # This lists all the files in the current directory newer than the reference file.
2923 # You can also specify the reference file inline; note quotes:
2924 # $ ls -l *(e:'nt ~/.zshenv':)
2925 is4 && function nt () {
2926     if [[ -n $1 ]] ; then
2927         local NTREF=${~1}
2928     fi
2929     [[ $REPLY -nt $NTREF ]]
2930 }
2931
2932 # shell functions
2933
2934 #f1# Reload an autoloadable function
2935 function freload () { while (( $# )); do; unfunction $1; autoload -U $1; shift; done }
2936 compdef _functions freload
2937
2938 #
2939 # Usage:
2940 #
2941 #      e.g.:   a -> b -> c -> d  ....
2942 #
2943 #      sll a
2944 #
2945 #
2946 #      if parameter is given with leading '=', lookup $PATH for parameter and resolve that
2947 #
2948 #      sll =java
2949 #
2950 #      Note: limit for recursive symlinks on linux:
2951 #            http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/namei.c?id=refs/heads/master#l808
2952 #            This limits recursive symlink follows to 8,
2953 #            while limiting consecutive symlinks to 40.
2954 #
2955 #      When resolving and displaying information about symlinks, no check is made
2956 #      that the displayed information does make any sense on your OS.
2957 #      We leave that decission to the user.
2958 #
2959 #      The zstat module is used to detect symlink loops. zstat is available since zsh4.
2960 #      With an older zsh you will need to abort with <C-c> in that case.
2961 #      When a symlink loop is detected, a warning ist printed and further processing is stopped.
2962 #
2963 #      Module zstat is loaded by default in grml zshrc, no extra action needed for that.
2964 #
2965 #      Known bugs:
2966 #      If you happen to come across a symlink that points to a destination on another partition
2967 #      with the same inode number, that will be marked as symlink loop though it is not.
2968 #      Two hints for this situation:
2969 #      I)  Play lottery the same day, as you seem to be rather lucky right now.
2970 #      II) Send patches.
2971 #
2972 #      return status:
2973 #      0 upon success
2974 #      1 file/dir not accesible
2975 #      2 symlink loop detected
2976 #
2977 #f1# List symlinks in detail (more detailed version of 'readlink -f', 'whence -s' and 'namei -l')
2978 function sll () {
2979     if [[ -z ${1} ]] ; then
2980         printf 'Usage: %s <symlink(s)>\n' "${0}"
2981         return 1
2982     fi
2983
2984     local file jumpd curdir
2985     local -i 10 RTN LINODE i
2986     local -a    SEENINODES
2987     curdir="${PWD}"
2988     RTN=0
2989
2990     for file in "${@}" ; do
2991         SEENINODES=()
2992         ls -l "${file:a}"   || RTN=1
2993
2994         while [[ -h "$file" ]] ; do
2995             if is4 ; then
2996                 LINODE=$(zstat -L +inode "${file}")
2997                 for i in ${SEENINODES} ; do
2998                     if (( ${i} == ${LINODE} )) ; then
2999                         builtin cd -q "${curdir}"
3000                         print 'link loop detected, aborting!'
3001                         return 2
3002                     fi
3003                 done
3004                 SEENINODES+=${LINODE}
3005             fi
3006             jumpd="${file:h}"
3007             file="${file:t}"
3008
3009             if [[ -d ${jumpd} ]] ; then
3010                 builtin cd -q "${jumpd}"  || RTN=1
3011             fi
3012             file=$(readlink "$file")
3013
3014             jumpd="${file:h}"
3015             file="${file:t}"
3016
3017             if [[ -d ${jumpd} ]] ; then
3018                 builtin cd -q "${jumpd}"  || RTN=1
3019             fi
3020
3021             ls -l "${PWD}/${file}"     || RTN=1
3022         done
3023         shift 1
3024         if (( ${#} >= 1 )) ; then
3025             print ""
3026         fi
3027         builtin cd -q "${curdir}"
3028     done
3029     return ${RTN}
3030 }
3031
3032 if check_com -c $PAGER ; then
3033     #f3# View Debian's changelog of given package(s)
3034     function dchange () {
3035         emulate -L zsh
3036         [[ -z "$1" ]] && printf 'Usage: %s <package_name(s)>\n' "$0" && return 1
3037
3038         local package
3039
3040         # `less` as $PAGER without e.g. `|lesspipe %s` inside $LESSOPEN can't properly
3041         # read *.gz files, try to detect this to use vi instead iff available
3042         local viewer
3043
3044         if [[ ${$(typeset -p PAGER)[2]} = -a ]] ; then
3045           viewer=($PAGER)    # support PAGER=(less -Mr) but leave array untouched
3046         else
3047           viewer=(${=PAGER}) # support PAGER='less -Mr'
3048         fi
3049
3050         if [[ ${viewer[1]:t} = less ]] && [[ -z "${LESSOPEN}" ]] && check_com vi ; then
3051           viewer='vi'
3052         fi
3053
3054         for package in "$@" ; do
3055             if [[ -r /usr/share/doc/${package}/changelog.Debian.gz ]] ; then
3056                 $viewer /usr/share/doc/${package}/changelog.Debian.gz
3057             elif [[ -r /usr/share/doc/${package}/changelog.gz ]] ; then
3058                 $viewer /usr/share/doc/${package}/changelog.gz
3059             elif [[ -r /usr/share/doc/${package}/changelog ]] ; then
3060                 $viewer /usr/share/doc/${package}/changelog
3061             else
3062                 if check_com -c aptitude ; then
3063                     echo "No changelog for package $package found, using aptitude to retrieve it."
3064                     aptitude changelog "$package"
3065                 elif check_com -c apt-get ; then
3066                     echo "No changelog for package $package found, using apt-get to retrieve it."
3067                     apt-get changelog "$package"
3068                 else
3069                     echo "No changelog for package $package found, sorry."
3070                 fi
3071             fi
3072         done
3073     }
3074     function _dchange () { _files -W /usr/share/doc -/ }
3075     compdef _dchange dchange
3076
3077     #f3# View Debian's NEWS of a given package
3078     function dnews () {
3079         emulate -L zsh
3080         if [[ -r /usr/share/doc/$1/NEWS.Debian.gz ]] ; then
3081             $PAGER /usr/share/doc/$1/NEWS.Debian.gz
3082         else
3083             if [[ -r /usr/share/doc/$1/NEWS.gz ]] ; then
3084                 $PAGER /usr/share/doc/$1/NEWS.gz
3085             else
3086                 echo "No NEWS file for package $1 found, sorry."
3087                 return 1
3088             fi
3089         fi
3090     }
3091     function _dnews () { _files -W /usr/share/doc -/ }
3092     compdef _dnews dnews
3093
3094     #f3# View Debian's copyright of a given package
3095     function dcopyright () {
3096         emulate -L zsh
3097         if [[ -r /usr/share/doc/$1/copyright ]] ; then
3098             $PAGER /usr/share/doc/$1/copyright
3099         else
3100             echo "No copyright file for package $1 found, sorry."
3101             return 1
3102         fi
3103     }
3104     function _dcopyright () { _files -W /usr/share/doc -/ }
3105     compdef _dcopyright dcopyright
3106
3107     #f3# View upstream's changelog of a given package
3108     function uchange () {
3109         emulate -L zsh
3110         if [[ -r /usr/share/doc/$1/changelog.gz ]] ; then
3111             $PAGER /usr/share/doc/$1/changelog.gz
3112         else
3113             echo "No changelog for package $1 found, sorry."
3114             return 1
3115         fi
3116     }
3117     function _uchange () { _files -W /usr/share/doc -/ }
3118     compdef _uchange uchange
3119 fi
3120
3121 # zsh profiling
3122 function profile () {
3123     ZSH_PROFILE_RC=1 zsh "$@"
3124 }
3125
3126 #f1# Edit an alias via zle
3127 function edalias () {
3128     [[ -z "$1" ]] && { echo "Usage: edalias <alias_to_edit>" ; return 1 } || vared aliases'[$1]' ;
3129 }
3130 compdef _aliases edalias
3131
3132 #f1# Edit a function via zle
3133 function edfunc () {
3134     [[ -z "$1" ]] && { echo "Usage: edfunc <function_to_edit>" ; return 1 } || zed -f "$1" ;
3135 }
3136 compdef _functions edfunc
3137
3138 # use it e.g. via 'Restart apache2'
3139 #m# f6 Start() \kbd{service \em{process}}\quad\kbd{start}
3140 #m# f6 Restart() \kbd{service \em{process}}\quad\kbd{restart}
3141 #m# f6 Stop() \kbd{service \em{process}}\quad\kbd{stop}
3142 #m# f6 Reload() \kbd{service \em{process}}\quad\kbd{reload}
3143 #m# f6 Force-Reload() \kbd{service \em{process}}\quad\kbd{force-reload}
3144 #m# f6 Status() \kbd{service \em{process}}\quad\kbd{status}
3145 if [[ -d /etc/init.d || -d /etc/service ]] ; then
3146     function __start_stop () {
3147         local action_="${1:l}"  # e.g Start/Stop/Restart
3148         local service_="$2"
3149         local param_="$3"
3150
3151         local service_target_="$(readlink /etc/init.d/$service_)"
3152         if [[ $service_target_ == "/usr/bin/sv" ]]; then
3153             # runit
3154             case "${action_}" in
3155                 start) if [[ ! -e /etc/service/$service_ ]]; then
3156                            $SUDO ln -s "/etc/sv/$service_" "/etc/service/"
3157                        else
3158                            $SUDO "/etc/init.d/$service_" "${action_}" "$param_"
3159                        fi ;;
3160                 # there is no reload in runits sysv emulation
3161                 reload) $SUDO "/etc/init.d/$service_" "force-reload" "$param_" ;;
3162                 *) $SUDO "/etc/init.d/$service_" "${action_}" "$param_" ;;
3163             esac
3164         else
3165             # sysv/sysvinit-utils, upstart
3166             if check_com -c service ; then
3167               $SUDO service "$service_" "${action_}" "$param_"
3168             else
3169               $SUDO "/etc/init.d/$service_" "${action_}" "$param_"
3170             fi
3171         fi
3172     }
3173
3174     function _grmlinitd () {
3175         local -a scripts
3176         scripts=( /etc/init.d/*(x:t) )
3177         _describe "service startup script" scripts
3178     }
3179
3180     for i in Start Restart Stop Force-Reload Reload Status ; do
3181         eval "function $i () { __start_stop $i \"\$1\" \"\$2\" ; }"
3182         compdef _grmlinitd $i
3183     done
3184     builtin unset -v i
3185 fi
3186
3187 #f1# Provides useful information on globbing
3188 function H-Glob () {
3189     echo -e "
3190     /      directories
3191     .      plain files
3192     @      symbolic links
3193     =      sockets
3194     p      named pipes (FIFOs)
3195     *      executable plain files (0100)
3196     %      device files (character or block special)
3197     %b     block special files
3198     %c     character special files
3199     r      owner-readable files (0400)
3200     w      owner-writable files (0200)
3201     x      owner-executable files (0100)
3202     A      group-readable files (0040)
3203     I      group-writable files (0020)
3204     E      group-executable files (0010)
3205     R      world-readable files (0004)
3206     W      world-writable files (0002)
3207     X      world-executable files (0001)
3208     s      setuid files (04000)
3209     S      setgid files (02000)
3210     t      files with the sticky bit (01000)
3211
3212   print *(m-1)          # Files modified up to a day ago
3213   print *(a1)           # Files accessed a day ago
3214   print *(@)            # Just symlinks
3215   print *(Lk+50)        # Files bigger than 50 kilobytes
3216   print *(Lk-50)        # Files smaller than 50 kilobytes
3217   print **/*.c          # All *.c files recursively starting in \$PWD
3218   print **/*.c~file.c   # Same as above, but excluding 'file.c'
3219   print (foo|bar).*     # Files starting with 'foo' or 'bar'
3220   print *~*.*           # All Files that do not contain a dot
3221   chmod 644 *(.^x)      # make all plain non-executable files publically readable
3222   print -l *(.c|.h)     # Lists *.c and *.h
3223   print **/*(g:users:)  # Recursively match all files that are owned by group 'users'
3224   echo /proc/*/cwd(:h:t:s/self//) # Analogous to >ps ax | awk '{print $1}'<"
3225 }
3226 alias help-zshglob=H-Glob
3227
3228 # grep for running process, like: 'any vim'
3229 function any () {
3230     emulate -L zsh
3231     unsetopt KSH_ARRAYS
3232     if [[ -z "$1" ]] ; then
3233         echo "any - grep for process(es) by keyword" >&2
3234         echo "Usage: any <keyword>" >&2 ; return 1
3235     else
3236         ps xauwww | grep -i "${grep_options[@]}" "[${1[1]}]${1[2,-1]}"
3237     fi
3238 }
3239
3240
3241 # After resuming from suspend, system is paging heavily, leading to very bad interactivity.
3242 # taken from $LINUX-KERNELSOURCE/Documentation/power/swsusp.txt
3243 [[ -r /proc/1/maps ]] && \
3244 function deswap () {
3245     print 'Reading /proc/[0-9]*/maps and sending output to /dev/null, this might take a while.'
3246     cat $(sed -ne 's:.* /:/:p' /proc/[0-9]*/maps | sort -u | grep -v '^/dev/')  > /dev/null
3247     print 'Finished, running "swapoff -a; swapon -a" may also be useful.'
3248 }
3249
3250 # a wrapper for vim, that deals with title setting
3251 #   VIM_OPTIONS
3252 #       set this array to a set of options to vim you always want
3253 #       to have set when calling vim (in .zshrc.local), like:
3254 #           VIM_OPTIONS=( -p )
3255 #       This will cause vim to send every file given on the
3256 #       commandline to be send to it's own tab (needs vim7).
3257 if check_com vim; then
3258     function vim () {
3259         VIM_PLEASE_SET_TITLE='yes' command vim ${VIM_OPTIONS} "$@"
3260     }
3261 fi
3262
3263 ssl_hashes=( sha512 sha256 sha1 md5 )
3264
3265 for sh in ${ssl_hashes}; do
3266     eval 'ssl-cert-'${sh}'() {
3267         emulate -L zsh
3268         if [[ -z $1 ]] ; then
3269             printf '\''usage: %s <file>\n'\'' "ssh-cert-'${sh}'"
3270             return 1
3271         fi
3272         openssl x509 -noout -fingerprint -'${sh}' -in $1
3273     }'
3274 done; unset sh
3275
3276 function ssl-cert-fingerprints () {
3277     emulate -L zsh
3278     local i
3279     if [[ -z $1 ]] ; then
3280         printf 'usage: ssl-cert-fingerprints <file>\n'
3281         return 1
3282     fi
3283     for i in ${ssl_hashes}
3284         do ssl-cert-$i $1;
3285     done
3286 }
3287
3288 function ssl-cert-info () {
3289     emulate -L zsh
3290     if [[ -z $1 ]] ; then
3291         printf 'usage: ssl-cert-info <file>\n'
3292         return 1
3293     fi
3294     openssl x509 -noout -text -in $1
3295     ssl-cert-fingerprints $1
3296 }
3297
3298 # make sure our environment is clean regarding colors
3299 builtin unset -v BLUE RED GREEN CYAN YELLOW MAGENTA WHITE NO_COLOR
3300
3301 # "persistent history"
3302 # just write important commands you always need to $GRML_IMPORTANT_COMMANDS
3303 # defaults for backward compatibility to ~/.important_commands
3304 if [[ -r ~/.important_commands ]] ; then
3305     GRML_IMPORTANT_COMMANDS=~/.important_commands
3306 else
3307     GRML_IMPORTANT_COMMANDS=${GRML_IMPORTANT_COMMANDS:-${ZDOTDIR:-${HOME}}/.important_commands}
3308 fi
3309 [[ -r ${GRML_IMPORTANT_COMMANDS} ]] && builtin fc -R ${GRML_IMPORTANT_COMMANDS}
3310
3311 # load the lookup subsystem if it's available on the system
3312 zrcautoload lookupinit && lookupinit
3313
3314 # variables
3315
3316 # set terminal property (used e.g. by msgid-chooser)
3317 export COLORTERM="yes"
3318
3319 # aliases
3320
3321 # general
3322 #a2# Execute \kbd{du -sch}
3323 [[ -n "$GRML_NO_SMALL_ALIASES" ]] || alias da='du -sch'
3324
3325 # listing stuff
3326 #a2# Execute \kbd{ls -lSrah}
3327 alias dir="command ls -lSrah"
3328 #a2# Only show dot-directories
3329 alias lad='command ls -d .*(/)'
3330 #a2# Only show dot-files
3331 alias lsa='command ls -a .*(.)'
3332 #a2# Only files with setgid/setuid/sticky flag
3333 alias lss='command ls -l *(s,S,t)'
3334 #a2# Only show symlinks
3335 alias lsl='command ls -l *(@)'
3336 #a2# Display only executables
3337 alias lsx='command ls -l *(*)'
3338 #a2# Display world-{readable,writable,executable} files
3339 alias lsw='command ls -ld *(R,W,X.^ND/)'
3340 #a2# Display the ten biggest files
3341 alias lsbig="command ls -flh *(.OL[1,10])"
3342 #a2# Only show directories
3343 alias lsd='command ls -d *(/)'
3344 #a2# Only show empty directories
3345 alias lse='command ls -d *(/^F)'
3346 #a2# Display the ten newest files
3347 alias lsnew="command ls -rtlh *(D.om[1,10])"
3348 #a2# Display the ten oldest files
3349 alias lsold="command ls -rtlh *(D.Om[1,10])"
3350 #a2# Display the ten smallest files
3351 alias lssmall="command ls -Srl *(.oL[1,10])"
3352 #a2# Display the ten newest directories and ten newest .directories
3353 alias lsnewdir="command ls -rthdl *(/om[1,10]) .*(D/om[1,10])"
3354 #a2# Display the ten oldest directories and ten oldest .directories
3355 alias lsolddir="command ls -rthdl *(/Om[1,10]) .*(D/Om[1,10])"
3356
3357 # some useful aliases
3358 #a2# Remove current empty directory. Execute \kbd{cd ..; rmdir \$OLDCWD}
3359 alias rmcdir='cd ..; rmdir $OLDPWD || cd $OLDPWD'
3360
3361 #a2# ssh with StrictHostKeyChecking=no \\&\quad and UserKnownHostsFile unset
3362 alias insecssh='ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null"'
3363 #a2# scp with StrictHostKeyChecking=no \\&\quad and UserKnownHostsFile unset
3364 alias insecscp='scp -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null"'
3365
3366 # work around non utf8 capable software in utf environment via $LANG and luit
3367 if check_com isutfenv && check_com luit ; then
3368     if check_com -c mrxvt ; then
3369         isutfenv && [[ -n "$LANG" ]] && \
3370             alias mrxvt="LANG=${LANG/(#b)(*)[.@]*/$match[1].iso885915} luit mrxvt"
3371     fi
3372
3373     if check_com -c aterm ; then
3374         isutfenv && [[ -n "$LANG" ]] && \
3375             alias aterm="LANG=${LANG/(#b)(*)[.@]*/$match[1].iso885915} luit aterm"
3376     fi
3377
3378     if check_com -c centericq ; then
3379         isutfenv && [[ -n "$LANG" ]] && \
3380             alias centericq="LANG=${LANG/(#b)(*)[.@]*/$match[1].iso885915} luit centericq"
3381     fi
3382 fi
3383
3384 # useful functions
3385
3386 #f5# Backup \kbd{file_or_folder {\rm to} file_or_folder\_timestamp}
3387 function bk () {
3388     emulate -L zsh
3389     local current_date=$(date -u "+%Y%m%dT%H%M%SZ")
3390     local clean keep move verbose result all to_bk
3391     setopt extended_glob
3392     keep=1
3393     while getopts ":hacmrv" opt; do
3394         case $opt in
3395             a) (( all++ ));;
3396             c) unset move clean && (( ++keep ));;
3397             m) unset keep clean && (( ++move ));;
3398             r) unset move keep && (( ++clean ));;
3399             v) verbose="-v";;
3400             h) <<__EOF0__
3401 bk [-hcmv] FILE [FILE ...]
3402 bk -r [-av] [FILE [FILE ...]]
3403 Backup a file or folder in place and append the timestamp
3404 Remove backups of a file or folder, or all backups in the current directory
3405
3406 Usage:
3407 -h    Display this help text
3408 -c    Keep the file/folder as is, create a copy backup using cp(1) (default)
3409 -m    Move the file/folder, using mv(1)
3410 -r    Remove backups of the specified file or directory, using rm(1). If none
3411       is provided, remove all backups in the current directory.
3412 -a    Remove all (even hidden) backups.
3413 -v    Verbose
3414
3415 The -c, -r and -m options are mutually exclusive. If specified at the same time,
3416 the last one is used.
3417
3418 The return code is the sum of all cp/mv/rm return codes.
3419 __EOF0__
3420 return 0;;
3421             \?) bk -h >&2; return 1;;
3422         esac
3423     done
3424     shift "$((OPTIND-1))"
3425     if (( keep > 0 )); then
3426         if islinux || isfreebsd; then
3427             for to_bk in "$@"; do
3428                 cp $verbose -a "${to_bk%/}" "${to_bk%/}_$current_date"
3429                 (( result += $? ))
3430             done
3431         else
3432             for to_bk in "$@"; do
3433                 cp $verbose -pR "${to_bk%/}" "${to_bk%/}_$current_date"
3434                 (( result += $? ))
3435             done
3436         fi
3437     elif (( move > 0 )); then
3438         while (( $# > 0 )); do
3439             mv $verbose "${1%/}" "${1%/}_$current_date"
3440             (( result += $? ))
3441             shift
3442         done
3443     elif (( clean > 0 )); then
3444         if (( $# > 0 )); then
3445             for to_bk in "$@"; do
3446                 rm $verbose -rf "${to_bk%/}"_[0-9](#c8)T([0-1][0-9]|2[0-3])([0-5][0-9])(#c2)Z
3447                 (( result += $? ))
3448             done
3449         else
3450             if (( all > 0 )); then
3451                 rm $verbose -rf *_[0-9](#c8)T([0-1][0-9]|2[0-3])([0-5][0-9])(#c2)Z(D)
3452             else
3453                 rm $verbose -rf *_[0-9](#c8)T([0-1][0-9]|2[0-3])([0-5][0-9])(#c2)Z
3454             fi
3455             (( result += $? ))
3456         fi
3457     fi
3458     return $result
3459 }
3460
3461 #f5# cd to directory and list files
3462 function cl () {
3463     emulate -L zsh
3464     cd $1 && ls -a
3465 }
3466
3467 # smart cd function, allows switching to /etc when running 'cd /etc/fstab'
3468 function cd () {
3469     if (( ${#argv} == 1 )) && [[ -f ${1} ]]; then
3470         [[ ! -e ${1:h} ]] && return 1
3471         print "Correcting ${1} to ${1:h}"
3472         builtin cd ${1:h}
3473     else
3474         builtin cd "$@"
3475     fi
3476 }
3477
3478 #f5# Create Directory and \kbd{cd} to it
3479 function mkcd () {
3480     if (( ARGC != 1 )); then
3481         printf 'usage: mkcd <new-directory>\n'
3482         return 1;
3483     fi
3484     if [[ ! -d "$1" ]]; then
3485         command mkdir -p "$1"
3486     else
3487         printf '`%s'\'' already exists: cd-ing.\n' "$1"
3488     fi
3489     builtin cd "$1"
3490 }
3491
3492 #f5# Create temporary directory and \kbd{cd} to it
3493 function cdt () {
3494     builtin cd "$(mktemp -d)"
3495     builtin pwd
3496 }
3497
3498 #f5# List files which have been accessed within the last {\it n} days, {\it n} defaults to 1
3499 function accessed () {
3500     emulate -L zsh
3501     print -l -- *(a-${1:-1})
3502 }
3503
3504 #f5# List files which have been changed within the last {\it n} days, {\it n} defaults to 1
3505 function changed () {
3506     emulate -L zsh
3507     print -l -- *(c-${1:-1})
3508 }
3509
3510 #f5# List files which have been modified within the last {\it n} days, {\it n} defaults to 1
3511 function modified () {
3512     emulate -L zsh
3513     print -l -- *(m-${1:-1})
3514 }
3515 # modified() was named new() in earlier versions, add an alias for backwards compatibility
3516 check_com new || alias new=modified
3517
3518 # use colors when GNU grep with color-support
3519 if (( $#grep_options > 0 )); then
3520     o=${grep_options:+"${grep_options[*]}"}
3521     #a2# Execute \kbd{grep -{}-color=auto}
3522     alias grep='grep '$o
3523     alias egrep='egrep '$o
3524     unset o
3525 fi
3526
3527 # Translate DE<=>EN
3528 # 'translate' looks up a word in a file with language-to-language
3529 # translations (field separator should be " : "). A typical wordlist looks
3530 # like the following:
3531 #  | english-word : german-translation
3532 # It's also only possible to translate english to german but not reciprocal.
3533 # Use the following oneliner to reverse the sort order:
3534 #  $ awk -F ':' '{ print $2" : "$1" "$3 }' \
3535 #    /usr/local/lib/words/en-de.ISO-8859-1.vok > ~/.translate/de-en.ISO-8859-1.vok
3536 #f5# Translates a word
3537 function trans () {
3538     emulate -L zsh
3539     case "$1" in
3540         -[dD]*)
3541             translate -l de-en $2
3542             ;;
3543         -[eE]*)
3544             translate -l en-de $2
3545             ;;
3546         *)
3547             echo "Usage: $0 { -D | -E }"
3548             echo "         -D == German to English"
3549             echo "         -E == English to German"
3550     esac
3551 }
3552
3553 # Usage: simple-extract <file>
3554 # Using option -d deletes the original archive file.
3555 #f5# Smart archive extractor
3556 function simple-extract () {
3557     emulate -L zsh
3558     setopt extended_glob noclobber
3559     local ARCHIVE DELETE_ORIGINAL DECOMP_CMD USES_STDIN USES_STDOUT GZTARGET WGET_CMD
3560     local RC=0
3561     zparseopts -D -E "d=DELETE_ORIGINAL"
3562     for ARCHIVE in "${@}"; do
3563         case $ARCHIVE in
3564             *(tar.bz2|tbz2|tbz))
3565                 DECOMP_CMD="tar -xvjf -"
3566                 USES_STDIN=true
3567                 USES_STDOUT=false
3568                 ;;
3569             *(tar.gz|tgz))
3570                 DECOMP_CMD="tar -xvzf -"
3571                 USES_STDIN=true
3572                 USES_STDOUT=false
3573                 ;;
3574             *(tar.xz|txz|tar.lzma))
3575                 DECOMP_CMD="tar -xvJf -"
3576                 USES_STDIN=true
3577                 USES_STDOUT=false
3578                 ;;
3579             *tar.zst)
3580                 DECOMP_CMD="tar --zstd -xvf -"
3581                 USES_STDIN=true
3582                 USES_STDOUT=false
3583                 ;;
3584             *tar.lrz)
3585                 DECOMP_CMD="lrzuntar"
3586                 USES_STDIN=false
3587                 USES_STDOUT=false
3588                 ;;
3589             *tar)
3590                 DECOMP_CMD="tar -xvf -"
3591                 USES_STDIN=true
3592                 USES_STDOUT=false
3593                 ;;
3594             *rar)
3595                 DECOMP_CMD="unrar x"
3596                 USES_STDIN=false
3597                 USES_STDOUT=false
3598                 ;;
3599             *lzh)
3600                 DECOMP_CMD="lha x"
3601                 USES_STDIN=false
3602                 USES_STDOUT=false
3603                 ;;
3604             *7z)
3605                 DECOMP_CMD="7z x"
3606                 USES_STDIN=false
3607                 USES_STDOUT=false
3608                 ;;
3609             *(zip|jar))
3610                 DECOMP_CMD="unzip"
3611                 USES_STDIN=false
3612                 USES_STDOUT=false
3613                 ;;
3614             *deb)
3615                 DECOMP_CMD="ar -x"
3616                 USES_STDIN=false
3617                 USES_STDOUT=false
3618                 ;;
3619             *bz2)
3620                 DECOMP_CMD="bzip2 -d -c -"
3621                 USES_STDIN=true
3622                 USES_STDOUT=true
3623                 ;;
3624             *(gz|Z))
3625                 DECOMP_CMD="gzip -d -c -"
3626                 USES_STDIN=true
3627                 USES_STDOUT=true
3628                 ;;
3629             *(xz|lzma))
3630                 DECOMP_CMD="xz -d -c -"
3631                 USES_STDIN=true
3632                 USES_STDOUT=true
3633                 ;;
3634             *zst)
3635                 DECOMP_CMD="zstd -d -c -"
3636                 USES_STDIN=true
3637                 USES_STDOUT=true
3638                 ;;
3639             *lrz)
3640                 DECOMP_CMD="lrunzip -"
3641                 USES_STDIN=true
3642                 USES_STDOUT=true
3643                 ;;
3644             *)
3645                 print "ERROR: '$ARCHIVE' has unrecognized archive type." >&2
3646                 RC=$((RC+1))
3647                 continue
3648                 ;;
3649         esac
3650
3651         if ! check_com ${DECOMP_CMD[(w)1]}; then
3652             echo "ERROR: ${DECOMP_CMD[(w)1]} not installed." >&2
3653             RC=$((RC+2))
3654             continue
3655         fi
3656
3657         GZTARGET="${ARCHIVE:t:r}"
3658         if [[ -f $ARCHIVE ]] ; then
3659
3660             print "Extracting '$ARCHIVE' ..."
3661             if $USES_STDIN; then
3662                 if $USES_STDOUT; then
3663                     ${=DECOMP_CMD} < "$ARCHIVE" > $GZTARGET
3664                 else
3665                     ${=DECOMP_CMD} < "$ARCHIVE"
3666                 fi
3667             else
3668                 if $USES_STDOUT; then
3669                     ${=DECOMP_CMD} "$ARCHIVE" > $GZTARGET
3670                 else
3671                     ${=DECOMP_CMD} "$ARCHIVE"
3672                 fi
3673             fi
3674             [[ $? -eq 0 && -n "$DELETE_ORIGINAL" ]] && rm -f "$ARCHIVE"
3675
3676         elif [[ "$ARCHIVE" == (#s)(https|http|ftp)://* ]] ; then
3677             if check_com curl; then
3678                 WGET_CMD="curl -L -s -o -"
3679             elif check_com wget; then
3680                 WGET_CMD="wget -q -O -"
3681             elif check_com fetch; then
3682                 WGET_CMD="fetch -q -o -"
3683             else
3684                 print "ERROR: neither wget, curl nor fetch is installed" >&2
3685                 RC=$((RC+4))
3686                 continue
3687             fi
3688             print "Downloading and Extracting '$ARCHIVE' ..."
3689             if $USES_STDIN; then
3690                 if $USES_STDOUT; then
3691                     ${=WGET_CMD} "$ARCHIVE" | ${=DECOMP_CMD} > $GZTARGET
3692                     RC=$((RC+$?))
3693                 else
3694                     ${=WGET_CMD} "$ARCHIVE" | ${=DECOMP_CMD}
3695                     RC=$((RC+$?))
3696                 fi
3697             else
3698                 if $USES_STDOUT; then
3699                     ${=DECOMP_CMD} =(${=WGET_CMD} "$ARCHIVE") > $GZTARGET
3700                 else
3701                     ${=DECOMP_CMD} =(${=WGET_CMD} "$ARCHIVE")
3702                 fi
3703             fi
3704
3705         else
3706             print "ERROR: '$ARCHIVE' is neither a valid file nor a supported URI." >&2
3707             RC=$((RC+8))
3708         fi
3709     done
3710     return $RC
3711 }
3712
3713 function __archive_or_uri () {
3714     _alternative \
3715         '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)"' \
3716         '_urls:Remote Archives:_urls'
3717 }
3718
3719 function _simple_extract () {
3720     _arguments \
3721         '-d[delete original archivefile after extraction]' \
3722         '*:Archive Or Uri:__archive_or_uri'
3723 }
3724 compdef _simple_extract simple-extract
3725 [[ -n "$GRML_NO_SMALL_ALIASES" ]] || alias se=simple-extract
3726
3727 #f5# Change the xterm title from within GNU-screen
3728 function xtrename () {
3729     emulate -L zsh
3730     if [[ $1 != "-f" ]] ; then
3731         if [[ -z ${DISPLAY} ]] ; then
3732             printf 'xtrename only makes sense in X11.\n'
3733             return 1
3734         fi
3735     else
3736         shift
3737     fi
3738     if [[ -z $1 ]] ; then
3739         printf 'usage: xtrename [-f] "title for xterm"\n'
3740         printf '  renames the title of xterm from _within_ screen.\n'
3741         printf '  also works without screen.\n'
3742         printf '  will not work if DISPLAY is unset, use -f to override.\n'
3743         return 0
3744     fi
3745     print -n "\eP\e]0;${1}\C-G\e\\"
3746     return 0
3747 }
3748
3749 # Create small urls via http://goo.gl using curl(1).
3750 # API reference: https://code.google.com/apis/urlshortener/
3751 function zurl () {
3752     emulate -L zsh
3753     setopt extended_glob
3754
3755     if [[ -z $1 ]]; then
3756         print "USAGE: zurl <URL>"
3757         return 1
3758     fi
3759
3760     local PN url prog api json contenttype item
3761     local -a data
3762     PN=$0
3763     url=$1
3764
3765     # Prepend 'http://' to given URL where necessary for later output.
3766     if [[ ${url} != http(s|)://* ]]; then
3767         url='http://'${url}
3768     fi
3769
3770     if check_com -c curl; then
3771         prog=curl
3772     else
3773         print "curl is not available, but mandatory for ${PN}. Aborting."
3774         return 1
3775     fi
3776     api='https://www.googleapis.com/urlshortener/v1/url'
3777     contenttype="Content-Type: application/json"
3778     json="{\"longUrl\": \"${url}\"}"
3779     data=(${(f)"$($prog --silent -H ${contenttype} -d ${json} $api)"})
3780     # Parse the response
3781     for item in "${data[@]}"; do
3782         case "$item" in
3783             ' '#'"id":'*)
3784                 item=${item#*: \"}
3785                 item=${item%\",*}
3786                 printf '%s\n' "$item"
3787                 return 0
3788                 ;;
3789         esac
3790     done
3791     return 1
3792 }
3793
3794 #f2# Find history events by search pattern and list them by date.
3795 function whatwhen () {
3796     emulate -L zsh
3797     local usage help ident format_l format_s first_char remain first last
3798     usage='USAGE: whatwhen [options] <searchstring> <search range>'
3799     help='Use `whatwhen -h'\'' for further explanations.'
3800     ident=${(l,${#${:-Usage: }},, ,)}
3801     format_l="${ident}%s\t\t\t%s\n"
3802     format_s="${format_l//(\\t)##/\\t}"
3803     # Make the first char of the word to search for case
3804     # insensitive; e.g. [aA]
3805     first_char=[${(L)1[1]}${(U)1[1]}]
3806     remain=${1[2,-1]}
3807     # Default search range is `-100'.
3808     first=${2:-\-100}
3809     # Optional, just used for `<first> <last>' given.
3810     last=$3
3811     case $1 in
3812         ("")
3813             printf '%s\n\n' 'ERROR: No search string specified. Aborting.'
3814             printf '%s\n%s\n\n' ${usage} ${help} && return 1
3815         ;;
3816         (-h)
3817             printf '%s\n\n' ${usage}
3818             print 'OPTIONS:'
3819             printf $format_l '-h' 'show help text'
3820             print '\f'
3821             print 'SEARCH RANGE:'
3822             printf $format_l "'0'" 'the whole history,'
3823             printf $format_l '-<n>' 'offset to the current history number; (default: -100)'
3824             printf $format_s '<[-]first> [<last>]' 'just searching within a give range'
3825             printf '\n%s\n' 'EXAMPLES:'
3826             printf ${format_l/(\\t)/} 'whatwhen grml' '# Range is set to -100 by default.'
3827             printf $format_l 'whatwhen zsh -250'
3828             printf $format_l 'whatwhen foo 1 99'
3829         ;;
3830         (\?)
3831             printf '%s\n%s\n\n' ${usage} ${help} && return 1
3832         ;;
3833         (*)
3834             # -l list results on stout rather than invoking $EDITOR.
3835             # -i Print dates as in YYYY-MM-DD.
3836             # -m Search for a - quoted - pattern within the history.
3837             fc -li -m "*${first_char}${remain}*" $first $last
3838         ;;
3839     esac
3840 }
3841
3842 # mercurial related stuff
3843 if check_com -c hg ; then
3844     # gnu like diff for mercurial
3845     # http://www.selenic.com/mercurial/wiki/index.cgi/TipsAndTricks
3846     #f5# GNU like diff for mercurial
3847     function hgdi () {
3848         emulate -L zsh
3849         local i
3850         for i in $(hg status -marn "$@") ; diff -ubwd <(hg cat "$i") "$i"
3851     }
3852
3853     # build debian package
3854     #a2# Alias for \kbd{hg-buildpackage}
3855     alias hbp='hg-buildpackage'
3856
3857     # execute commands on the versioned patch-queue from the current repos
3858     [[ -n "$GRML_NO_SMALL_ALIASES" ]] || alias mq='hg -R $(readlink -f $(hg root)/.hg/patches)'
3859
3860     # diffstat for specific version of a mercurial repository
3861     #   hgstat      => display diffstat between last revision and tip
3862     #   hgstat 1234 => display diffstat between revision 1234 and tip
3863     #f5# Diffstat for specific version of a mercurial repos
3864     function hgstat () {
3865         emulate -L zsh
3866         [[ -n "$1" ]] && hg diff -r $1 -r tip | diffstat || hg export tip | diffstat
3867     }
3868
3869 fi # end of check whether we have the 'hg'-executable
3870
3871 # disable bracketed paste mode for dumb terminals
3872 [[ "$TERM" == dumb ]] && unset zle_bracketed_paste
3873
3874 # grml-small cleanups and workarounds
3875
3876 # The following is used to remove zsh-config-items that do not work
3877 # in grml-small by default.
3878 # If you do not want these adjustments (for whatever reason), set
3879 # $GRMLSMALL_SPECIFIC to 0 in your .zshrc.pre file (which this configuration
3880 # sources if it is there).
3881
3882 if (( GRMLSMALL_SPECIFIC > 0 )) && isgrmlsmall ; then
3883
3884     # Clean up
3885
3886     unset "abk[V]"
3887     unalias    'V'      &> /dev/null
3888     unfunction vman     &> /dev/null
3889     unfunction viless   &> /dev/null
3890     unfunction 2html    &> /dev/null
3891
3892     # manpages are not in grmlsmall
3893     unfunction manzsh   &> /dev/null
3894     unfunction man2     &> /dev/null
3895
3896     # Workarounds
3897
3898     # See https://github.com/grml/grml/issues/56
3899     if ! [[ -x ${commands[dig]} ]]; then
3900         function dig_after_all () {
3901             unfunction dig
3902             unfunction _dig
3903             autoload -Uz _dig
3904             unfunction dig_after_all
3905         }
3906         function dig () {
3907             if [[ -x ${commands[dig]} ]]; then
3908                 dig_after_all
3909                 command dig "$@"
3910                 return "$!"
3911             fi
3912             printf 'This installation does not include `dig'\'' for size reasons.\n'
3913             printf 'Try `drill'\'' as a light weight alternative.\n'
3914             return 0
3915         }
3916         function _dig () {
3917             if [[ -x ${commands[dig]} ]]; then
3918                 dig_after_all
3919                 zle -M 'Found `dig'\'' installed. '
3920             else
3921                 zle -M 'Try `drill'\'' instead of `dig'\''.'
3922             fi
3923         }
3924         compdef _dig dig
3925     fi
3926 fi
3927
3928 zrclocal
3929
3930 unfunction grml_status_feature
3931
3932 ## genrefcard.pl settings
3933
3934 ### doc strings for external functions from files
3935 #m# f5 grml-wallpaper() Sets a wallpaper (try completion for possible values)
3936
3937 ### example: split functions-search 8,16,24,32
3938 #@# split functions-search 8
3939
3940 ## END OF FILE #################################################################
3941 # vim:filetype=zsh foldmethod=marker autoindent expandtab shiftwidth=4
3942 # Local variables:
3943 # mode: sh
3944 # End: