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 |
|
26 | BASH_COMPLETION_VERSINFO=(2 8)
|
27 |
|
28 | if [[ $- == *v* ]]; then
|
29 | BASH_COMPLETION_ORIGINAL_V_VALUE="-v"
|
30 | else
|
31 | BASH_COMPLETION_ORIGINAL_V_VALUE="+v"
|
32 | fi
|
33 |
|
34 | if [[ ${BASH_COMPLETION_DEBUG-} ]]; then
|
35 | set -v
|
36 | else
|
37 | set +v
|
38 | fi
|
39 |
|
40 | # Blacklisted completions, causing problems with our code.
|
41 | #
|
42 | _blacklist_glob='@(acroread.sh)'
|
43 |
|
44 | # Turn on extended globbing and programmable completion
|
45 | shopt -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
|
53 | complete -u groups slay w sux
|
54 |
|
55 | # bg completes with stopped jobs
|
56 | complete -A stopped -P '"%' -S '"' bg
|
57 |
|
58 | # other job commands
|
59 | complete -j -P '"%' -S '"' fg jobs disown
|
60 |
|
61 | # readonly and unset complete with shell variables
|
62 | complete -v readonly unset
|
63 |
|
64 | # set completes with set options
|
65 | complete -A setopt set
|
66 |
|
67 | # shopt completes with shopt options
|
68 | complete -A shopt shopt
|
69 |
|
70 | # helptopics
|
71 | complete -A helptopic help
|
72 |
|
73 | # unalias completes with aliases
|
74 | complete -a unalias
|
75 |
|
76 | # type and which complete on commands
|
77 | complete -c command type which
|
78 |
|
79 | # builtin completes on builtins
|
80 | complete -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.
|
116 | have()
|
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
|
131 | quote()
|
132 | {
|
133 | local quoted=${1//\'/\'\\\'\'}
|
134 | printf "'%s'" "$quoted"
|
135 | }
|
136 |
|
137 | # @see _quote_readline_by_ref()
|
138 | quote_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
|
147 | dequote()
|
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 | } &&
|
1182 | complete -F _service service
|
1183 | _sysvdirs
|
1184 | for svcdir in ${sysvdirs[@]}; do
|
1185 | for svc in $svcdir/!($_backup_glob); do
|
1186 | [[ -x $svc ]] && complete -F _service $svc
|
1187 | done
|
1188 | done
|
1189 | unset 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 | }
|
1429 | shopt -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()
|
1666 | complete -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 | }
|
1714 | if shopt -q cdable_vars; then
|
1715 | complete -v -F _cd -o nospace cd pushd
|
1716 | else
|
1717 | complete -F _cd -o nospace cd pushd
|
1718 | fi
|
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 | }
|
1826 | complete -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 | }
|
1835 | complete -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.
|
1891 | complete -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.
|
1899 | if [[ ${BASH_VERSINFO[0]} -gt 4 ||
|
1900 | ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 2 ]]; then
|
1901 | declare -Ag _xspecs
|
1902 | else
|
1903 | declare -A _xspecs
|
1904 | fi
|
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
|
2019 | unset -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
|
2031 | complete -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 | } &&
|
2076 | complete -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
|
2094 | compat_dir=${BASH_COMPLETION_COMPAT_DIR:-/etc/bash_completion.d}
|
2095 | if [[ -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
|
2100 | fi
|
2101 | unset compat_dir i _blacklist_glob
|
2102 |
|
2103 | # source user completion file
|
2104 | user_completion=${BASH_COMPLETION_USER_FILE:-~/.bash_completion}
|
2105 | [[ ${BASH_SOURCE[0]} != $user_completion && -r $user_completion ]] \
|
2106 | && . $user_completion
|
2107 | unset user_completion
|
2108 |
|
2109 | unset -f have
|
2110 | unset have
|
2111 |
|
2112 | set $BASH_COMPLETION_ORIGINAL_V_VALUE
|
2113 | unset BASH_COMPLETION_ORIGINAL_V_VALUE
|
2114 |
|
2115 | # ex: filetype=sh
|