1 # -*- shell-script -*- 2 # 3 # bash_completion - programmable completion functions for bash 4.1+ 4 # 5 # Copyright © 2006-2008, Ian Macdonald 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 '' -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 '' \ 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 '' -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'. 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'. 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'. 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_.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 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