| 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
|