OILS / testdata / completion / bash_completion View on Github | oilshell.org

2115 lines, 1895 significant
1# -*- shell-script -*-
2#
3# bash_completion - programmable completion functions for bash 4.1+
4#
5# Copyright © 2006-2008, Ian Macdonald <ian@caliban.org>
6# © 2009-2018, Bash Completion Maintainers
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2, or (at your option)
11# any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software Foundation,
20# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21#
22# The latest version of this software can be obtained here:
23#
24# https://github.com/scop/bash-completion
25
26BASH_COMPLETION_VERSINFO=(2 8)
27
28if [[ $- == *v* ]]; then
29 BASH_COMPLETION_ORIGINAL_V_VALUE="-v"
30else
31 BASH_COMPLETION_ORIGINAL_V_VALUE="+v"
32fi
33
34if [[ ${BASH_COMPLETION_DEBUG-} ]]; then
35 set -v
36else
37 set +v
38fi
39
40# Blacklisted completions, causing problems with our code.
41#
42_blacklist_glob='@(acroread.sh)'
43
44# Turn on extended globbing and programmable completion
45shopt -s extglob progcomp
46
47# A lot of the following one-liners were taken directly from the
48# completion examples provided with the bash 2.04 source distribution
49
50# start of section containing compspecs that can be handled within bash
51
52# user commands see only users
53complete -u groups slay w sux
54
55# bg completes with stopped jobs
56complete -A stopped -P '"%' -S '"' bg
57
58# other job commands
59complete -j -P '"%' -S '"' fg jobs disown
60
61# readonly and unset complete with shell variables
62complete -v readonly unset
63
64# set completes with set options
65complete -A setopt set
66
67# shopt completes with shopt options
68complete -A shopt shopt
69
70# helptopics
71complete -A helptopic help
72
73# unalias completes with aliases
74complete -a unalias
75
76# type and which complete on commands
77complete -c command type which
78
79# builtin completes on builtins
80complete -b builtin
81
82# start of section containing completion functions called by other functions
83
84# Check if we're running on the given userland
85# @param $1 userland to check for
86_userland()
87{
88 local userland=$( uname -s )
89 [[ $userland == @(Linux|GNU/*) ]] && userland=GNU
90 [[ $userland == $1 ]]
91}
92
93# This function sets correct SysV init directories
94#
95_sysvdirs()
96{
97 sysvdirs=( )
98 [[ -d /etc/rc.d/init.d ]] && sysvdirs+=( /etc/rc.d/init.d )
99 [[ -d /etc/init.d ]] && sysvdirs+=( /etc/init.d )
100 # Slackware uses /etc/rc.d
101 [[ -f /etc/slackware-version ]] && sysvdirs=( /etc/rc.d )
102}
103
104# This function checks whether we have a given program on the system.
105#
106_have()
107{
108 # Completions for system administrator commands are installed as well in
109 # case completion is attempted via `sudo command ...'.
110 PATH=$PATH:/usr/sbin:/sbin:/usr/local/sbin type $1 &>/dev/null
111}
112
113# Backwards compatibility for compat completions that use have().
114# @deprecated should no longer be used; generally not needed with dynamically
115# loaded completions, and _have is suitable for runtime use.
116have()
117{
118 unset -v have
119 _have $1 && have=yes
120}
121
122# This function checks whether a given readline variable
123# is `on'.
124#
125_rl_enabled()
126{
127 [[ "$( bind -v )" == *$1+([[:space:]])on* ]]
128}
129
130# This function shell-quotes the argument
131quote()
132{
133 local quoted=${1//\'/\'\\\'\'}
134 printf "'%s'" "$quoted"
135}
136
137# @see _quote_readline_by_ref()
138quote_readline()
139{
140 local quoted
141 _quote_readline_by_ref "$1" ret
142 printf %s "$ret"
143} # quote_readline()
144
145
146# This function shell-dequotes the argument
147dequote()
148{
149 eval printf %s "$1" 2> /dev/null
150}
151
152
153# Assign variable one scope above the caller
154# Usage: local "$1" && _upvar $1 "value(s)"
155# Param: $1 Variable name to assign value to
156# Param: $* Value(s) to assign. If multiple values, an array is
157# assigned, otherwise a single value is assigned.
158# NOTE: For assigning multiple variables, use '_upvars'. Do NOT
159# use multiple '_upvar' calls, since one '_upvar' call might
160# reassign a variable to be used by another '_upvar' call.
161# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
162_upvar()
163{
164 if unset -v "$1"; then # Unset & validate varname
165 if (( $# == 2 )); then
166 eval $1=\"\$2\" # Return single value
167 else
168 eval $1=\(\"\${@:2}\"\) # Return array
169 fi
170 fi
171}
172
173
174# Assign variables one scope above the caller
175# Usage: local varname [varname ...] &&
176# _upvars [-v varname value] | [-aN varname [value ...]] ...
177# Available OPTIONS:
178# -aN Assign next N values to varname as array
179# -v Assign single value to varname
180# Return: 1 if error occurs
181# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
182_upvars()
183{
184 if ! (( $# )); then
185 echo "${FUNCNAME[0]}: usage: ${FUNCNAME[0]} [-v varname"\
186 "value] | [-aN varname [value ...]] ..." 1>&2
187 return 2
188 fi
189 while (( $# )); do
190 case $1 in
191 -a*)
192 # Error checking
193 [[ ${1#-a} ]] || { echo "bash: ${FUNCNAME[0]}: \`$1': missing"\
194 "number specifier" 1>&2; return 1; }
195 printf %d "${1#-a}" &> /dev/null || { echo "bash:"\
196 "${FUNCNAME[0]}: \`$1': invalid number specifier" 1>&2
197 return 1; }
198 # Assign array of -aN elements
199 [[ "$2" ]] && unset -v "$2" && eval $2=\(\"\${@:3:${1#-a}}\"\) &&
200 shift $((${1#-a} + 2)) || { echo "bash: ${FUNCNAME[0]}:"\
201 "\`$1${2+ }$2': missing argument(s)" 1>&2; return 1; }
202 ;;
203 -v)
204 # Assign single value
205 [[ "$2" ]] && unset -v "$2" && eval $2=\"\$3\" &&
206 shift 3 || { echo "bash: ${FUNCNAME[0]}: $1: missing"\
207 "argument(s)" 1>&2; return 1; }
208 ;;
209 *)
210 echo "bash: ${FUNCNAME[0]}: $1: invalid option" 1>&2
211 return 1 ;;
212 esac
213 done
214}
215
216
217# Reassemble command line words, excluding specified characters from the
218# list of word completion separators (COMP_WORDBREAKS).
219# @param $1 chars Characters out of $COMP_WORDBREAKS which should
220# NOT be considered word breaks. This is useful for things like scp where
221# we want to return host:path and not only path, so we would pass the
222# colon (:) as $1 here.
223# @param $2 words Name of variable to return words to
224# @param $3 cword Name of variable to return cword to
225#
226__reassemble_comp_words_by_ref()
227{
228 local exclude i j line ref
229 # Exclude word separator characters?
230 if [[ $1 ]]; then
231 # Yes, exclude word separator characters;
232 # Exclude only those characters, which were really included
233 exclude="${1//[^$COMP_WORDBREAKS]}"
234 fi
235
236 # Default to cword unchanged
237 printf -v "$3" %s "$COMP_CWORD"
238 # Are characters excluded which were former included?
239 if [[ $exclude ]]; then
240 # Yes, list of word completion separators has shrunk;
241 line=$COMP_LINE
242 # Re-assemble words to complete
243 for (( i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do
244 # Is current word not word 0 (the command itself) and is word not
245 # empty and is word made up of just word separator characters to
246 # be excluded and is current word not preceded by whitespace in
247 # original line?
248 while [[ $i -gt 0 && ${COMP_WORDS[$i]} == +([$exclude]) ]]; do
249 # Is word separator not preceded by whitespace in original line
250 # and are we not going to append to word 0 (the command
251 # itself), then append to current word.
252 [[ $line != [[:blank:]]* ]] && (( j >= 2 )) && ((j--))
253 # Append word separator to current or new word
254 ref="$2[$j]"
255 printf -v "$ref" %s "${!ref}${COMP_WORDS[i]}"
256 # Indicate new cword
257 [[ $i == $COMP_CWORD ]] && printf -v "$3" %s "$j"
258 # Remove optional whitespace + word separator from line copy
259 line=${line#*"${COMP_WORDS[$i]}"}
260 # Start new word if word separator in original line is
261 # followed by whitespace.
262 [[ $line == [[:blank:]]* ]] && ((j++))
263 # Indicate next word if available, else end *both* while and
264 # for loop
265 (( $i < ${#COMP_WORDS[@]} - 1)) && ((i++)) || break 2
266 done
267 # Append word to current word
268 ref="$2[$j]"
269 printf -v "$ref" %s "${!ref}${COMP_WORDS[i]}"
270 # Remove optional whitespace + word from line copy
271 line=${line#*"${COMP_WORDS[i]}"}
272 # Indicate new cword
273 [[ $i == $COMP_CWORD ]] && printf -v "$3" %s "$j"
274 done
275 [[ $i == $COMP_CWORD ]] && printf -v "$3" %s "$j"
276 else
277 # No, list of word completions separators hasn't changed;
278 for i in ${!COMP_WORDS[@]}; do
279 printf -v "$2[i]" %s "${COMP_WORDS[i]}"
280 done
281 fi
282} # __reassemble_comp_words_by_ref()
283
284
285# @param $1 exclude Characters out of $COMP_WORDBREAKS which should NOT be
286# considered word breaks. This is useful for things like scp where
287# we want to return host:path and not only path, so we would pass the
288# colon (:) as $1 in this case.
289# @param $2 words Name of variable to return words to
290# @param $3 cword Name of variable to return cword to
291# @param $4 cur Name of variable to return current word to complete to
292# @see __reassemble_comp_words_by_ref()
293__get_cword_at_cursor_by_ref()
294{
295 local cword words=()
296 __reassemble_comp_words_by_ref "$1" words cword
297
298 local i cur index=$COMP_POINT lead=${COMP_LINE:0:$COMP_POINT}
299 # Cursor not at position 0 and not leaded by just space(s)?
300 if [[ $index -gt 0 && ( $lead && ${lead//[[:space:]]} ) ]]; then
301 cur=$COMP_LINE
302 for (( i = 0; i <= cword; ++i )); do
303 while [[
304 # Current word fits in $cur?
305 ${#cur} -ge ${#words[i]} &&
306 # $cur doesn't match cword?
307 "${cur:0:${#words[i]}}" != "${words[i]}"
308 ]]; do
309 # Strip first character
310 cur="${cur:1}"
311 # Decrease cursor position, staying >= 0
312 [[ $index -gt 0 ]] && ((index--))
313 done
314
315 # Does found word match cword?
316 if [[ $i -lt $cword ]]; then
317 # No, cword lies further;
318 local old_size=${#cur}
319 cur="${cur#"${words[i]}"}"
320 local new_size=${#cur}
321 index=$(( index - old_size + new_size ))
322 fi
323 done
324 # Clear $cur if just space(s)
325 [[ $cur && ! ${cur//[[:space:]]} ]] && cur=
326 # Zero $index if negative
327 [[ $index -lt 0 ]] && index=0
328 fi
329
330 local "$2" "$3" "$4" && _upvars -a${#words[@]} $2 "${words[@]}" \
331 -v $3 "$cword" -v $4 "${cur:0:$index}"
332}
333
334
335# Get the word to complete and optional previous words.
336# This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
337# where the user is completing in the middle of a word.
338# (For example, if the line is "ls foobar",
339# and the cursor is here --------> ^
340# Also one is able to cross over possible wordbreak characters.
341# Usage: _get_comp_words_by_ref [OPTIONS] [VARNAMES]
342# Available VARNAMES:
343# cur Return cur via $cur
344# prev Return prev via $prev
345# words Return words via $words
346# cword Return cword via $cword
347#
348# Available OPTIONS:
349# -n EXCLUDE Characters out of $COMP_WORDBREAKS which should NOT be
350# considered word breaks. This is useful for things like scp
351# where we want to return host:path and not only path, so we
352# would pass the colon (:) as -n option in this case.
353# -c VARNAME Return cur via $VARNAME
354# -p VARNAME Return prev via $VARNAME
355# -w VARNAME Return words via $VARNAME
356# -i VARNAME Return cword via $VARNAME
357#
358# Example usage:
359#
360# $ _get_comp_words_by_ref -n : cur prev
361#
362_get_comp_words_by_ref()
363{
364 local exclude flag i OPTIND=1
365 local cur cword words=()
366 local upargs=() upvars=() vcur vcword vprev vwords
367
368 while getopts "c:i:n:p:w:" flag "$@"; do
369 case $flag in
370 c) vcur=$OPTARG ;;
371 i) vcword=$OPTARG ;;
372 n) exclude=$OPTARG ;;
373 p) vprev=$OPTARG ;;
374 w) vwords=$OPTARG ;;
375 esac
376 done
377 while [[ $# -ge $OPTIND ]]; do
378 case ${!OPTIND} in
379 cur) vcur=cur ;;
380 prev) vprev=prev ;;
381 cword) vcword=cword ;;
382 words) vwords=words ;;
383 *) echo "bash: $FUNCNAME(): \`${!OPTIND}': unknown argument" \
384 1>&2; return 1
385 esac
386 let "OPTIND += 1"
387 done
388
389 __get_cword_at_cursor_by_ref "$exclude" words cword cur
390
391 [[ $vcur ]] && { upvars+=("$vcur" ); upargs+=(-v $vcur "$cur" ); }
392 [[ $vcword ]] && { upvars+=("$vcword"); upargs+=(-v $vcword "$cword"); }
393 [[ $vprev && $cword -ge 1 ]] && { upvars+=("$vprev" ); upargs+=(-v $vprev
394 "${words[cword - 1]}"); }
395 [[ $vwords ]] && { upvars+=("$vwords"); upargs+=(-a${#words[@]} $vwords
396 "${words[@]}"); }
397
398 (( ${#upvars[@]} )) && local "${upvars[@]}" && _upvars "${upargs[@]}"
399}
400
401
402# Get the word to complete.
403# This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
404# where the user is completing in the middle of a word.
405# (For example, if the line is "ls foobar",
406# and the cursor is here --------> ^
407# @param $1 string Characters out of $COMP_WORDBREAKS which should NOT be
408# considered word breaks. This is useful for things like scp where
409# we want to return host:path and not only path, so we would pass the
410# colon (:) as $1 in this case.
411# @param $2 integer Index number of word to return, negatively offset to the
412# current word (default is 0, previous is 1), respecting the exclusions
413# given at $1. For example, `_get_cword "=:" 1' returns the word left of
414# the current word, respecting the exclusions "=:".
415# @deprecated Use `_get_comp_words_by_ref cur' instead
416# @see _get_comp_words_by_ref()
417_get_cword()
418{
419 local LC_CTYPE=C
420 local cword words
421 __reassemble_comp_words_by_ref "$1" words cword
422
423 # return previous word offset by $2
424 if [[ ${2//[^0-9]/} ]]; then
425 printf "%s" "${words[cword-$2]}"
426 elif [[ "${#words[cword]}" -eq 0 || "$COMP_POINT" == "${#COMP_LINE}" ]]; then
427 printf "%s" "${words[cword]}"
428 else
429 local i
430 local cur="$COMP_LINE"
431 local index="$COMP_POINT"
432 for (( i = 0; i <= cword; ++i )); do
433 while [[
434 # Current word fits in $cur?
435 "${#cur}" -ge ${#words[i]} &&
436 # $cur doesn't match cword?
437 "${cur:0:${#words[i]}}" != "${words[i]}"
438 ]]; do
439 # Strip first character
440 cur="${cur:1}"
441 # Decrease cursor position, staying >= 0
442 [[ $index -gt 0 ]] && ((index--))
443 done
444
445 # Does found word matches cword?
446 if [[ "$i" -lt "$cword" ]]; then
447 # No, cword lies further;
448 local old_size="${#cur}"
449 cur="${cur#${words[i]}}"
450 local new_size="${#cur}"
451 index=$(( index - old_size + new_size ))
452 fi
453 done
454
455 if [[ "${words[cword]:0:${#cur}}" != "$cur" ]]; then
456 # We messed up! At least return the whole word so things
457 # keep working
458 printf "%s" "${words[cword]}"
459 else
460 printf "%s" "${cur:0:$index}"
461 fi
462 fi
463} # _get_cword()
464
465
466# Get word previous to the current word.
467# This is a good alternative to `prev=${COMP_WORDS[COMP_CWORD-1]}' because bash4
468# will properly return the previous word with respect to any given exclusions to
469# COMP_WORDBREAKS.
470# @deprecated Use `_get_comp_words_by_ref cur prev' instead
471# @see _get_comp_words_by_ref()
472#
473_get_pword()
474{
475 if [[ $COMP_CWORD -ge 1 ]]; then
476 _get_cword "${@:-}" 1
477 fi
478}
479
480
481# If the word-to-complete contains a colon (:), left-trim COMPREPLY items with
482# word-to-complete.
483# With a colon in COMP_WORDBREAKS, words containing
484# colons are always completed as entire words if the word to complete contains
485# a colon. This function fixes this, by removing the colon-containing-prefix
486# from COMPREPLY items.
487# The preferred solution is to remove the colon (:) from COMP_WORDBREAKS in
488# your .bashrc:
489#
490# # Remove colon (:) from list of word completion separators
491# COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
492#
493# See also: Bash FAQ - E13) Why does filename completion misbehave if a colon
494# appears in the filename? - http://tiswww.case.edu/php/chet/bash/FAQ
495# @param $1 current word to complete (cur)
496# @modifies global array $COMPREPLY
497#
498__ltrim_colon_completions()
499{
500 if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
501 # Remove colon-word prefix from COMPREPLY items
502 local colon_word=${1%"${1##*:}"}
503 local i=${#COMPREPLY[*]}
504 while [[ $((--i)) -ge 0 ]]; do
505 COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
506 done
507 fi
508} # __ltrim_colon_completions()
509
510
511# This function quotes the argument in a way so that readline dequoting
512# results in the original argument. This is necessary for at least
513# `compgen' which requires its arguments quoted/escaped:
514#
515# $ ls "a'b/"
516# c
517# $ compgen -f "a'b/" # Wrong, doesn't return output
518# $ compgen -f "a\'b/" # Good
519# a\'b/c
520#
521# See also:
522# - http://lists.gnu.org/archive/html/bug-bash/2009-03/msg00155.html
523# - http://www.mail-archive.com/bash-completion-devel@lists.alioth.\
524# debian.org/msg01944.html
525# @param $1 Argument to quote
526# @param $2 Name of variable to return result to
527_quote_readline_by_ref()
528{
529 if [[ $1 == \'* ]]; then
530 # Leave out first character
531 printf -v $2 %s "${1:1}"
532 else
533 printf -v $2 %q "$1"
534 fi
535
536 # If result becomes quoted like this: $'string', re-evaluate in order to
537 # drop the additional quoting. See also: http://www.mail-archive.com/
538 # bash-completion-devel@lists.alioth.debian.org/msg01942.html
539 [[ ${!2} == \$* ]] && eval $2=${!2}
540} # _quote_readline_by_ref()
541
542
543# This function performs file and directory completion. It's better than
544# simply using 'compgen -f', because it honours spaces in filenames.
545# @param $1 If `-d', complete only on directories. Otherwise filter/pick only
546# completions with `.$1' and the uppercase version of it as file
547# extension.
548#
549_filedir()
550{
551 local IFS=$'\n'
552
553 _tilde "$cur" || return
554
555 local -a toks
556 local x reset
557
558 reset=$(shopt -po noglob); set -o noglob
559 toks=( $( compgen -d -- "$cur" ) )
560 IFS=' '; $reset; IFS=$'\n'
561
562 if [[ "$1" != -d ]]; then
563 local quoted
564 _quote_readline_by_ref "$cur" quoted
565
566 # Munge xspec to contain uppercase version too
567 # http://thread.gmane.org/gmane.comp.shells.bash.bugs/15294/focus=15306
568 local xspec=${1:+"!*.@($1|${1^^})"}
569 reset=$(shopt -po noglob); set -o noglob
570 toks+=( $( compgen -f -X "$xspec" -- $quoted ) )
571 IFS=' '; $reset; IFS=$'\n'
572
573 # Try without filter if it failed to produce anything and configured to
574 [[ -n ${COMP_FILEDIR_FALLBACK:-} && -n "$1" && ${#toks[@]} -lt 1 ]] && {
575 reset=$(shopt -po noglob); set -o noglob
576 toks+=( $( compgen -f -- $quoted ) )
577 IFS=' '; $reset; IFS=$'\n'
578 }
579 fi
580
581 if [[ ${#toks[@]} -ne 0 ]]; then
582 # 2>/dev/null for direct invocation, e.g. in the _filedir unit test
583 compopt -o filenames 2>/dev/null
584 COMPREPLY+=( "${toks[@]}" )
585 fi
586} # _filedir()
587
588
589# This function splits $cur=--foo=bar into $prev=--foo, $cur=bar, making it
590# easier to support both "--foo bar" and "--foo=bar" style completions.
591# `=' should have been removed from COMP_WORDBREAKS when setting $cur for
592# this to be useful.
593# Returns 0 if current option was split, 1 otherwise.
594#
595_split_longopt()
596{
597 if [[ "$cur" == --?*=* ]]; then
598 # Cut also backslash before '=' in case it ended up there
599 # for some reason.
600 prev="${cur%%?(\\)=*}"
601 cur="${cur#*=}"
602 return 0
603 fi
604
605 return 1
606}
607
608# Complete variables.
609# @return True (0) if variables were completed,
610# False (> 0) if not.
611_variables()
612{
613 if [[ $cur =~ ^(\$(\{[!#]?)?)([A-Za-z0-9_]*)$ ]]; then
614 # Completing $var / ${var / ${!var / ${#var
615 if [[ $cur == \${* ]]; then
616 local arrs vars
617 vars=( $( compgen -A variable -P ${BASH_REMATCH[1]} -S '}' -- ${BASH_REMATCH[3]} ) ) && \
618 arrs=( $( compgen -A arrayvar -P ${BASH_REMATCH[1]} -S '[' -- ${BASH_REMATCH[3]} ) )
619 if [[ ${#vars[@]} -eq 1 && $arrs ]]; then
620 # Complete ${arr with ${array[ if there is only one match, and that match is an array variable
621 compopt -o nospace
622 COMPREPLY+=( ${arrs[*]} )
623 else
624 # Complete ${var with ${variable}
625 COMPREPLY+=( ${vars[*]} )
626 fi
627 else
628 # Complete $var with $variable
629 COMPREPLY+=( $( compgen -A variable -P '$' -- "${BASH_REMATCH[3]}" ) )
630 fi
631 return 0
632 elif [[ $cur =~ ^(\$\{[#!]?)([A-Za-z0-9_]*)\[([^]]*)$ ]]; then
633 # Complete ${array[i with ${array[idx]}
634 local IFS=$'\n'
635 COMPREPLY+=( $( compgen -W '$(printf %s\\n "${!'${BASH_REMATCH[2]}'[@]}")' \
636 -P "${BASH_REMATCH[1]}${BASH_REMATCH[2]}[" -S ']}' -- "${BASH_REMATCH[3]}" ) )
637 # Complete ${arr[@ and ${arr[*
638 if [[ ${BASH_REMATCH[3]} == [@*] ]]; then
639 COMPREPLY+=( "${BASH_REMATCH[1]}${BASH_REMATCH[2]}[${BASH_REMATCH[3]}]}" )
640 fi
641 __ltrim_colon_completions "$cur" # array indexes may have colons
642 return 0
643 elif [[ $cur =~ ^\$\{[#!]?[A-Za-z0-9_]*\[.*\]$ ]]; then
644 # Complete ${array[idx] with ${array[idx]}
645 COMPREPLY+=( "$cur}" )
646 __ltrim_colon_completions "$cur"
647 return 0
648 else
649 case $prev in
650 TZ)
651 cur=/usr/share/zoneinfo/$cur
652 _filedir
653 for i in ${!COMPREPLY[@]}; do
654 if [[ ${COMPREPLY[i]} == *.tab ]]; then
655 unset 'COMPREPLY[i]'
656 continue
657 elif [[ -d ${COMPREPLY[i]} ]]; then
658 COMPREPLY[i]+=/
659 compopt -o nospace
660 fi
661 COMPREPLY[i]=${COMPREPLY[i]#/usr/share/zoneinfo/}
662 done
663 return 0
664 ;;
665 esac
666 fi
667 return 1
668}
669
670# Initialize completion and deal with various general things: do file
671# and variable completion where appropriate, and adjust prev, words,
672# and cword as if no redirections exist so that completions do not
673# need to deal with them. Before calling this function, make sure
674# cur, prev, words, and cword are local, ditto split if you use -s.
675#
676# Options:
677# -n EXCLUDE Passed to _get_comp_words_by_ref -n with redirection chars
678# -e XSPEC Passed to _filedir as first arg for stderr redirections
679# -o XSPEC Passed to _filedir as first arg for other output redirections
680# -i XSPEC Passed to _filedir as first arg for stdin redirections
681# -s Split long options with _split_longopt, implies -n =
682# @return True (0) if completion needs further processing,
683# False (> 0) no further processing is necessary.
684#
685_init_completion()
686{
687 local exclude= flag outx errx inx OPTIND=1
688
689 while getopts "n:e:o:i:s" flag "$@"; do
690 case $flag in
691 n) exclude+=$OPTARG ;;
692 e) errx=$OPTARG ;;
693 o) outx=$OPTARG ;;
694 i) inx=$OPTARG ;;
695 s) split=false ; exclude+== ;;
696 esac
697 done
698
699 # For some reason completion functions are not invoked at all by
700 # bash (at least as of 4.1.7) after the command line contains an
701 # ampersand so we don't get a chance to deal with redirections
702 # containing them, but if we did, hopefully the below would also
703 # do the right thing with them...
704
705 COMPREPLY=()
706 local redir="@(?([0-9])<|?([0-9&])>?(>)|>&)"
707 _get_comp_words_by_ref -n "$exclude<>&" cur prev words cword
708
709 # Complete variable names.
710 _variables && return 1
711
712 # Complete on files if current is a redirect possibly followed by a
713 # filename, e.g. ">foo", or previous is a "bare" redirect, e.g. ">".
714 if [[ $cur == $redir* || $prev == $redir ]]; then
715 local xspec
716 case $cur in
717 2'>'*) xspec=$errx ;;
718 *'>'*) xspec=$outx ;;
719 *'<'*) xspec=$inx ;;
720 *)
721 case $prev in
722 2'>'*) xspec=$errx ;;
723 *'>'*) xspec=$outx ;;
724 *'<'*) xspec=$inx ;;
725 esac
726 ;;
727 esac
728 cur="${cur##$redir}"
729 _filedir $xspec
730 return 1
731 fi
732
733 # Remove all redirections so completions don't have to deal with them.
734 local i skip
735 for (( i=1; i < ${#words[@]}; )); do
736 if [[ ${words[i]} == $redir* ]]; then
737 # If "bare" redirect, remove also the next word (skip=2).
738 [[ ${words[i]} == $redir ]] && skip=2 || skip=1
739 words=( "${words[@]:0:i}" "${words[@]:i+skip}" )
740 [[ $i -le $cword ]] && cword=$(( cword - skip ))
741 else
742 i=$(( ++i ))
743 fi
744 done
745
746 [[ $cword -le 0 ]] && return 1
747 prev=${words[cword-1]}
748
749 [[ ${split-} ]] && _split_longopt && split=true
750
751 return 0
752}
753
754# Helper function for _parse_help and _parse_usage.
755__parse_options()
756{
757 local option option2 i IFS=$' \t\n,/|'
758
759 # Take first found long option, or first one (short) if not found.
760 option=
761 local -a array
762 read -a array <<<"$1"
763 for i in "${array[@]}"; do
764 case "$i" in
765 ---*) break ;;
766 --?*) option=$i ; break ;;
767 -?*) [[ $option ]] || option=$i ;;
768 *) break ;;
769 esac
770 done
771 [[ $option ]] || return
772
773 IFS=$' \t\n' # affects parsing of the regexps below...
774
775 # Expand --[no]foo to --foo and --nofoo etc
776 if [[ $option =~ (\[((no|dont)-?)\]). ]]; then
777 option2=${option/"${BASH_REMATCH[1]}"/}
778 option2=${option2%%[<{().[]*}
779 printf '%s\n' "${option2/=*/=}"
780 option=${option/"${BASH_REMATCH[1]}"/"${BASH_REMATCH[2]}"}
781 fi
782
783 option=${option%%[<{().[]*}
784 printf '%s\n' "${option/=*/=}"
785}
786
787# Parse GNU style help output of the given command.
788# @param $1 command; if "-", read from stdin and ignore rest of args
789# @param $2 command options (default: --help)
790#
791_parse_help()
792{
793 eval local cmd=$( quote "$1" )
794 local line
795 { case $cmd in
796 -) cat ;;
797 *) LC_ALL=C "$( dequote "$cmd" )" ${2:---help} 2>&1 ;;
798 esac } \
799 | while read -r line; do
800
801 [[ $line == *([[:blank:]])-* ]] || continue
802 # transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc
803 while [[ $line =~ \
804 ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+\]? ]]; do
805 line=${line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"}
806 done
807 __parse_options "${line// or /, }"
808
809 done
810}
811
812# Parse BSD style usage output (options in brackets) of the given command.
813# @param $1 command; if "-", read from stdin and ignore rest of args
814# @param $2 command options (default: --usage)
815#
816_parse_usage()
817{
818 eval local cmd=$( quote "$1" )
819 local line match option i char
820 { case $cmd in
821 -) cat ;;
822 *) LC_ALL=C "$( dequote "$cmd" )" ${2:---usage} 2>&1 ;;
823 esac } \
824 | while read -r line; do
825
826 while [[ $line =~ \[[[:space:]]*(-[^]]+)[[:space:]]*\] ]]; do
827 match=${BASH_REMATCH[0]}
828 option=${BASH_REMATCH[1]}
829 case $option in
830 -?(\[)+([a-zA-Z0-9?]))
831 # Treat as bundled short options
832 for (( i=1; i < ${#option}; i++ )); do
833 char=${option:i:1}
834 [[ $char != '[' ]] && printf '%s\n' -$char
835 done
836 ;;
837 *)
838 __parse_options "$option"
839 ;;
840 esac
841 line=${line#*"$match"}
842 done
843
844 done
845}
846
847# This function completes on signal names (minus the SIG prefix)
848# @param $1 prefix
849_signals()
850{
851 local -a sigs=( $( compgen -P "$1" -A signal "SIG${cur#$1}" ) )
852 COMPREPLY+=( "${sigs[@]/#${1}SIG/${1}}" )
853}
854
855# This function completes on known mac addresses
856#
857_mac_addresses()
858{
859 local re='\([A-Fa-f0-9]\{2\}:\)\{5\}[A-Fa-f0-9]\{2\}'
860 local PATH="$PATH:/sbin:/usr/sbin"
861
862 # Local interfaces
863 # - ifconfig on Linux: HWaddr or ether
864 # - ifconfig on FreeBSD: ether
865 # - ip link: link/ether
866 COMPREPLY+=( $( \
867 { LC_ALL=C ifconfig -a || ip link show; } 2>/dev/null | command sed -ne \
868 "s/.*[[:space:]]HWaddr[[:space:]]\{1,\}\($re\)[[:space:]].*/\1/p" -ne \
869 "s/.*[[:space:]]HWaddr[[:space:]]\{1,\}\($re\)[[:space:]]*$/\1/p" -ne \
870 "s|.*[[:space:]]\(link/\)\{0,1\}ether[[:space:]]\{1,\}\($re\)[[:space:]].*|\2|p" -ne \
871 "s|.*[[:space:]]\(link/\)\{0,1\}ether[[:space:]]\{1,\}\($re\)[[:space:]]*$|\2|p"
872 ) )
873
874 # ARP cache
875 COMPREPLY+=( $( { arp -an || ip neigh show; } 2>/dev/null | command sed -ne \
876 "s/.*[[:space:]]\($re\)[[:space:]].*/\1/p" -ne \
877 "s/.*[[:space:]]\($re\)[[:space:]]*$/\1/p" ) )
878
879 # /etc/ethers
880 COMPREPLY+=( $( command sed -ne \
881 "s/^[[:space:]]*\($re\)[[:space:]].*/\1/p" /etc/ethers 2>/dev/null ) )
882
883 COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$cur" ) )
884 __ltrim_colon_completions "$cur"
885}
886
887# This function completes on configured network interfaces
888#
889_configured_interfaces()
890{
891 if [[ -f /etc/debian_version ]]; then
892 # Debian system
893 COMPREPLY=( $( compgen -W "$( command sed -ne 's|^iface \([^ ]\{1,\}\).*$|\1|p'\
894 /etc/network/interfaces /etc/network/interfaces.d/* 2>/dev/null )" \
895 -- "$cur" ) )
896 elif [[ -f /etc/SuSE-release ]]; then
897 # SuSE system
898 COMPREPLY=( $( compgen -W "$( printf '%s\n' \
899 /etc/sysconfig/network/ifcfg-* | \
900 command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p' )" -- "$cur" ) )
901 elif [[ -f /etc/pld-release ]]; then
902 # PLD Linux
903 COMPREPLY=( $( compgen -W "$( command ls -B \
904 /etc/sysconfig/interfaces | \
905 command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p' )" -- "$cur" ) )
906 else
907 # Assume Red Hat
908 COMPREPLY=( $( compgen -W "$( printf '%s\n' \
909 /etc/sysconfig/network-scripts/ifcfg-* | \
910 command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p' )" -- "$cur" ) )
911 fi
912}
913
914# Local IP addresses.
915# -4: IPv4 addresses only (default)
916# -6: IPv6 addresses only
917# -a: All addresses
918#
919_ip_addresses()
920{
921 local n
922 case $1 in
923 -a) n='6\?' ;;
924 -6) n='6' ;;
925 esac
926 local PATH=$PATH:/sbin
927 local addrs=$( { LC_ALL=C ifconfig -a || ip addr show; } 2>/dev/null |
928 command sed -ne \
929 's/.*addr:\([^[:space:]]*\).*/\1/p' -ne \
930 "s|.*inet$n[[:space:]]\{1,\}\([^[:space:]/]*\).*|\1|p" )
931 COMPREPLY+=( $( compgen -W "$addrs" -- "$cur" ) )
932}
933
934# This function completes on available kernels
935#
936_kernel_versions()
937{
938 COMPREPLY=( $( compgen -W '$( command ls /lib/modules )' -- "$cur" ) )
939}
940
941# This function completes on all available network interfaces
942# -a: restrict to active interfaces only
943# -w: restrict to wireless interfaces only
944#
945_available_interfaces()
946{
947 local PATH=$PATH:/sbin
948
949 COMPREPLY=( $( {
950 if [[ ${1:-} == -w ]]; then
951 iwconfig
952 elif [[ ${1:-} == -a ]]; then
953 ifconfig || ip link show up
954 else
955 ifconfig -a || ip link show
956 fi
957 } 2>/dev/null | awk \
958 '/^[^ \t]/ { if ($1 ~ /^[0-9]+:/) { print $2 } else { print $1 } }' ) )
959
960 COMPREPLY=( $( compgen -W '${COMPREPLY[@]/%[[:punct:]]/}' -- "$cur" ) )
961}
962
963# Echo number of CPUs, falling back to 1 on failure.
964_ncpus()
965{
966 local var=NPROCESSORS_ONLN
967 [[ $OSTYPE == *linux* ]] && var=_$var
968 local n=$( getconf $var 2>/dev/null )
969 printf %s ${n:-1}
970}
971
972# Perform tilde (~) completion
973# @return True (0) if completion needs further processing,
974# False (> 0) if tilde is followed by a valid username, completions
975# are put in COMPREPLY and no further processing is necessary.
976_tilde()
977{
978 local result=0
979 if [[ $1 == \~* && $1 != */* ]]; then
980 # Try generate ~username completions
981 COMPREPLY=( $( compgen -P '~' -u -- "${1#\~}" ) )
982 result=${#COMPREPLY[@]}
983 # 2>/dev/null for direct invocation, e.g. in the _tilde unit test
984 [[ $result -gt 0 ]] && compopt -o filenames 2>/dev/null
985 fi
986 return $result
987}
988
989
990# Expand variable starting with tilde (~)
991# We want to expand ~foo/... to /home/foo/... to avoid problems when
992# word-to-complete starting with a tilde is fed to commands and ending up
993# quoted instead of expanded.
994# Only the first portion of the variable from the tilde up to the first slash
995# (~../) is expanded. The remainder of the variable, containing for example
996# a dollar sign variable ($) or asterisk (*) is not expanded.
997# Example usage:
998#
999# $ v="~"; __expand_tilde_by_ref v; echo "$v"
1000#
1001# Example output:
1002#
1003# v output
1004# -------- ----------------
1005# ~ /home/user
1006# ~foo/bar /home/foo/bar
1007# ~foo/$HOME /home/foo/$HOME
1008# ~foo/a b /home/foo/a b
1009# ~foo/* /home/foo/*
1010#
1011# @param $1 Name of variable (not the value of the variable) to expand
1012__expand_tilde_by_ref()
1013{
1014 if [[ ${!1} == \~* ]]; then
1015 eval $1=$(printf ~%q "${!1#\~}")
1016 fi
1017} # __expand_tilde_by_ref()
1018
1019
1020# This function expands tildes in pathnames
1021#
1022_expand()
1023{
1024 # Expand ~username type directory specifications. We want to expand
1025 # ~foo/... to /home/foo/... to avoid problems when $cur starting with
1026 # a tilde is fed to commands and ending up quoted instead of expanded.
1027
1028 if [[ "$cur" == \~*/* ]]; then
1029 __expand_tilde_by_ref cur
1030 elif [[ "$cur" == \~* ]]; then
1031 _tilde "$cur" || eval COMPREPLY[0]=$(printf ~%q "${COMPREPLY[0]#\~}")
1032 return ${#COMPREPLY[@]}
1033 fi
1034}
1035
1036# This function completes on process IDs.
1037# AIX and Solaris ps prefers X/Open syntax.
1038[[ $OSTYPE == *@(solaris|aix)* ]] &&
1039_pids()
1040{
1041 COMPREPLY=( $( compgen -W '$( command ps -efo pid | command sed 1d )' -- "$cur" ))
1042} ||
1043_pids()
1044{
1045 COMPREPLY=( $( compgen -W '$( command ps axo pid= )' -- "$cur" ) )
1046}
1047
1048# This function completes on process group IDs.
1049# AIX and SunOS prefer X/Open, all else should be BSD.
1050[[ $OSTYPE == *@(solaris|aix)* ]] &&
1051_pgids()
1052{
1053 COMPREPLY=( $( compgen -W '$( command ps -efo pgid | command sed 1d )' -- "$cur" ))
1054} ||
1055_pgids()
1056{
1057 COMPREPLY=( $( compgen -W '$( command ps axo pgid= )' -- "$cur" ))
1058}
1059
1060# This function completes on process names.
1061# AIX and SunOS prefer X/Open, all else should be BSD.
1062# @param $1 if -s, don't try to avoid truncated command names
1063[[ $OSTYPE == *@(solaris|aix)* ]] &&
1064_pnames()
1065{
1066 COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps -efo comm | \
1067 command sed -e 1d -e "s:.*/::" -e "s/^-//" | sort -u )' -- "$cur" ) )
1068} ||
1069_pnames()
1070{
1071 if [[ "$1" == -s ]]; then
1072 COMPREPLY=( $( compgen -X '<defunct>' \
1073 -W '$( command ps axo comm | command sed -e 1d )' -- "$cur" ) )
1074 else
1075 # FIXME: completes "[kblockd/0]" to "0". Previously it was completed
1076 # to "kblockd" which isn't correct either. "kblockd/0" would be
1077 # arguably most correct, but killall from psmisc 22 treats arguments
1078 # containing "/" specially unless -r is given so that wouldn't quite
1079 # work either. Perhaps it'd be best to not complete these to anything
1080 # for now.
1081 COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps axo command= | command sed -e \
1082 "s/ .*//" -e \
1083 "s:.*/::" -e \
1084 "s/:$//" -e \
1085 "s/^[[(-]//" -e \
1086 "s/[])]$//" | sort -u )' -- "$cur" ) )
1087 fi
1088}
1089
1090# This function completes on user IDs
1091#
1092_uids()
1093{
1094 if type getent &>/dev/null; then
1095 COMPREPLY=( $( compgen -W '$( getent passwd | cut -d: -f3 )' -- "$cur" ) )
1096 elif type perl &>/dev/null; then
1097 COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($uid) = (getpwent)[2]) { print $uid . "\n" }'"'"' )' -- "$cur" ) )
1098 else
1099 # make do with /etc/passwd
1100 COMPREPLY=( $( compgen -W '$( cut -d: -f3 /etc/passwd )' -- "$cur" ) )
1101 fi
1102}
1103
1104# This function completes on group IDs
1105#
1106_gids()
1107{
1108 if type getent &>/dev/null; then
1109 COMPREPLY=( $( compgen -W '$( getent group | cut -d: -f3 )' \
1110 -- "$cur" ) )
1111 elif type perl &>/dev/null; then
1112 COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($gid) = (getgrent)[2]) { print $gid . "\n" }'"'"' )' -- "$cur" ) )
1113 else
1114 # make do with /etc/group
1115 COMPREPLY=( $( compgen -W '$( cut -d: -f3 /etc/group )' -- "$cur" ) )
1116 fi
1117}
1118
1119# Glob for matching various backup files.
1120#
1121_backup_glob='@(#*#|*@(~|.@(bak|orig|rej|swp|dpkg*|rpm@(orig|new|save))))'
1122
1123# Complete on xinetd services
1124#
1125_xinetd_services()
1126{
1127 local xinetddir=/etc/xinetd.d
1128 if [[ -d $xinetddir ]]; then
1129 local IFS=$' \t\n' reset=$(shopt -p nullglob); shopt -s nullglob
1130 local -a svcs=( $( printf '%s\n' $xinetddir/!($_backup_glob) ) )
1131 $reset
1132 COMPREPLY+=( $( compgen -W '${svcs[@]#$xinetddir/}' -- "$cur" ) )
1133 fi
1134}
1135
1136# This function completes on services
1137#
1138_services()
1139{
1140 local sysvdirs
1141 _sysvdirs
1142
1143 local IFS=$' \t\n' reset=$(shopt -p nullglob); shopt -s nullglob
1144 COMPREPLY=( \
1145 $( printf '%s\n' ${sysvdirs[0]}/!($_backup_glob|functions|README) ) )
1146 $reset
1147
1148 COMPREPLY+=( $( { systemctl list-units --full --all || \
1149 systemctl list-unit-files; } 2>/dev/null | \
1150 awk '$1 ~ /\.service$/ { sub("\\.service$", "", $1); print $1 }' ) )
1151
1152 if [[ -x /sbin/upstart-udev-bridge ]]; then
1153 COMPREPLY+=( $( initctl list 2>/dev/null | cut -d' ' -f1 ) )
1154 fi
1155
1156 COMPREPLY=( $( compgen -W '${COMPREPLY[@]#${sysvdirs[0]}/}' -- "$cur" ) )
1157}
1158
1159# This completes on a list of all available service scripts for the
1160# 'service' command and/or the SysV init.d directory, followed by
1161# that script's available commands
1162#
1163_service()
1164{
1165 local cur prev words cword
1166 _init_completion || return
1167
1168 # don't complete past 2nd token
1169 [[ $cword -gt 2 ]] && return
1170
1171 if [[ $cword -eq 1 && $prev == ?(*/)service ]]; then
1172 _services
1173 [[ -e /etc/mandrake-release ]] && _xinetd_services
1174 else
1175 local sysvdirs
1176 _sysvdirs
1177 COMPREPLY=( $( compgen -W '`command sed -e "y/|/ /" \
1178 -ne "s/^.*\(U\|msg_u\)sage.*{\(.*\)}.*$/\2/p" \
1179 ${sysvdirs[0]}/${prev##*/} 2>/dev/null` start stop' -- "$cur" ) )
1180 fi
1181} &&
1182complete -F _service service
1183_sysvdirs
1184for svcdir in ${sysvdirs[@]}; do
1185 for svc in $svcdir/!($_backup_glob); do
1186 [[ -x $svc ]] && complete -F _service $svc
1187 done
1188done
1189unset svc svcdir sysvdirs
1190
1191# This function completes on modules
1192#
1193_modules()
1194{
1195 local modpath
1196 modpath=/lib/modules/$1
1197 COMPREPLY=( $( compgen -W "$( command ls -RL $modpath 2>/dev/null | \
1198 command sed -ne 's/^\(.*\)\.k\{0,1\}o\(\.[gx]z\)\{0,1\}$/\1/p' )" -- "$cur" ) )
1199}
1200
1201# This function completes on installed modules
1202#
1203_installed_modules()
1204{
1205 COMPREPLY=( $( compgen -W "$( PATH="$PATH:/sbin" lsmod | \
1206 awk '{if (NR != 1) print $1}' )" -- "$1" ) )
1207}
1208
1209# This function completes on user or user:group format; as for chown and cpio.
1210#
1211# The : must be added manually; it will only complete usernames initially.
1212# The legacy user.group format is not supported.
1213#
1214# @param $1 If -u, only return users/groups the user has access to in
1215# context of current completion.
1216_usergroup()
1217{
1218 if [[ $cur == *\\\\* || $cur == *:*:* ]]; then
1219 # Give up early on if something seems horribly wrong.
1220 return
1221 elif [[ $cur == *\\:* ]]; then
1222 # Completing group after 'user\:gr<TAB>'.
1223 # Reply with a list of groups prefixed with 'user:', readline will
1224 # escape to the colon.
1225 local prefix
1226 prefix=${cur%%*([^:])}
1227 prefix=${prefix//\\}
1228 local mycur="${cur#*[:]}"
1229 if [[ $1 == -u ]]; then
1230 _allowed_groups "$mycur"
1231 else
1232 local IFS=$'\n'
1233 COMPREPLY=( $( compgen -g -- "$mycur" ) )
1234 fi
1235 COMPREPLY=( $( compgen -P "$prefix" -W "${COMPREPLY[@]}" ) )
1236 elif [[ $cur == *:* ]]; then
1237 # Completing group after 'user:gr<TAB>'.
1238 # Reply with a list of unprefixed groups since readline with split on :
1239 # and only replace the 'gr' part
1240 local mycur="${cur#*:}"
1241 if [[ $1 == -u ]]; then
1242 _allowed_groups "$mycur"
1243 else
1244 local IFS=$'\n'
1245 COMPREPLY=( $( compgen -g -- "$mycur" ) )
1246 fi
1247 else
1248 # Completing a partial 'usernam<TAB>'.
1249 #
1250 # Don't suffix with a : because readline will escape it and add a
1251 # slash. It's better to complete into 'chown username ' than 'chown
1252 # username\:'.
1253 if [[ $1 == -u ]]; then
1254 _allowed_users "$cur"
1255 else
1256 local IFS=$'\n'
1257 COMPREPLY=( $( compgen -u -- "$cur" ) )
1258 fi
1259 fi
1260}
1261
1262_allowed_users()
1263{
1264 if _complete_as_root; then
1265 local IFS=$'\n'
1266 COMPREPLY=( $( compgen -u -- "${1:-$cur}" ) )
1267 else
1268 local IFS=$'\n '
1269 COMPREPLY=( $( compgen -W \
1270 "$( id -un 2>/dev/null || whoami 2>/dev/null )" -- "${1:-$cur}" ) )
1271 fi
1272}
1273
1274_allowed_groups()
1275{
1276 if _complete_as_root; then
1277 local IFS=$'\n'
1278 COMPREPLY=( $( compgen -g -- "$1" ) )
1279 else
1280 local IFS=$'\n '
1281 COMPREPLY=( $( compgen -W \
1282 "$( id -Gn 2>/dev/null || groups 2>/dev/null )" -- "$1" ) )
1283 fi
1284}
1285
1286# This function completes on valid shells
1287#
1288_shells()
1289{
1290 local shell rest
1291 while read -r shell rest; do
1292 [[ $shell == /* && $shell == "$cur"* ]] && COMPREPLY+=( $shell )
1293 done 2>/dev/null < /etc/shells
1294}
1295
1296# This function completes on valid filesystem types
1297#
1298_fstypes()
1299{
1300 local fss
1301
1302 if [[ -e /proc/filesystems ]]; then
1303 # Linux
1304 fss="$( cut -d$'\t' -f2 /proc/filesystems )
1305 $( awk '! /\*/ { print $NF }' /etc/filesystems 2>/dev/null )"
1306 else
1307 # Generic
1308 fss="$( awk '/^[ \t]*[^#]/ { print $3 }' /etc/fstab 2>/dev/null )
1309 $( awk '/^[ \t]*[^#]/ { print $3 }' /etc/mnttab 2>/dev/null )
1310 $( awk '/^[ \t]*[^#]/ { print $4 }' /etc/vfstab 2>/dev/null )
1311 $( awk '{ print $1 }' /etc/dfs/fstypes 2>/dev/null )
1312 $( [[ -d /etc/fs ]] && command ls /etc/fs )"
1313 fi
1314
1315 [[ -n $fss ]] && COMPREPLY+=( $( compgen -W "$fss" -- "$cur" ) )
1316}
1317
1318# Get real command.
1319# - arg: $1 Command
1320# - stdout: Filename of command in PATH with possible symbolic links resolved.
1321# Empty string if command not found.
1322# - return: True (0) if command found, False (> 0) if not.
1323_realcommand()
1324{
1325 type -P "$1" > /dev/null && {
1326 if type -p realpath > /dev/null; then
1327 realpath "$(type -P "$1")"
1328 elif type -p greadlink > /dev/null; then
1329 greadlink -f "$(type -P "$1")"
1330 elif type -p readlink > /dev/null; then
1331 readlink -f "$(type -P "$1")"
1332 else
1333 type -P "$1"
1334 fi
1335 }
1336}
1337
1338# This function returns the first argument, excluding options
1339# @param $1 chars Characters out of $COMP_WORDBREAKS which should
1340# NOT be considered word breaks. See __reassemble_comp_words_by_ref.
1341_get_first_arg()
1342{
1343 local i
1344
1345 arg=
1346 for (( i=1; i < COMP_CWORD; i++ )); do
1347 if [[ "${COMP_WORDS[i]}" != -* ]]; then
1348 arg=${COMP_WORDS[i]}
1349 break
1350 fi
1351 done
1352}
1353
1354
1355# This function counts the number of args, excluding options
1356# @param $1 chars Characters out of $COMP_WORDBREAKS which should
1357# NOT be considered word breaks. See __reassemble_comp_words_by_ref.
1358# @param $2 glob Options whose following argument should not be counted
1359_count_args()
1360{
1361 local i cword words
1362 __reassemble_comp_words_by_ref "$1" words cword
1363
1364 args=1
1365 for (( i=1; i < cword; i++ )); do
1366 if [[ ${words[i]} != -* && ${words[i-1]} != $2 ]]; then
1367 args=$(($args+1))
1368 fi
1369 done
1370}
1371
1372# This function completes on PCI IDs
1373#
1374_pci_ids()
1375{
1376 COMPREPLY+=( $( compgen -W \
1377 "$( PATH="$PATH:/sbin" lspci -n | awk '{print $3}')" -- "$cur" ) )
1378}
1379
1380# This function completes on USB IDs
1381#
1382_usb_ids()
1383{
1384 COMPREPLY+=( $( compgen -W \
1385 "$( PATH="$PATH:/sbin" lsusb | awk '{print $6}' )" -- "$cur" ) )
1386}
1387
1388# CD device names
1389_cd_devices()
1390{
1391 COMPREPLY+=( $( compgen -f -d -X "!*/?([amrs])cd*" -- "${cur:-/dev/}" ) )
1392}
1393
1394# DVD device names
1395_dvd_devices()
1396{
1397 COMPREPLY+=( $( compgen -f -d -X "!*/?(r)dvd*" -- "${cur:-/dev/}" ) )
1398}
1399
1400# TERM environment variable values
1401_terms()
1402{
1403 COMPREPLY+=( $( compgen -W \
1404 "$( command sed -ne 's/^\([^[:space:]#|]\{2,\}\)|.*/\1/p' /etc/termcap \
1405 2>/dev/null )" -- "$cur" ) )
1406 COMPREPLY+=( $( compgen -W "$( { toe -a 2>/dev/null || toe 2>/dev/null; } \
1407 | awk '{ print $1 }' | sort -u )" -- "$cur" ) )
1408}
1409
1410# a little help for FreeBSD ports users
1411[[ $OSTYPE == *freebsd* ]] && complete -W 'index search fetch fetch-list
1412 extract patch configure build install reinstall deinstall clean
1413 clean-depends kernel buildworld' make
1414
1415# This function provides simple user@host completion
1416#
1417_user_at_host()
1418{
1419 local cur prev words cword
1420 _init_completion -n : || return
1421
1422 if [[ $cur == *@* ]]; then
1423 _known_hosts_real "$cur"
1424 else
1425 COMPREPLY=( $( compgen -u -S @ -- "$cur" ) )
1426 compopt -o nospace
1427 fi
1428}
1429shopt -u hostcomplete && complete -F _user_at_host talk ytalk finger
1430
1431# NOTE: Using this function as a helper function is deprecated. Use
1432# `_known_hosts_real' instead.
1433_known_hosts()
1434{
1435 local cur prev words cword
1436 _init_completion -n : || return
1437
1438 # NOTE: Using `_known_hosts' as a helper function and passing options
1439 # to `_known_hosts' is deprecated: Use `_known_hosts_real' instead.
1440 local options
1441 [[ "$1" == -a || "$2" == -a ]] && options=-a
1442 [[ "$1" == -c || "$2" == -c ]] && options+=" -c"
1443 _known_hosts_real $options -- "$cur"
1444} # _known_hosts()
1445
1446# Helper function to locate ssh included files in configs
1447# This function look for the "Include" keyword in ssh config files and include
1448# them recursively adding each result to the config variable
1449_included_ssh_config_files()
1450{
1451 [[ $# -lt 1 ]] && echo "error: $FUNCNAME: missing mandatory argument CONFIG"
1452 local configfile i f
1453 configfile=$1
1454 local included=$( command sed -ne 's/^[[:blank:]]*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][[:blank:]]\{1,\}\([^#%]*\)\(#.*\)\{0,1\}$/\1/p' "${configfile}" )
1455 for i in ${included[@]}; do
1456 # Check the origin of $configfile to complete relative included paths on included
1457 # files according to ssh_config(5):
1458 # "[...] Files without absolute paths are assumed to be in ~/.ssh if included in a user
1459 # configuration file or /etc/ssh if included from the system configuration file.[...]"
1460 if ! [[ "$i" =~ ^\~.*|^\/.* ]]; then
1461 if [[ "$configfile" =~ ^\/etc\/ssh.* ]]; then
1462 i="/etc/ssh/$i"
1463 else
1464 i="$HOME/.ssh/$i"
1465 fi
1466 fi
1467 __expand_tilde_by_ref i
1468 # In case the expanded variable contains multiple paths
1469 for f in ${i}; do
1470 if [ -r $f ]; then
1471 config+=( "$f" )
1472 # The Included file is processed to look for Included files in itself
1473 _included_ssh_config_files $f
1474 fi
1475 done
1476 done
1477} # _included_ssh_config_files()
1478
1479# Helper function for completing _known_hosts.
1480# This function performs host completion based on ssh's config and known_hosts
1481# files, as well as hostnames reported by avahi-browse if
1482# COMP_KNOWN_HOSTS_WITH_AVAHI is set to a non-empty value. Also hosts from
1483# HOSTFILE (compgen -A hostname) are added, unless
1484# COMP_KNOWN_HOSTS_WITH_HOSTFILE is set to an empty value.
1485# Usage: _known_hosts_real [OPTIONS] CWORD
1486# Options: -a Use aliases from ssh config files
1487# -c Use `:' suffix
1488# -F configfile Use `configfile' for configuration settings
1489# -p PREFIX Use PREFIX
1490# -4 Filter IPv6 addresses from results
1491# -6 Filter IPv4 addresses from results
1492# Return: Completions, starting with CWORD, are added to COMPREPLY[]
1493_known_hosts_real()
1494{
1495 local configfile flag prefix OIFS=$IFS
1496 local cur user suffix aliases i host ipv4 ipv6
1497 local -a kh tmpkh khd config
1498
1499 # TODO remove trailing %foo from entries
1500
1501 local OPTIND=1
1502 while getopts "ac46F:p:" flag "$@"; do
1503 case $flag in
1504 a) aliases='yes' ;;
1505 c) suffix=':' ;;
1506 F) configfile=$OPTARG ;;
1507 p) prefix=$OPTARG ;;
1508 4) ipv4=1 ;;
1509 6) ipv6=1 ;;
1510 esac
1511 done
1512 [[ $# -lt $OPTIND ]] && echo "error: $FUNCNAME: missing mandatory argument CWORD"
1513 cur=${!OPTIND}; let "OPTIND += 1"
1514 [[ $# -ge $OPTIND ]] && echo "error: $FUNCNAME("$@"): unprocessed arguments:"\
1515 $(while [[ $# -ge $OPTIND ]]; do printf '%s\n' ${!OPTIND}; shift; done)
1516
1517 [[ $cur == *@* ]] && user=${cur%@*}@ && cur=${cur#*@}
1518 kh=()
1519
1520 # ssh config files
1521 if [[ -n $configfile ]]; then
1522 [[ -r $configfile ]] && config+=( "$configfile" )
1523 else
1524 for i in /etc/ssh/ssh_config ~/.ssh/config ~/.ssh2/config; do
1525 [[ -r $i ]] && config+=( "$i" )
1526 done
1527 fi
1528
1529 # "Include" keyword in ssh config files
1530 for i in "${config[@]}"; do
1531 _included_ssh_config_files "$i"
1532 done
1533
1534 # Known hosts files from configs
1535 if [[ ${#config[@]} -gt 0 ]]; then
1536 local IFS=$'\n' j
1537 # expand paths (if present) to global and user known hosts files
1538 # TODO(?): try to make known hosts files with more than one consecutive
1539 # spaces in their name work (watch out for ~ expansion
1540 # breakage! Alioth#311595)
1541 tmpkh=( $( awk 'sub("^[ \t]*([Gg][Ll][Oo][Bb][Aa][Ll]|[Uu][Ss][Ee][Rr])[Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee][ \t]+", "") { print $0 }' "${config[@]}" | sort -u ) )
1542 IFS=$OIFS
1543 for i in "${tmpkh[@]}"; do
1544 # First deal with quoted entries...
1545 while [[ $i =~ ^([^\"]*)\"([^\"]*)\"(.*)$ ]]; do
1546 i=${BASH_REMATCH[1]}${BASH_REMATCH[3]}
1547 j=${BASH_REMATCH[2]}
1548 __expand_tilde_by_ref j # Eval/expand possible `~' or `~user'
1549 [[ -r $j ]] && kh+=( "$j" )
1550 done
1551 # ...and then the rest.
1552 for j in $i; do
1553 __expand_tilde_by_ref j # Eval/expand possible `~' or `~user'
1554 [[ -r $j ]] && kh+=( "$j" )
1555 done
1556 done
1557 fi
1558
1559 if [[ -z $configfile ]]; then
1560 # Global and user known_hosts files
1561 for i in /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2 \
1562 /etc/known_hosts /etc/known_hosts2 ~/.ssh/known_hosts \
1563 ~/.ssh/known_hosts2; do
1564 [[ -r $i ]] && kh+=( "$i" )
1565 done
1566 for i in /etc/ssh2/knownhosts ~/.ssh2/hostkeys; do
1567 [[ -d $i ]] && khd+=( "$i"/*pub )
1568 done
1569 fi
1570
1571 # If we have known_hosts files to use
1572 if [[ ${#kh[@]} -gt 0 || ${#khd[@]} -gt 0 ]]; then
1573 if [[ ${#kh[@]} -gt 0 ]]; then
1574 # https://man.openbsd.org/sshd.8#SSH_KNOWN_HOSTS_FILE_FORMAT
1575 for i in "${kh[@]}"; do
1576 while read -ra tmpkh; do
1577 set -- "${tmpkh[@]}"
1578 # Skip entries starting with | (hashed) and # (comment)
1579 [[ $1 == [\|\#]* ]] && continue
1580 # Ignore leading @foo (markers)
1581 [[ $1 == @* ]] && shift
1582 # Split entry on commas
1583 local IFS=,
1584 for host in $1; do
1585 # Skip hosts containing wildcards
1586 [[ $host == *[*?]* ]] && continue
1587 # Remove leading [
1588 host="${host#[}"
1589 # Remove trailing ] + optional :port
1590 host="${host%]?(:+([0-9]))}"
1591 # Add host to candidates
1592 COMPREPLY+=( $host )
1593 done
1594 IFS=$OIFS
1595 done < "$i"
1596 done
1597 COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$cur" ) )
1598 fi
1599 if [[ ${#khd[@]} -gt 0 ]]; then
1600 # Needs to look for files called
1601 # .../.ssh2/key_22_<hostname>.pub
1602 # dont fork any processes, because in a cluster environment,
1603 # there can be hundreds of hostkeys
1604 for i in "${khd[@]}" ; do
1605 if [[ "$i" == *key_22_$cur*.pub && -r "$i" ]]; then
1606 host=${i/#*key_22_/}
1607 host=${host/%.pub/}
1608 COMPREPLY+=( $host )
1609 fi
1610 done
1611 fi
1612
1613 # apply suffix and prefix
1614 for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
1615 COMPREPLY[i]=$prefix$user${COMPREPLY[i]}$suffix
1616 done
1617 fi
1618
1619 # append any available aliases from ssh config files
1620 if [[ ${#config[@]} -gt 0 && -n "$aliases" ]]; then
1621 local hosts=$( command sed -ne 's/^[[:blank:]]*[Hh][Oo][Ss][Tt][[:blank:]]\{1,\}\([^#*?%]*\)\(#.*\)\{0,1\}$/\1/p' "${config[@]}" )
1622 COMPREPLY+=( $( compgen -P "$prefix$user" \
1623 -S "$suffix" -W "$hosts" -- "$cur" ) )
1624 fi
1625
1626 # Add hosts reported by avahi-browse, if desired and it's available.
1627 if [[ ${COMP_KNOWN_HOSTS_WITH_AVAHI:-} ]] && \
1628 type avahi-browse &>/dev/null; then
1629 # The original call to avahi-browse also had "-k", to avoid lookups
1630 # into avahi's services DB. We don't need the name of the service, and
1631 # if it contains ";", it may mistify the result. But on Gentoo (at
1632 # least), -k wasn't available (even if mentioned in the manpage) some
1633 # time ago, so...
1634 COMPREPLY+=( $( compgen -P "$prefix$user" -S "$suffix" -W \
1635 "$( avahi-browse -cpr _workstation._tcp 2>/dev/null | \
1636 awk -F';' '/^=/ { print $7 }' | sort -u )" -- "$cur" ) )
1637 fi
1638
1639 # Add hosts reported by ruptime.
1640 COMPREPLY+=( $( compgen -W \
1641 "$( ruptime 2>/dev/null | awk '!/^ruptime:/ { print $1 }' )" \
1642 -- "$cur" ) )
1643
1644 # Add results of normal hostname completion, unless
1645 # `COMP_KNOWN_HOSTS_WITH_HOSTFILE' is set to an empty value.
1646 if [[ -n ${COMP_KNOWN_HOSTS_WITH_HOSTFILE-1} ]]; then
1647 COMPREPLY+=(
1648 $( compgen -A hostname -P "$prefix$user" -S "$suffix" -- "$cur" ) )
1649 fi
1650
1651 if [[ $ipv4 ]]; then
1652 COMPREPLY=( "${COMPREPLY[@]/*:*$suffix/}" )
1653 fi
1654 if [[ $ipv6 ]]; then
1655 COMPREPLY=( "${COMPREPLY[@]/+([0-9]).+([0-9]).+([0-9]).+([0-9])$suffix/}" )
1656 fi
1657 if [[ $ipv4 || $ipv6 ]]; then
1658 for i in ${!COMPREPLY[@]}; do
1659 [[ ${COMPREPLY[i]} ]] || unset -v COMPREPLY[i]
1660 done
1661 fi
1662
1663 __ltrim_colon_completions "$prefix$user$cur"
1664
1665} # _known_hosts_real()
1666complete -F _known_hosts traceroute traceroute6 \
1667 fping fping6 telnet rsh rlogin ftp dig mtr ssh-installkeys showmount
1668
1669# This meta-cd function observes the CDPATH variable, so that cd additionally
1670# completes on directories under those specified in CDPATH.
1671#
1672_cd()
1673{
1674 local cur prev words cword
1675 _init_completion || return
1676
1677 local IFS=$'\n' i j k
1678
1679 compopt -o filenames
1680
1681 # Use standard dir completion if no CDPATH or parameter starts with /,
1682 # ./ or ../
1683 if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then
1684 _filedir -d
1685 return
1686 fi
1687
1688 local -r mark_dirs=$(_rl_enabled mark-directories && echo y)
1689 local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y)
1690
1691 # we have a CDPATH, so loop on its contents
1692 for i in ${CDPATH//:/$'\n'}; do
1693 # create an array of matched subdirs
1694 k="${#COMPREPLY[@]}"
1695 for j in $( compgen -d -- $i/$cur ); do
1696 if [[ ( $mark_symdirs && -h $j || $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then
1697 j+="/"
1698 fi
1699 COMPREPLY[k++]=${j#$i/}
1700 done
1701 done
1702
1703 _filedir -d
1704
1705 if [[ ${#COMPREPLY[@]} -eq 1 ]]; then
1706 i=${COMPREPLY[0]}
1707 if [[ "$i" == "$cur" && $i != "*/" ]]; then
1708 COMPREPLY[0]="${i}/"
1709 fi
1710 fi
1711
1712 return
1713}
1714if shopt -q cdable_vars; then
1715 complete -v -F _cd -o nospace cd pushd
1716else
1717 complete -F _cd -o nospace cd pushd
1718fi
1719
1720# a wrapper method for the next one, when the offset is unknown
1721_command()
1722{
1723 local offset i
1724
1725 # find actual offset, as position of the first non-option
1726 offset=1
1727 for (( i=1; i <= COMP_CWORD; i++ )); do
1728 if [[ "${COMP_WORDS[i]}" != -* ]]; then
1729 offset=$i
1730 break
1731 fi
1732 done
1733 _command_offset $offset
1734}
1735
1736# A meta-command completion function for commands like sudo(8), which need to
1737# first complete on a command, then complete according to that command's own
1738# completion definition.
1739#
1740_command_offset()
1741{
1742 # rewrite current completion context before invoking
1743 # actual command completion
1744
1745 # find new first word position, then
1746 # rewrite COMP_LINE and adjust COMP_POINT
1747 local word_offset=$1 i j
1748 for (( i=0; i < $word_offset; i++ )); do
1749 for (( j=0; j <= ${#COMP_LINE}; j++ )); do
1750 [[ "$COMP_LINE" == "${COMP_WORDS[i]}"* ]] && break
1751 COMP_LINE=${COMP_LINE:1}
1752 ((COMP_POINT--))
1753 done
1754 COMP_LINE=${COMP_LINE#"${COMP_WORDS[i]}"}
1755 ((COMP_POINT-=${#COMP_WORDS[i]}))
1756 done
1757
1758 # shift COMP_WORDS elements and adjust COMP_CWORD
1759 for (( i=0; i <= COMP_CWORD - $word_offset; i++ )); do
1760 COMP_WORDS[i]=${COMP_WORDS[i+$word_offset]}
1761 done
1762 for (( i; i <= COMP_CWORD; i++ )); do
1763 unset 'COMP_WORDS[i]'
1764 done
1765 ((COMP_CWORD -= $word_offset))
1766
1767 COMPREPLY=()
1768 local cur
1769 _get_comp_words_by_ref cur
1770
1771 if [[ $COMP_CWORD -eq 0 ]]; then
1772 local IFS=$'\n'
1773 compopt -o filenames
1774 COMPREPLY=( $( compgen -d -c -- "$cur" ) )
1775 else
1776 local cmd=${COMP_WORDS[0]} compcmd=${COMP_WORDS[0]}
1777 local cspec=$( complete -p $cmd 2>/dev/null )
1778
1779 # If we have no completion for $cmd yet, see if we have for basename
1780 if [[ ! $cspec && $cmd == */* ]]; then
1781 cspec=$( complete -p ${cmd##*/} 2>/dev/null )
1782 [[ $cspec ]] && compcmd=${cmd##*/}
1783 fi
1784 # If still nothing, just load it for the basename
1785 if [[ ! $cspec ]]; then
1786 compcmd=${cmd##*/}
1787 _completion_loader $compcmd
1788 cspec=$( complete -p $compcmd 2>/dev/null )
1789 fi
1790
1791 if [[ -n $cspec ]]; then
1792 if [[ ${cspec#* -F } != $cspec ]]; then
1793 # complete -F <function>
1794
1795 # get function name
1796 local func=${cspec#*-F }
1797 func=${func%% *}
1798
1799 if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
1800 $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}" "${COMP_WORDS[${#COMP_WORDS[@]}-2]}"
1801 else
1802 $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}"
1803 fi
1804
1805 # restore initial compopts
1806 local opt
1807 while [[ $cspec == *" -o "* ]]; do
1808 # FIXME: should we take "+o opt" into account?
1809 cspec=${cspec#*-o }
1810 opt=${cspec%% *}
1811 compopt -o $opt
1812 cspec=${cspec#$opt}
1813 done
1814 else
1815 cspec=${cspec#complete}
1816 cspec=${cspec%%$compcmd}
1817 COMPREPLY=( $( eval compgen "$cspec" -- '$cur' ) )
1818 fi
1819 elif [[ ${#COMPREPLY[@]} -eq 0 ]]; then
1820 # XXX will probably never happen as long as completion loader loads
1821 # *something* for every command thrown at it ($cspec != empty)
1822 _minimal
1823 fi
1824 fi
1825}
1826complete -F _command aoss command do else eval exec ltrace nice nohup padsp \
1827 then time tsocks vsound xargs
1828
1829_root_command()
1830{
1831 local PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
1832 local root_command=$1
1833 _command
1834}
1835complete -F _root_command fakeroot gksu gksudo kdesudo really
1836
1837# Return true if the completion should be treated as running as root
1838_complete_as_root()
1839{
1840 [[ $EUID -eq 0 || ${root_command:-} ]]
1841}
1842
1843_longopt()
1844{
1845 local cur prev words cword split
1846 _init_completion -s || return
1847
1848 case "${prev,,}" in
1849 --help|--usage|--version)
1850 return
1851 ;;
1852 --*dir*)
1853 _filedir -d
1854 return
1855 ;;
1856 --*file*|--*path*)
1857 _filedir
1858 return
1859 ;;
1860 --+([-a-z0-9_]))
1861 local argtype=$( LC_ALL=C $1 --help 2>&1 | command sed -ne \
1862 "s|.*$prev\[\{0,1\}=[<[]\{0,1\}\([-A-Za-z0-9_]\{1,\}\).*|\1|p" )
1863 case ${argtype,,} in
1864 *dir*)
1865 _filedir -d
1866 return
1867 ;;
1868 *file*|*path*)
1869 _filedir
1870 return
1871 ;;
1872 esac
1873 ;;
1874 esac
1875
1876 $split && return
1877
1878 if [[ "$cur" == -* ]]; then
1879 COMPREPLY=( $( compgen -W "$( LC_ALL=C $1 --help 2>&1 | \
1880 command sed -ne 's/.*\(--[-A-Za-z0-9]\{1,\}=\{0,1\}\).*/\1/p' | sort -u )" \
1881 -- "$cur" ) )
1882 [[ $COMPREPLY == *= ]] && compopt -o nospace
1883 elif [[ "$1" == @(rmdir|chroot) ]]; then
1884 _filedir -d
1885 else
1886 [[ "$1" == mkdir ]] && compopt -o nospace
1887 _filedir
1888 fi
1889}
1890# makeinfo and texi2dvi are defined elsewhere.
1891complete -F _longopt a2ps awk base64 bash bc bison cat chroot colordiff cp \
1892 csplit cut date df diff dir du enscript env expand fmt fold gperf \
1893 grep grub head irb ld ldd less ln ls m4 md5sum mkdir mkfifo mknod \
1894 mv netstat nl nm objcopy objdump od paste pr ptx readelf rm rmdir \
1895 sed seq sha{,1,224,256,384,512}sum shar sort split strip sum tac tail tee \
1896 texindex touch tr uname unexpand uniq units vdir wc who
1897
1898# declare only knows -g in bash >= 4.2.
1899if [[ ${BASH_VERSINFO[0]} -gt 4 ||
1900 ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 2 ]]; then
1901 declare -Ag _xspecs
1902else
1903 declare -A _xspecs
1904fi
1905_filedir_xspec()
1906{
1907 local cur prev words cword
1908 _init_completion || return
1909
1910 _tilde "$cur" || return
1911
1912 local IFS=$'\n' xspec=${_xspecs[${1##*/}]} tmp
1913 local -a toks
1914
1915 toks=( $(
1916 compgen -d -- "$(quote_readline "$cur")" | {
1917 while read -r tmp; do
1918 printf '%s\n' $tmp
1919 done
1920 }
1921 ))
1922
1923 # Munge xspec to contain uppercase version too
1924 # http://thread.gmane.org/gmane.comp.shells.bash.bugs/15294/focus=15306
1925 eval xspec="${xspec}"
1926 local matchop=!
1927 if [[ $xspec == !* ]]; then
1928 xspec=${xspec#!}
1929 matchop=@
1930 fi
1931 xspec="$matchop($xspec|${xspec^^})"
1932
1933 toks+=( $(
1934 eval compgen -f -X "'!$xspec'" -- "\$(quote_readline "\$cur")" | {
1935 while read -r tmp; do
1936 [[ -n $tmp ]] && printf '%s\n' $tmp
1937 done
1938 }
1939 ))
1940
1941 # Try without filter if it failed to produce anything and configured to
1942 [[ -n ${COMP_FILEDIR_FALLBACK:-} && ${#toks[@]} -lt 1 ]] && {
1943 local reset=$(shopt -po noglob); set -o noglob
1944 toks+=( $( compgen -f -- "$(quote_readline "$cur")" ) )
1945 IFS=' '; $reset; IFS=$'\n'
1946 }
1947
1948 if [[ ${#toks[@]} -ne 0 ]]; then
1949 compopt -o filenames
1950 COMPREPLY=( "${toks[@]}" )
1951 fi
1952}
1953
1954_install_xspec()
1955{
1956 local xspec=$1 cmd
1957 shift
1958 for cmd in $@; do
1959 _xspecs[$cmd]=$xspec
1960 done
1961}
1962# bzcmp, bzdiff, bz*grep, bzless, bzmore intentionally not here, see Debian: #455510
1963_install_xspec '!*.?(t)bz?(2)' bunzip2 bzcat pbunzip2 pbzcat lbunzip2 lbzcat
1964_install_xspec '!*.@(zip|[egjswx]ar|exe|pk3|wsz|zargo|xpi|s[tx][cdiw]|sx[gm]|o[dt][tspgfc]|od[bm]|oxt|epub|apk|ipa|do[ct][xm]|p[op]t[mx]|xl[st][xm]|pyz|whl)' unzip zipinfo
1965_install_xspec '*.Z' compress znew
1966# zcmp, zdiff, z*grep, zless, zmore intentionally not here, see Debian: #455510
1967_install_xspec '!*.@(Z|[gGd]z|t[ag]z)' gunzip zcat
1968_install_xspec '!*.@(Z|[gGdz]z|t[ag]z)' unpigz
1969_install_xspec '!*.Z' uncompress
1970# lzcmp, lzdiff intentionally not here, see Debian: #455510
1971_install_xspec '!*.@(tlz|lzma)' lzcat lzegrep lzfgrep lzgrep lzless lzmore unlzma
1972_install_xspec '!*.@(?(t)xz|tlz|lzma)' unxz xzcat
1973_install_xspec '!*.lrz' lrunzip
1974_install_xspec '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx)' ee
1975_install_xspec '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|svg)' qiv
1976_install_xspec '!*.@(gif|jp?(e)g?(2)|j2[ck]|jp[2f]|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|?(e)ps)' xv
1977_install_xspec '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview
1978_install_xspec '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi kdvi
1979_install_xspec '!*.dvi' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx
1980_install_xspec '!*.[pf]df' acroread gpdf xpdf
1981_install_xspec '!*.@(?(e)ps|pdf)' kpdf
1982_install_xspec '!*.@(okular|@(?(e|x)ps|?(E|X)PS|[pf]df|[PF]DF|dvi|DVI|cb[rz]|CB[RZ]|djv?(u)|DJV?(U)|dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX|epub|EPUB|odt|ODT|fb?(2)|FB?(2)|mobi|MOBI|g3|G3|chm|CHM)?(.?(gz|GZ|bz2|BZ2|xz|XZ)))' okular
1983_install_xspec '!*.pdf' epdfview pdfunite
1984_install_xspec '!*.@(cb[rz7t]|djv?(u)|?(e)ps|pdf)' zathura
1985_install_xspec '!*.@(?(e)ps|pdf)' ps2pdf ps2pdf12 ps2pdf13 ps2pdf14 ps2pdfwr
1986_install_xspec '!*.texi*' makeinfo texi2html
1987_install_xspec '!*.@(?(la)tex|texi|dtx|ins|ltx|dbj)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi xetex xelatex luatex lualatex
1988_install_xspec '!*.mp3' mpg123 mpg321 madplay
1989_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM)|+([0-9]).@(vdr|VDR))?(.@(crdownload|part))' xine aaxine fbxine
1990_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM|iso|ISO)|+([0-9]).@(vdr|VDR))?(.@(crdownload|part))' kaffeine dragon
1991_install_xspec '!*.@(avi|asf|wmv)' aviplay
1992_install_xspec '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay
1993_install_xspec '!*.@(mpg|mpeg|avi|mov|qt)' xanim
1994_install_xspec '!*.@(og[ag]|m3u|flac|spx)' ogg123
1995_install_xspec '!*.@(mp3|ogg|pls|m3u)' gqmpeg freeamp
1996_install_xspec '!*.fig' xfig
1997_install_xspec '!*.@(mid?(i)|cmf)' playmidi
1998_install_xspec '!*.@(mid?(i)|rmi|rcp|[gr]36|g18|mod|xm|it|x3m|s[3t]m|kar)' timidity
1999_install_xspec '!*.@(669|abc|am[fs]|d[bs]m|dmf|far|it|mdl|m[eo]d|mid?(i)|mt[2m]|oct|okt?(a)|p[st]m|s[3t]m|ult|umx|wav|xm)' modplugplay modplug123
2000_install_xspec '*.@([ao]|so|so.!(conf|*/*)|[rs]pm|gif|jp?(e)g|mp3|mp?(e)g|avi|asf|ogg|class)' vi vim gvim rvim view rview rgvim rgview gview emacs xemacs sxemacs kate kwrite
2001_install_xspec '!*.@(zip|z|gz|tgz)' bzme
2002# konqueror not here on purpose, it's more than a web/html browser
2003_install_xspec '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx galeon dillo elinks amaya epiphany
2004_install_xspec '!*.@(sxw|stw|sxg|sgl|doc?([mx])|dot?([mx])|rtf|txt|htm|html|?(f)odt|ott|odm|pdf)' oowriter lowriter
2005_install_xspec '!*.@(sxi|sti|pps?(x)|ppt?([mx])|pot?([mx])|?(f)odp|otp)' ooimpress loimpress
2006_install_xspec '!*.@(sxc|stc|xls?([bmx])|xlw|xlt?([mx])|[ct]sv|?(f)ods|ots)' oocalc localc
2007_install_xspec '!*.@(sxd|std|sda|sdd|?(f)odg|otg)' oodraw lodraw
2008_install_xspec '!*.@(sxm|smf|mml|odf)' oomath lomath
2009_install_xspec '!*.odb' oobase lobase
2010_install_xspec '!*.[rs]pm' rpm2cpio
2011_install_xspec '!*.aux' bibtex
2012_install_xspec '!*.po' poedit gtranslator kbabel lokalize
2013_install_xspec '!*.@([Pp][Rr][Gg]|[Cc][Ll][Pp])' harbour gharbour hbpp
2014_install_xspec '!*.[Hh][Rr][Bb]' hbrun
2015_install_xspec '!*.ly' lilypond ly2dvi
2016_install_xspec '!*.@(dif?(f)|?(d)patch)?(.@([gx]z|bz2|lzma))' cdiff
2017_install_xspec '!@(*.@(ks|jks|jceks|p12|pfx|bks|ubr|gkr|cer|crt|cert|p7b|pkipath|pem|p10|csr|crl)|cacerts)' portecle
2018_install_xspec '!*.@(mp[234c]|og[ag]|@(fl|a)ac|m4[abp]|spx|tta|w?(a)v|wma|aif?(f)|asf|ape)' kid3 kid3-qt
2019unset -f _install_xspec
2020
2021# Minimal completion to use as fallback in _completion_loader.
2022_minimal()
2023{
2024 local cur prev words cword split
2025 _init_completion -s || return
2026 $split && return
2027 _filedir
2028}
2029# Complete the empty string to allow completion of '>', '>>', and '<' on < 4.3
2030# http://lists.gnu.org/archive/html/bug-bash/2012-01/msg00045.html
2031complete -F _minimal ''
2032
2033
2034__load_completion()
2035{
2036 local -a dirs=( ${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions )
2037 local OIFS=$IFS IFS=: dir cmd="${1##*/}" compfile
2038 [[ -n $cmd ]] || return 1
2039 for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do
2040 dirs+=( $dir/bash-completion/completions )
2041 done
2042 IFS=$OIFS
2043
2044 if [[ $BASH_SOURCE == */* ]]; then
2045 dirs+=( "${BASH_SOURCE%/*}/completions" )
2046 else
2047 dirs+=( ./completions )
2048 fi
2049
2050 for dir in "${dirs[@]}"; do
2051 [[ -d "$dir" ]] || continue
2052 for compfile in "$cmd" "$cmd.bash" "_$cmd"; do
2053 compfile="$dir/$compfile"
2054 # Avoid trying to source dirs; https://bugzilla.redhat.com/903540
2055 [[ -f "$compfile" ]] && . "$compfile" &>/dev/null && return 0
2056 done
2057 done
2058
2059 # Look up simple "xspec" completions
2060 [[ "${_xspecs[$cmd]}" ]] && complete -F _filedir_xspec "$cmd" && return 0
2061
2062 return 1
2063}
2064
2065# set up dynamic completion loading
2066_completion_loader()
2067{
2068 # $1=_EmptycmD_ already for empty cmds in bash 4.3, set to it for earlier
2069 local cmd="${1:-_EmptycmD_}"
2070
2071 __load_completion "$cmd" && return 124
2072
2073 # Need to define *something*, otherwise there will be no completion at all.
2074 complete -F _minimal -- "$cmd" && return 124
2075} &&
2076complete -D -F _completion_loader
2077
2078# Function for loading and calling functions from dynamically loaded
2079# completion files that may not have been sourced yet.
2080# @param $1 completion file to load function from in case it is missing
2081# @param $2... function and its arguments
2082_xfunc()
2083{
2084 set -- "$@"
2085 local srcfile=$1
2086 shift
2087 declare -F $1 &>/dev/null || {
2088 __load_completion "$srcfile"
2089 }
2090 "$@"
2091}
2092
2093# source compat completion directory definitions
2094compat_dir=${BASH_COMPLETION_COMPAT_DIR:-/etc/bash_completion.d}
2095if [[ -d $compat_dir && -r $compat_dir && -x $compat_dir ]]; then
2096 for i in "$compat_dir"/*; do
2097 [[ ${i##*/} != @($_backup_glob|Makefile*|$_blacklist_glob) \
2098 && -f $i && -r $i ]] && . "$i"
2099 done
2100fi
2101unset compat_dir i _blacklist_glob
2102
2103# source user completion file
2104user_completion=${BASH_COMPLETION_USER_FILE:-~/.bash_completion}
2105[[ ${BASH_SOURCE[0]} != $user_completion && -r $user_completion ]] \
2106 && . $user_completion
2107unset user_completion
2108
2109unset -f have
2110unset have
2111
2112set $BASH_COMPLETION_ORIGINAL_V_VALUE
2113unset BASH_COMPLETION_ORIGINAL_V_VALUE
2114
2115# ex: filetype=sh