#                                                          -*- shell-script -*-
#
#   bash_completion - programmable completion functions for bash 4.2+
#
#   Copyright © 2006-2008, Ian Macdonald <ian@caliban.org>
#             © 2009-2020, Bash Completion Maintainers
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2, or (at your option)
#   any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software Foundation,
#   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#   SPDX-License-Identifier: GPL-2.0-or-later
#
#   The latest version of this software can be obtained here:
#
#   https://github.com/scop/bash-completion

BASH_COMPLETION_VERSINFO=(
    2  # x-release-please-major
    17 # x-release-please-minor
    0  # x-release-please-patch
)

if [[ $- == *v* ]]; then
    _comp__init_original_set_v="-v"
else
    _comp__init_original_set_v="+v"
fi

if [[ ${BASH_COMPLETION_DEBUG-} ]]; then
    set -v
else
    set +v
fi

# Turn on extended globbing and programmable completion
shopt -s extglob progcomp

# Declare a compatibility function name
# @param $1 Version of bash-completion where the deprecation occurred
# @param $2 Old function name
# @param $3 New function name
# @since 2.12
_comp_deprecate_func()
{
    if (($# != 3)); then
        printf 'bash_completion: %s: usage: %s DEPRECATION_VERSION OLD_NAME NEW_NAME\n' "$FUNCNAME" "$FUNCNAME"
        return 2
    fi
    if [[ $2 != [a-zA-Z_]*([a-zA-Z_0-9]) ]]; then
        printf 'bash_completion: %s: %s\n' "$FUNCNAME" "\$2: invalid function name '$1'" >&2
        return 2
    elif [[ $3 != [a-zA-Z_]*([a-zA-Z_0-9]) ]]; then
        printf 'bash_completion: %s: %s\n' "$FUNCNAME" "\$3: invalid function name '$2'" >&2
        return 2
    fi
    eval -- "$2() { $3 \"\$@\"; }"
}

# Declare a compatibility variable name.
# For bash 4.3+, a real name alias is created, allowing value changes to
# "apply through" when the variables are set later. For bash versions earlier
# than that, the operation is once-only; the value of the new variable
# (if it's unset) is set to that of the old (if set) at call time.
#
# @param $1 Version of bash-completion where the deprecation occurred
# @param $2 Old variable name
# @param $3 New variable name
# @since 2.12
_comp_deprecate_var()
{
    if (($# != 3)); then
        printf 'bash_completion: %s: usage: %s DEPRECATION_VERSION OLD_NAME NEW_NAME\n' "$FUNCNAME" "$FUNCNAME"
        return 2
    fi
    if [[ $2 != [a-zA-Z_]*([a-zA-Z_0-9]) ]]; then
        printf 'bash_completion: %s: %s\n' "$FUNCNAME" "\$2: invalid variable name '$1'" >&2
        return 2
    elif [[ $3 != [a-zA-Z_]*([a-zA-Z_0-9]) ]]; then
        printf 'bash_completion: %s: %s\n' "$FUNCNAME" "\$3: invalid variable name '$2'" >&2
        return 2
    fi
    if ((BASH_VERSINFO[0] >= 5 || BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 3)); then
        eval "declare -gn $2=$3"
    elif [[ -v $2 && ! -v $3 ]]; then
        printf -v "$3" %s "$2"
    fi
}

# A lot of the following one-liners were taken directly from the
# completion examples provided with the bash 2.04 source distribution

# start of section containing compspecs that can be handled within bash

# user commands see only users
complete -u groups slay w sux

# bg completes with stopped jobs
complete -A stopped -P '"%' -S '"' bg

# other job commands
complete -j -P '"%' -S '"' fg jobs disown

# readonly and unset complete with shell variables
complete -v readonly unset

# shopt completes with shopt options
complete -A shopt shopt

# unalias completes with aliases
complete -a unalias

# type and which complete on commands
complete -c command type which

# builtin completes on builtins
complete -b builtin

# start of section containing completion functions called by other functions

# Check if we're running on the given userland
# @param $1 userland to check for
# @since 2.12
_comp_userland()
{
    local userland=$(uname -s)
    [[ $userland == @(Linux|GNU/*) ]] && userland=GNU
    [[ $userland == "$1" ]]
}

# This function sets correct SysV init directories
#
# @since 2.12
_comp_sysvdirs()
{
    sysvdirs=()
    [[ -d /etc/rc.d/init.d ]] && sysvdirs+=(/etc/rc.d/init.d)
    [[ -d /etc/init.d ]] && sysvdirs+=(/etc/init.d)
    # Slackware uses /etc/rc.d
    [[ -f /etc/slackware-version ]] && sysvdirs=(/etc/rc.d)
    ((${#sysvdirs[@]}))
}

# This function checks whether we have a given program on the system.
#
# @since 2.12
_comp_have_command()
{
    # Completions for system administrator commands are installed as well in
    # case completion is attempted via `sudo command ...'.
    PATH=$PATH:/usr/sbin:/sbin:/usr/local/sbin type "$1" &>/dev/null
}

# This function checks whether a given readline variable
# is `on'.
#
# @since 2.12
_comp_readline_variable_on()
{
    [[ $(bind -v) == *$1+([[:space:]])on* ]]
}

# This function shell-quotes the argument
# @param    $1  String to be quoted
# @var[out] REPLY Resulting string
# @since 2.12
_comp_quote()
{
    REPLY=\'${1//\'/\'\\\'\'}\'
}

# shellcheck disable=SC1003
_comp_dequote__initialize()
{
    local regex_param='\$([_a-zA-Z][_a-zA-Z0-9]*|[-*@#?$!0-9_])|\$\{[!#]?([_a-zA-Z][_a-zA-Z0-9]*(\[([0-9]+|[*@])\])?|[-*@#?$!0-9_])\}'
    local regex_quoted='\\.|'\''[^'\'']*'\''|\$?"([^\"$`!]|'$regex_param'|\\.)*"|\$'\''([^\'\'']|\\.)*'\'''
    _comp_dequote__regex_safe_word='^([^\'\''"$`;&|<>()!]|'$regex_quoted'|'$regex_param')*$'
    unset -f "$FUNCNAME"
}
_comp_dequote__initialize

# This function expands a word using `eval` in a safe way.  This function can
# be typically used to get the expanded value of `${word[i]}` as `_comp_dequote
# "${word[i]}"`.  When the word contains unquoted shell special characters,
# command substitutions, and other unsafe strings, the function call fails
# before applying `eval` and REPLY is set to be the literal string.  Otherwise,
# `eval` is applied to the string to generate the result.
#
# @param    $1  String to be expanded.  A safe word consists of the following
#               sequence of substrings:
#
#               - Shell non-special characters: [^\'"$`;&|<>()!].
#               - Parameter expansions of the forms $PARAM, ${!PARAM},
#                 ${#PARAM}, ${NAME[INDEX]}, ${!NAME[INDEX]}, ${#NAME[INDEX]}
#                 where INDEX is an integer, `*` or `@`, NAME is a valid
#                 variable name [_a-zA-Z][_a-zA-Z0-9]*, and PARAM is NAME or a
#                 parameter [-*@#?$!0-9_].
#               - Quotes \?, '...', "...", $'...', and $"...".  In the double
#                 quotations, parameter expansions are allowed.
#
#               Note: To dequote values coming from `cur`, please use the
#               function `_comp_dequote_incomplete` instead because `cur` is
#               the word that is currently input and can be syntactically
#               incomplete.
#
# @var[out] REPLY  Array that contains the expanded results.  Multiple words or
#                  no words may be generated through pathname expansions.  If
#                  $1 is not a safe word, REPLY contains the literal value of
#                  $1.
#
# @return 0 if $1 is a safe word and the expansion result contains one word at
#     least, or 1 otherwise.
#
# Note: This function allows parameter expansions as safe strings, which might
# cause unexpected results:
#
# * This allows execution of arbitrary commands through extra expansions of
#   array subscripts in name references. For example,
#
#     declare -n v='dummy[$(echo xxx >/dev/tty)]'
#     echo "$v"            # This line executes the command 'echo xxx'.
#     _comp_dequote '"$v"' # This line also executes it.
#
# * This may change the internal state of the variable that has side effects.
#   For example, the state of the random number generator of RANDOM can change:
#
#     RANDOM=1234               # Set seed
#     echo "$RANDOM"            # This produces 30658.
#     RANDOM=1234               # Reset seed
#     _comp_dequote '"$RANDOM"' # This line changes the internal state.
#     echo "$RANDOM"            # This fails to reproduce 30658.
#
# We allow these parameter expansions as a part of safe strings assuming the
# referential transparency of the simple parameter expansions and the sane
# setup of the variables by the user or other frameworks that the user loads.
# @since 2.12
_comp_dequote()
{
    if [[ ${1-} =~ $_comp_dequote__regex_safe_word ]]; then
        REPLY=()                      # fallback value for failglob
        eval "REPLY=($1)" 2>/dev/null # may produce failglob
        ((${#REPLY[@]} > 0))
        return "$?"
    else
        REPLY=("${1-}")
        return 1
    fi
}

# Try to reconstruct an incomplete word and apply _comp_dequote.  In
# particular, this function can be used to dequote `cur`, which is currently
# input and can be syntactically incomplete.
#
# @param    $1  String to be expanded.  The same as _comp_dequote, but
#               incomplete backslash, single quotation, and double quotation
#               are allowed.
# @var[out] REPLY  Result.  The same as _comp_dequote.
# @since 2.17
_comp_dequote_incomplete()
{
    local _word=${1-}
    if ! [[ $_word =~ $_comp_dequote__regex_safe_word ]]; then
        # shellcheck disable=SC1003
        if [[ ${_word%'\'} =~ $_comp_dequote__regex_safe_word ]]; then
            _word=${_word%'\'}
        elif [[ $_word\' =~ $_comp_dequote__regex_safe_word ]]; then
            _word=$_word\'
        elif [[ $_word\" =~ $_comp_dequote__regex_safe_word ]]; then
            _word=$_word\"
        fi
    fi
    _comp_dequote "$_word"
}

# Unset the given variables across a scope boundary. Useful for unshadowing
# global scoped variables. Note that simply calling unset on a local variable
# will not unshadow the global variable. Rather, the result will be a local
# variable in an unset state.
# Usage: local IFS='|'; _comp_unlocal IFS
# @param $* Variable names to be unset
# @since 2.12
_comp_unlocal()
{
    if ((BASH_VERSINFO[0] >= 5)) && shopt -q localvar_unset; then
        shopt -u localvar_unset
        unset -v "$@"
        shopt -s localvar_unset
    else
        unset -v "$@"
    fi
}

# Assign variables one scope above the caller
# Usage: local varname [varname ...] &&
#        _comp_upvars [-v varname value] | [-aN varname [value ...]] ...
# Available OPTIONS:
#     -aN  Assign next N values to varname as array
#     -v   Assign single value to varname
# @return  1 if error occurs
# @see https://fvue.nl/wiki/Bash:_Passing_variables_by_reference
# @since 2.12
_comp_upvars()
{
    if ! (($#)); then
        echo "bash_completion: $FUNCNAME: usage: $FUNCNAME" \
            "[-v varname value] | [-aN varname [value ...]] ..." >&2
        return 2
    fi
    while (($#)); do
        case $1 in
            -a*)
                # Error checking
                [[ ${1#-a} ]] || {
                    echo "bash_completion: $FUNCNAME:" \
                        "\`$1': missing number specifier" >&2
                    return 1
                }
                printf %d "${1#-a}" &>/dev/null || {
                    echo bash_completion: \
                        "$FUNCNAME: \`$1': invalid number specifier" >&2
                    return 1
                }
                # Assign array of -aN elements
                # shellcheck disable=SC2015,SC2140  # TODO
                [[ $2 ]] && unset -v "$2" && eval "$2"=\(\"\$"{@:3:${1#-a}}"\"\) &&
                    shift $((${1#-a} + 2)) || {
                    echo bash_completion: \
                        "$FUNCNAME: \`$1${2+ }$2': missing argument(s)" \
                        >&2
                    return 1
                }
                ;;
            -v)
                # Assign single value
                # shellcheck disable=SC2015  # TODO
                [[ $2 ]] && unset -v "$2" && eval "$2"=\"\$3\" &&
                    shift 3 || {
                    echo "bash_completion: $FUNCNAME: $1:" \
                        "missing argument(s)" >&2
                    return 1
                }
                ;;
            *)
                echo "bash_completion: $FUNCNAME: $1: invalid option" >&2
                return 1
                ;;
        esac
    done
}

# Get the list of filenames that match with the specified glob pattern.
# This function does the globbing in a controlled environment, avoiding
# interference from user's shell options/settings or environment variables.
# @param $1 array_name  Array name
#   The array name should not start with an underscore "_", which is internally
#   used.  The array name should not be "GLOBIGNORE" or "GLOBSORT".
# @param $2 pattern     Pattern string to be evaluated.
#   This pattern string will be evaluated using "eval", so brace expansions,
#   parameter expansions, command substitutions, and other expansions will be
#   processed.  The user-provided strings should not be directly specified to
#   this argument.
# @return 0 if at least one path is generated, 1 if no path is generated, or 2
#   if the usage is incorrect.
# @since 2.12
_comp_expand_glob()
{
    if (($# != 2)); then
        printf 'bash-completion: %s: unexpected number of arguments\n' "$FUNCNAME" >&2
        printf 'usage: %s ARRAY_NAME PATTERN\n' "$FUNCNAME" >&2
        return 2
    elif [[ $1 == @(GLOBIGNORE|GLOBSORT|_*|*[^_a-zA-Z0-9]*|[0-9]*|'') ]]; then
        printf 'bash-completion: %s: invalid array name "%s"\n' "$FUNCNAME" "$1" >&2
        return 2
    fi

    # Save and adjust the settings.
    local _original_opts=$SHELLOPTS:$BASHOPTS
    set +o noglob
    shopt -s nullglob
    shopt -u failglob dotglob

    # Also the user's GLOBIGNORE and GLOBSORT (bash >= 5.3) may affect the
    # result of pathname expansions.
    local GLOBIGNORE="" GLOBSORT=name

    # To canonicalize the sorting order of the generated paths, we set
    # LC_COLLATE=C and unset LC_ALL while preserving LC_CTYPE.
    local LC_COLLATE=C LC_CTYPE=${LC_ALL:-${LC_CTYPE:-${LANG-}}} LC_ALL=

    eval -- "$1=()" # a fallback in case that the next line fails.
    eval -- "$1=($2)"

    # Restore the settings.  Note: Changing GLOBIGNORE affects the state of
    # "shopt -q dotglob", so we need to explicitly restore the original state
    # of "shopt -q dotglob".
    _comp_unlocal GLOBIGNORE
    if [[ :$_original_opts: == *:dotglob:* ]]; then
        shopt -s dotglob
    else
        shopt -u dotglob
    fi
    [[ :$_original_opts: == *:nullglob:* ]] || shopt -u nullglob
    [[ :$_original_opts: == *:failglob:* ]] && shopt -s failglob
    [[ :$_original_opts: == *:noglob:* ]] && set -o noglob
    eval "((\${#$1[@]}))"
}

# Split a string and assign to an array.  This function basically performs
# `IFS=<sep>; <array_name>=(<text>)` but properly handles saving/restoring the
# state of `IFS` and the shell option `noglob`.  A naive splitting by
# `arr=(...)` suffers from unexpected IFS and pathname expansions, so one
# should prefer this function to such naive splitting.
# OPTIONS
#   -a      Append to the array
#   -F sep  Set a set of separator characters (used as IFS).  The default
#           separator is $' \t\n'
#   -l      The same as -F $'\n'
# @param $1 array_name  The array name
#   The array name should not start with an underscores "_", which is
#   internally used.  The array name should not be either "IFS" or
#   "OPT{IND,ARG,ERR}".
# @param $2 text        The string to split
# @return 2 when the usage is wrong, 0 when one or more completions are
#   generated, or 1 when the execution succeeds but no candidates are
#   generated.
# @since 2.12
_comp_split()
{
    local _append="" IFS=$' \t\n'

    local OPTIND=1 OPTARG="" OPTERR=0 _opt
    while getopts ':alF:' _opt "$@"; do
        case $_opt in
            a) _append=set ;;
            l) IFS=$'\n' ;;
            F) IFS=$OPTARG ;;
            *)
                echo "bash_completion: $FUNCNAME: usage error" >&2
                return 2
                ;;
        esac
    done
    shift "$((OPTIND - 1))"
    if (($# != 2)); then
        printf '%s\n' "bash_completion: $FUNCNAME: unexpected number of arguments" >&2
        printf '%s\n' "usage: $FUNCNAME [-al] [-F SEP] ARRAY_NAME TEXT" >&2
        return 2
    elif [[ $1 == @(*[^_a-zA-Z0-9]*|[0-9]*|''|_*|IFS|OPTIND|OPTARG|OPTERR) ]]; then
        printf '%s\n' "bash_completion: $FUNCNAME: invalid array name '$1'" >&2
        return 2
    fi

    local _original_opts=$SHELLOPTS
    set -o noglob

    local _old_size _new_size
    if [[ $_append ]]; then
        eval "$1+=()" # in case $1 is unset
        eval "_old_size=\${#$1[@]}"
        eval "$1+=(\$2)"
    else
        _old_size=0
        eval "$1=(\$2)"
    fi
    eval "_new_size=\${#$1[@]}"

    [[ :$_original_opts: == *:noglob:* ]] || set +o noglob
    ((_new_size > _old_size))
}

# Helper function for _comp_compgen
# @var[in] $?
# @var[in] _var
# @var[in] _append
# @var[in] _upvars
# @return original $?
_comp_compgen__error_fallback()
{
    local _status=$?
    if [[ $_append ]]; then
        # make sure existence of variable
        eval -- "$_var+=()"
    else
        eval -- "$_var=()"
    fi
    if ((${#_upvars[@]})); then
        _comp_unlocal "${_upvars[@]}"
        _upvars=()
    fi
    return "$_status"
}

# Provide a common interface to generate completion candidates in COMPREPLY or
# in a specified array.
# OPTIONS
#   -a        Append to the array
#   -v arr    Store the results to the array ARR. The default is `COMPREPLY`.
#             The array name should not start with an underscores "_", which is
#             internally used.  The array name should not be any of "cur",
#             "IFS" or "OPT{IND,ARG,ERR}".
#   -U var    Unlocalize VAR before performing the assignments.  This option
#             can be specified multiple times to register multiple variables.
#             This option is supposed to be used in implementing a generator
#             (G1) when G1 defines a local variable name that does not start
#             with `_`.  In such a case, when the target variable specified to
#             G1 by `-v VAR1` conflicts with the local variable, the assignment
#             to the target variable fails to propagate outside G1.  To avoid
#             such a situation, G1 can call `_comp_compgen` with `-U VAR` to
#             unlocalize `VAR` before accessing the target variable.  For a
#             builtin compgen call (i.e., _comp_compgen [options] -- options),
#             VAR is unlocalized after calling the builtin `compgen` but before
#             assigning results to the target array.  For a generator call
#             (i.e., _comp_compgen [options] G2 ...), VAR is unlocalized before
#             calling the child generator function `_comp_compgen_G2`.
#   -c cur    Set a word used as a prefix to filter the completions.  The
#             default is ${cur-}.
#   -R        The same as -c ''.  Use raw outputs without filtering.
#   -C dir    Evaluate compgen/generator in the specified directory.
#   -P prefix Prepend the prefix to the generated completions.  Unlike `compgen
#             -P prefix`, this prefix is subject to filtering by `cur`.  When a
#             non-empty prefix is specified, first `cur` is tested whether it
#             is consistent with the prefix.  Then, `cur` is reduced for the
#             part excluding the prefix, and the normal completion generation
#             is performed.  Finally, the prefix is prepended to generated
#             completions.
# @var[in,opt] cur  Used as the default value of a prefix to filter the
#   completions.
#
# Usage #1: _comp_compgen [-alR|-F sep|-v arr|-c cur|-C dir] -- options...
# Call `compgen` with the specified arguments and store the results in the
# specified array.  This function essentially performs arr=($(compgen args...))
# but properly handles shell options, IFS, etc. using _comp_split.  This
# function is equivalent to `_comp_split [-a] -l arr "$(IFS=sep; compgen
# args... -- cur)"`, but this pattern is frequent in the codebase and is good
# to separate out as a function for the possible future implementation change.
# OPTIONS
#   -F sep  Set a set of separator characters (used as IFS in evaluating
#           `compgen').  The default separator is $' \t\n'.  Note that this is
#           not the set of separators to delimit output of `compgen', but the
#           separators in evaluating the expansions of `-W '...'`, etc.  The
#           delimiter of the output of `compgen` is always a newline.
#   -l      The same as -F $'\n'.  Use lines as words in evaluating compgen.
# @param $1... options  Arguments that are passed to compgen (if $1 starts with
#   a hyphen `-`).
#
#   Note: References to positional parameters $1, $2, ... (such as -W '$1')
#   will not work as expected because these reference the arguments of
#   `_comp_compgen' instead of those of the caller function.  When there are
#   needs to reference them, save the arguments to an array and reference the
#   array instead.
#
#   Note: The array option `-V arr` in bash >= 5.3 should be instead specified
#   as `-v arr` as a part of the `_comp_compgen` options.
# @return  True (0) if at least one completion is generated, False (1) if no
#   completion is generated, or 2 with an incorrect usage.
#
# Usage #2: _comp_compgen [-aR|-v arr|-c cur|-C dir|-i cmd|-x cmd] name args...
# Call the generator `_comp_compgen_NAME ARGS...` with the specified options.
# This provides a common interface to call the functions `_comp_compgen_NAME`,
# which produce completion candidates, with custom options [-alR|-v arr|-c
# cur].  The option `-F sep` is not used with this usage.
# OPTIONS
#   -x cmd  Call exported generator `_comp_xfunc_CMD_compgen_NAME`
#   -i cmd  Call internal generator `_comp_cmd_CMD__compgen_NAME`
# @param $1... name args  Calls the function _comp_compgen_NAME with the
#   specified ARGS (if $1 does not start with a hyphen `-`).  The options
#   [-alR|-v arr|-c cur] are inherited by the child calls of `_comp_compgen`
#   inside `_comp_compgen_NAME` unless the child call `_comp_compgen` receives
#   overriding options.
# @var[in,opt,internal] _comp_compgen__append
# @var[in,opt,internal] _comp_compgen__var
# @var[in,opt,internal] _comp_compgen__cur
#   These variables are internally used to pass the effect of the options
#   [-alR|-v arr|-c cur] to the child calls of `_comp_compgen` in
#   `_comp_compgen_NAME`.
# @return   Exit status of the generator.
#
# @remarks When no options are supplied to _comp_compgen, `_comp_compgen NAME
# args` is equivalent to the direct call `_comp_compgen_NAME args`.  As the
# direct call is slightly more efficient, the direct call is preferred over
# calling it through `_comp_compgen`.
#
# @remarks Design `_comp_compgen_NAME`: a function that produce completions can
# be defined with the name _comp_compgen_NAME.  The function is supposed to
# generate completions by calling `_comp_compgen`.  To reflect the options
# specified to the outer calls of `_comp_compgen`, the function should not
# directly modify `COMPREPLY`.  To add words, one can call
#
#     _comp_compgen -- -W '"${words[@]}"'
#
# To directly add words without filtering by `cur`, one can call
#
#     _comp_compgen -R -- -W '"${words[@]}"'
#
# or use the utility `_comp_compgen_set`:
#
#     _comp_compgen_set "${words[@]}"
#
# Other nested calls of _comp_compgen can also be used.  The function is
# supposed to replace the existing content of the array by default to allow the
# caller control whether to replace or append by the option `-a`.
#
# @since 2.12
_comp_compgen()
{
    local _append=
    local _var=
    local _cur=${_comp_compgen__cur-${cur-}}
    local _dir=""
    local _prefix=""
    local _ifs=$' \t\n' _has_ifs=""
    local _icmd="" _xcmd=""
    local -a _upvars=()

    local _old_nocasematch=""
    if shopt -q nocasematch; then
        _old_nocasematch=set
        shopt -u nocasematch
    fi
    local OPTIND=1 OPTARG="" OPTERR=0 _opt
    while getopts ':av:U:Rc:C:P:lF:i:x:' _opt "$@"; do
        case $_opt in
            a) _append=set ;;
            v)
                if [[ $OPTARG == @(*[^_a-zA-Z0-9]*|[0-9]*|''|_*|IFS|OPTIND|OPTARG|OPTERR|cur) ]]; then
                    printf 'bash_completion: %s: -v: invalid array name `%s'\''\n' "$FUNCNAME" "$OPTARG" >&2
                    return 2
                fi
                _var=$OPTARG
                ;;
            U)
                if [[ $OPTARG == @(*[^_a-zA-Z0-9]*|[0-9]*|'') ]]; then
                    printf 'bash_completion: %s: -U: invalid variable name `%s'\''\n' "$FUNCNAME" "$OPTARG" >&2
                    return 2
                elif [[ $OPTARG == @(_*|IFS|OPTIND|OPTARG|OPTERR|cur) ]]; then
                    printf 'bash_completion: %s: -U: unnecessary to mark `%s'\'' as upvar\n' "$FUNCNAME" "$OPTARG" >&2
                    return 2
                fi
                _upvars+=("$OPTARG")
                ;;
            c) _cur=$OPTARG ;;
            R) _cur="" ;;
            C)
                if [[ ! $OPTARG ]]; then
                    printf 'bash_completion: %s: -C: invalid directory name `%s'\''\n' "$FUNCNAME" "$OPTARG" >&2
                    return 2
                fi
                _dir=$OPTARG
                ;;
            P) _prefix=$OPTARG ;;
            l) _has_ifs=set _ifs=$'\n' ;;
            F) _has_ifs=set _ifs=$OPTARG ;;
            [ix])
                if [[ ! $OPTARG ]]; then
                    printf 'bash_completion: %s: -%s: invalid command name `%s'\''\n' "$FUNCNAME" "$_opt" "$OPTARG" >&2
                    return 2
                elif [[ $_icmd ]]; then
                    printf 'bash_completion: %s: -%s: `-i %s'\'' is already specified\n' "$FUNCNAME" "$_opt" "$_icmd" >&2
                    return 2
                elif [[ $_xcmd ]]; then
                    printf 'bash_completion: %s: -%s: `-x %s'\'' is already specified\n' "$FUNCNAME" "$_opt" "$_xcmd" >&2
                    return 2
                fi
                ;;&
            i) _icmd=$OPTARG ;;
            x) _xcmd=$OPTARG ;;
            *)
                printf 'bash_completion: %s: usage error\n' "$FUNCNAME" >&2
                return 2
                ;;
        esac
    done
    [[ $_old_nocasematch ]] && shopt -s nocasematch
    shift "$((OPTIND - 1))"
    if (($# == 0)); then
        printf 'bash_completion: %s: unexpected number of arguments\n' "$FUNCNAME" >&2
        printf 'usage: %s [-alR|-F SEP|-v ARR|-c CUR] -- ARGS...' "$FUNCNAME" >&2
        return 2
    fi
    if [[ ! $_var ]]; then
        # Inherit _append and _var only when -v var is unspecified.
        _var=${_comp_compgen__var-COMPREPLY}
        [[ $_append ]] || _append=${_comp_compgen__append-}
    fi

    local _prefix_fail=""
    if [[ $_prefix ]]; then
        if [[ $_cur == "$_prefix"* ]]; then
            _cur=${_cur#"$_prefix"}
        elif [[ $_prefix == "$_cur"* ]]; then
            _cur=""
        else
            # No completions are generated because the current word does not match
            # the prefix.
            _prefix_fail=set
        fi
    fi

    if [[ $1 != -* ]]; then
        # usage: _comp_compgen [options] NAME args
        if [[ $_has_ifs ]]; then
            printf 'bash_completion: %s: `-l'\'' and `-F sep'\'' are not supported for generators\n' "$FUNCNAME" >&2
            return 2
        fi

        local -a _generator
        if [[ $_icmd ]]; then
            _generator=("_comp_cmd_${_icmd//[^a-zA-Z0-9_]/_}__compgen_$1")
        elif [[ $_xcmd ]]; then
            _generator=(_comp_xfunc "$_xcmd" "compgen_$1")
        else
            _generator=("_comp_compgen_$1")
        fi
        if ! declare -F -- "${_generator[0]}" &>/dev/null; then
            printf 'bash_completion: %s: unrecognized generator `%s'\'' (function %s not found)\n' "$FUNCNAME" "$1" "${_generator[0]}" >&2
            return 2
        fi
        shift

        if [[ $_prefix_fail ]]; then
            _comp_compgen__error_fallback
            return 1
        fi

        _comp_compgen__call_generator "$@"
    else
        # usage: _comp_compgen [options] -- [compgen_options]
        if [[ $_icmd || $_xcmd ]]; then
            printf 'bash_completion: %s: generator name is unspecified for `%s'\''\n' "$FUNCNAME" "${_icmd:+-i $_icmd}${_xcmd:+x $_xcmd}" >&2
            return 2
        fi

        # Note: $* in the below checks would be affected by uncontrolled IFS in
        # bash >= 5.0, so we need to set IFS to the normal value.  The behavior
        # in bash < 5.0, where unquoted $* in conditional command did not honor
        # IFS, was a bug.
        # Note: Also, ${_cur:+-- "$_cur"} and ${_append:+-a} would be affected
        # by uncontrolled IFS.
        local IFS=$' \t\n'
        # Note: extglob *\$?(\{)[0-9]* can be extremely slow when the string
        # "${*:2:_nopt}" becomes longer, so we test \$[0-9] and \$\{[0-9]
        # separately.
        if [[ $* == *\$[0-9]* || $* == *\$\{[0-9]* ]]; then
            printf 'bash_completion: %s: positional parameter $1, $2, ... do not work inside this function\n' "$FUNCNAME" >&2
            return 2
        fi

        if [[ $_prefix_fail ]]; then
            _comp_compgen__error_fallback
            return 1
        fi

        _comp_compgen__call_builtin "$@"
    fi
}

# Helper function for _comp_compgen.  This function calls a generator.
# @param $1... generator_args
# @var[in] _dir
# @var[in] _cur
# @arr[in] _generator
# @arr[in] _upvars
# @var[in] _append
# @var[in] _var
_comp_compgen__call_generator()
{
    if [[ $_dir ]]; then
        local _original_pwd=$PWD
        local PWD=${PWD-} OLDPWD=${OLDPWD-}
        # Note: We also redirect stdout because `cd` may output the target
        # directory to stdout when CDPATH is set.
        command cd -- "$_dir" &>/dev/null ||
            {
                _comp_compgen__error_fallback
                return
            }
    fi

    ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}"

    local _comp_compgen__append=$_append
    local _comp_compgen__var=$_var
    local _comp_compgen__cur=$_cur cur=$_cur
    if [[ $_prefix ]]; then
        local -a tmp=()
        local _comp_compgen__var=tmp
        local _comp_compgen__append=""
    fi
    # Note: we use $1 as a part of a function name, and we use $2... as
    # arguments to the function if any.
    # shellcheck disable=SC2145
    "${_generator[@]}" "$@"
    local _status=$?
    if [[ $_prefix ]]; then
        local _i
        for _i in "${!tmp[@]}"; do
            tmp[_i]=$_prefix${tmp[_i]}
        done
        _comp_compgen -RU tmp ${_append:+-a} -v "$_var" -- -W '"${tmp[@]}"'
    fi

    # Go back to the original directory.
    # Note: Failure of this line results in the change of the current
    # directory visible to the user.  We intentionally do not redirect
    # stderr so that the error message appear in the terminal.
    # shellcheck disable=SC2164
    [[ $_dir ]] && command cd -- "$_original_pwd"

    return "$_status"
}

# Helper function for _comp_compgen.  This function calls the builtin compgen.
# @param $1... compgen_args
# @var[in] _dir
# @var[in] _ifs
# @var[in] _cur
# @arr[in] _upvars
# @var[in] _append
# @var[in] _var
if ((BASH_VERSINFO[0] > 5 || BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3)); then
    # bash >= 5.3 has `compgen -V array_name`
    _comp_compgen__call_builtin()
    {
        if [[ $_dir ]]; then
            local _original_pwd=$PWD
            local PWD=${PWD-} OLDPWD=${OLDPWD-}
            # Note: We also redirect stdout because `cd` may output the target
            # directory to stdout when CDPATH is set.
            command cd -- "$_dir" &>/dev/null || {
                _comp_compgen__error_fallback
                return
            }
        fi

        local -a _result=()

        # Note: We specify -X '' to exclude empty completions to make the
        # behavior consistent with the implementation for Bash < 5.3 where
        # `_comp_split -l` removes empty lines.  If the caller specifies -X
        # pat, the effect of -X '' is overwritten by the specified one.
        IFS=$_ifs compgen -V _result -X '' "$@" ${_cur:+-- "$_cur"} || {
            _comp_compgen__error_fallback
            # shellcheck disable=SC2164
            [[ $_dir ]] && command cd -- "$_original_pwd"
            return
        }

        # Go back to the original directory.
        # Note: Failure of this line results in the change of the current
        # directory visible to the user.  We intentionally do not redirect
        # stderr so that the error message appear in the terminal.
        # shellcheck disable=SC2164
        [[ $_dir ]] && command cd -- "$_original_pwd"

        ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}"
        ((${#_result[@]})) || return

        if [[ $_prefix ]]; then
            local _i
            for _i in "${!_result[@]}"; do
                _result[_i]=$_prefix${_result[_i]}
            done
        fi
        if [[ $_append ]]; then
            eval -- "$_var+=(\"\${_result[@]}\")"
        else
            eval -- "$_var=(\"\${_result[@]}\")"
        fi
        return
    }
else
    _comp_compgen__call_builtin()
    {
        local _result
        _result=$(
            if [[ $_dir ]]; then
                # Note: We also redirect stdout because `cd` may output the target
                # directory to stdout when CDPATH is set.
                command cd -- "$_dir" &>/dev/null || return
            fi
            IFS=$_ifs compgen "$@" ${_cur:+-- "$_cur"}
        ) || {
            _comp_compgen__error_fallback
            return
        }

        ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}"

        if [[ $_prefix ]]; then
            _comp_split -l ${_append:+-a} "$_var" \
                "$(IFS=$'\n' compgen -W '$_result' -P "$_prefix")"
        else
            _comp_split -l ${_append:+-a} "$_var" "$_result"
        fi
    }
fi

# usage: _comp_compgen_set [words...]
# Reset COMPREPLY with the specified WORDS.  If no arguments are specified, the
# array is cleared.
#
# When an array name is specified by `-v VAR` in a caller _comp_compgen, the
# array is reset instead of COMPREPLY.  When the `-a` flag is specified in a
# caller _comp_compgen, the words are appended to the existing elements of the
# array instead of replacing the existing elements.  This function ignores
# ${cur-} or the prefix specified by `-v CUR`.
# @return 0 if at least one completion is generated, or 1 otherwise.
# @since 2.12
_comp_compgen_set()
{
    local _append=${_comp_compgen__append-}
    local _var=${_comp_compgen__var-COMPREPLY}
    eval -- "$_var${_append:++}=(\"\$@\")"
    (($#))
}

# Simply split the text and generate completions.  This function should be used
# instead of `_comp_compgen -- -W "$(command)"`, which is vulnerable because
# option -W evaluates the shell expansions included in the option argument.
# Options:
#   -F sep  Specify the separators. The default is $' \t\n'
#   -l      The same as -F $'\n'
#   -X arg  The same as the compgen option -X.
#   -S arg  The same as the compgen option -S.
#   -P arg  The same as the compgen option -P.
#   -o arg  The same as the compgen option -o.
# @param $1 String to split
# @return 0 if at least one completion is generated, or 1 otherwise.
# @since 2.12
_comp_compgen_split()
{
    local _ifs=$' \t\n'
    local -a _compgen_options=()

    local OPTIND=1 OPTARG="" OPTERR=0 _opt
    while getopts ':lF:X:S:P:o:' _opt "$@"; do
        case $_opt in
            l) _ifs=$'\n' ;;
            F) _ifs=$OPTARG ;;
            [XSPo]) _compgen_options+=("-$_opt" "$OPTARG") ;;
            *)
                printf 'bash_completion: usage: %s [-l|-F sep] [--] str\n' "$FUNCNAME" >&2
                return 2
                ;;
        esac
    done
    shift "$((OPTIND - 1))"
    if (($# != 1)); then
        printf 'bash_completion: %s: unexpected number of arguments.\n' "$FUNCNAME" >&2
        printf 'usage: %s [-l|-F sep] [--] str' "$FUNCNAME" >&2
        return 2
    fi

    local input=$1 IFS=$' \t\n'
    _comp_compgen -F "$_ifs" -U input -- ${_compgen_options[@]+"${_compgen_options[@]}"} -W '$input'
}

# Check if the argument looks like a path.
# @param $1 thing to check
# @return True (0) if it does, False (> 0) otherwise
# @since 2.12
_comp_looks_like_path()
{
    [[ ${1-} == @(*/|[.~])* ]]
}

# Reassemble command line words, excluding specified characters from the
# list of word completion separators (COMP_WORDBREAKS).
# @param $1 chars  Characters out of $COMP_WORDBREAKS which should
#     NOT be considered word breaks. This is useful for things like scp where
#     we want to return host:path and not only path, so we would pass the
#     colon (:) as $1 here.
# @param $2 words  Name of variable to return words to
# @param $3 cword  Name of variable to return cword to
#
_comp__reassemble_words()
{
    local exclude="" i j line ref
    # Exclude word separator characters?
    if [[ $1 ]]; then
        # Yes, exclude word separator characters;
        # Exclude only those characters, which were really included
        exclude="[${1//[^$COMP_WORDBREAKS]/}]"
    fi

    # Default to cword unchanged
    printf -v "$3" %s "$COMP_CWORD"
    # Are characters excluded which were former included?
    if [[ $exclude ]]; then
        # Yes, list of word completion separators has shrunk;
        line=$COMP_LINE
        # Re-assemble words to complete
        for ((i = 0, j = 0; i < ${#COMP_WORDS[@]}; i++, j++)); do
            # Is current word not word 0 (the command itself) and is word not
            # empty and is word made up of just word separator characters to
            # be excluded and is current word not preceded by whitespace in
            # original line?
            while [[ $i -gt 0 && ${COMP_WORDS[i]} == +($exclude) ]]; do
                # Is word separator not preceded by whitespace in original line
                # and are we not going to append to word 0 (the command
                # itself), then append to current word.
                [[ $line != [[:blank:]]* ]] && ((j >= 2)) && ((j--))
                # Append word separator to current or new word
                ref="$2[$j]"
                printf -v "$ref" %s "${!ref-}${COMP_WORDS[i]}"
                # Indicate new cword
                ((i == COMP_CWORD)) && printf -v "$3" %s "$j"
                # Remove optional whitespace + word separator from line copy
                line=${line#*"${COMP_WORDS[i]}"}
                # Indicate next word if available, else end *both* while and
                # for loop
                if ((i < ${#COMP_WORDS[@]} - 1)); then
                    ((i++))
                else
                    break 2
                fi
                # Start new word if word separator in original line is
                # followed by whitespace.
                [[ $line == [[:blank:]]* ]] && ((j++))
            done
            # Append word to current word
            ref="$2[$j]"
            printf -v "$ref" %s "${!ref-}${COMP_WORDS[i]}"
            # Remove optional whitespace + word from line copy
            line=${line#*"${COMP_WORDS[i]}"}
            # Indicate new cword
            ((i == COMP_CWORD)) && printf -v "$3" %s "$j"
        done
        ((i == COMP_CWORD)) && printf -v "$3" %s "$j"
    else
        # No, list of word completions separators hasn't changed;
        for i in "${!COMP_WORDS[@]}"; do
            printf -v "$2[i]" %s "${COMP_WORDS[i]}"
        done
    fi
}

# @param $1 exclude  Characters out of $COMP_WORDBREAKS which should NOT be
#     considered word breaks. This is useful for things like scp where
#     we want to return host:path and not only path, so we would pass the
#     colon (:) as $1 in this case.
# @param $2 words  Name of variable to return words to
# @param $3 cword  Name of variable to return cword to
# @param $4 cur  Name of variable to return current word to complete to
# @see _comp__reassemble_words()
_comp__get_cword_at_cursor()
{
    local cword words=()
    _comp__reassemble_words "$1" words cword

    local i cur="" index=$COMP_POINT lead=${COMP_LINE:0:COMP_POINT}
    # Cursor not at position 0 and not led by just space(s)?
    if [[ $index -gt 0 && ($lead && ${lead//[[:space:]]/}) ]]; then
        cur=$COMP_LINE
        for ((i = 0; i <= cword; ++i)); do
            # Current word fits in $cur, and $cur doesn't match cword?
            while [[ ${#cur} -ge ${#words[i]} &&
                ${cur:0:${#words[i]}} != "${words[i]-}" ]]; do
                # Strip first character
                cur=${cur:1}
                # Decrease cursor position, staying >= 0
                ((index > 0)) && ((index--))
            done

            # Does found word match cword?
            if ((i < cword)); then
                # No, cword lies further;
                local old_size=${#cur}
                cur=${cur#"${words[i]}"}
                local new_size=${#cur}
                ((index -= old_size - new_size))
            fi
        done
        # Clear $cur if just space(s)
        [[ $cur && ! ${cur//[[:space:]]/} ]] && cur=
        # Zero $index if negative
        ((index < 0)) && index=0
    fi

    local IFS=$' \t\n'
    local "$2" "$3" "$4" && _comp_upvars -a"${#words[@]}" "$2" ${words[@]+"${words[@]}"} \
        -v "$3" "$cword" -v "$4" "${cur:0:index}"
}

# Get the word to complete and optional previous words.
# This is nicer than ${COMP_WORDS[COMP_CWORD]}, since it handles cases
# where the user is completing in the middle of a word.
# (For example, if the line is "ls foobar",
# and the cursor is here -------->   ^
# Also one is able to cross over possible wordbreak characters.
# Usage: _comp_get_words [OPTIONS] [VARNAMES]
# Available VARNAMES:
#     cur         Return cur via $cur
#     prev        Return prev via $prev
#     words       Return words via $words
#     cword       Return cword via $cword
#
# Available OPTIONS:
#     -n EXCLUDE  Characters out of $COMP_WORDBREAKS which should NOT be
#                 considered word breaks. This is useful for things like scp
#                 where we want to return host:path and not only path, so we
#                 would pass the colon (:) as -n option in this case.
#     -c VARNAME  Return cur via $VARNAME
#     -p VARNAME  Return prev via $VARNAME
#     -w VARNAME  Return words via $VARNAME
#     -i VARNAME  Return cword via $VARNAME
#
# Example usage:
#
#    $ _comp_get_words -n : cur prev
#
# @since 2.12
_comp_get_words()
{
    local exclude="" flag i OPTIND=1
    local cur cword words=()
    local upargs=() upvars=() vcur="" vcword="" vprev="" vwords=""

    while getopts "c:i:n:p:w:" flag "$@"; do
        case $flag in
            [cipw])
                if [[ $OPTARG != [a-zA-Z_]*([a-zA-Z_0-9])?(\[*\]) ]]; then
                    echo "bash_completion: $FUNCNAME: -$flag: invalid variable name \`$OPTARG'" >&2
                    return 1
                fi
                ;;&
            c) vcur=$OPTARG ;;
            i) vcword=$OPTARG ;;
            n) exclude=$OPTARG ;;
            p) vprev=$OPTARG ;;
            w) vwords=$OPTARG ;;
            *)
                echo "bash_completion: $FUNCNAME: usage error" >&2
                return 1
                ;;
        esac
    done
    while [[ $# -ge $OPTIND ]]; do
        case ${!OPTIND} in
            cur) vcur=cur ;;
            prev) vprev=prev ;;
            cword) vcword=cword ;;
            words) vwords=words ;;
            *)
                echo "bash_completion: $FUNCNAME: \`${!OPTIND}':" \
                    "unknown argument" >&2
                return 1
                ;;
        esac
        ((OPTIND += 1))
    done

    _comp__get_cword_at_cursor "${exclude-}" words cword cur

    [[ $vcur ]] && {
        upvars+=("$vcur")
        upargs+=(-v "$vcur" "$cur")
    }
    [[ $vcword ]] && {
        upvars+=("$vcword")
        upargs+=(-v "$vcword" "$cword")
    }
    [[ $vprev ]] && {
        local value=""
        ((cword >= 1)) && value=${words[cword - 1]}
        upvars+=("$vprev")
        upargs+=(-v "$vprev" "$value")
    }
    [[ $vwords ]] && {
        # Note: bash < 4.4 has a bug that all the elements are connected with
        # ${v+"$@"} when IFS does not contain whitespace.
        local IFS=$' \t\n'
        upvars+=("$vwords")
        upargs+=(-a"${#words[@]}" "$vwords" ${words+"${words[@]}"})
    }

    ((${#upvars[@]})) && local "${upvars[@]}" && _comp_upvars "${upargs[@]}"
}

# Generate the specified items after left-trimming with the word-to-complete
# containing a colon (:).  If the word-to-complete does not contain a colon,
# this generates the specified items without modifications.
# @param $@     items to generate
# @var[in] cur  current word to complete
#
# @remarks In Bash, with a colon in COMP_WORDBREAKS, words containing colons
# are always completed as entire words if the word to complete contains a
# colon.  This function fixes this behavior by removing the
# colon-containing-prefix from the items.
#
# The preferred solution is to remove the colon (:) from COMP_WORDBREAKS in
# your .bashrc:
#
#    # Remove colon (:) from list of word completion separators
#    COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
#
# See also: Bash FAQ - E13) Why does filename completion misbehave if a colon
# appears in the filename? - https://tiswww.case.edu/php/chet/bash/FAQ
#
# @since 2.12
_comp_compgen_ltrim_colon()
{
    (($#)) || return 0
    local -a _tmp
    _tmp=("$@")
    if [[ $cur == *:* && $COMP_WORDBREAKS == *:* ]]; then
        # Remove colon-word prefix from items
        local _colon_word=${cur%"${cur##*:}"}
        _tmp=("${_tmp[@]#"$_colon_word"}")
    fi
    _comp_compgen_set "${_tmp[@]}"
}

# If the word-to-complete contains a colon (:), left-trim COMPREPLY items with
# word-to-complete.
#
# @param $1 current word to complete (cur)
# @var[in,out] COMPREPLY
#
# @since 2.12
_comp_ltrim_colon_completions()
{
    ((${#COMPREPLY[@]})) || return 0
    _comp_compgen -c "$1" ltrim_colon "${COMPREPLY[@]}"
}

# This function quotes the argument in a way so that readline dequoting
# results in the original argument.  This is necessary for at least
# `compgen` which requires its arguments quoted/escaped:
#
#     $ ls "a'b/"
#     c
#     $ compgen -f "a'b/"       # Wrong, doesn't return output
#     $ compgen -f "a\'b/"      # Good
#     a\'b/c
#
# See also:
# - https://lists.gnu.org/archive/html/bug-bash/2009-03/msg00155.html
# - https://www.mail-archive.com/bash-completion-devel@lists.alioth.debian.org/msg01944.html
# @param $1      Argument to quote
# @var[out] REPLY  Quoted result is stored in this variable
# @since 2.12
# shellcheck disable=SC2178 # The assignment is not intended for the global "REPLY"
_comp_quote_compgen()
{
    if [[ $1 == \'* ]]; then
        # Leave out first character
        REPLY=${1:1}
    else
        printf -v REPLY %q "$1"

        # If result becomes quoted like this: $'string', re-evaluate in order
        # to drop the additional quoting.  See also:
        # https://www.mail-archive.com/bash-completion-devel@lists.alioth.debian.org/msg01942.html
        if [[ $REPLY == \$\'*\' ]]; then
            local value=${REPLY:2:-1} # Strip beginning $' and ending '.
            value=${value//'%'/%%}    # Escape % for printf format.
            # shellcheck disable=SC2059
            printf -v REPLY "$value" # Decode escape sequences of \....
        fi
    fi
}

# This function performs file and directory completion. It's better than
# simply using 'compgen -f', because it honours spaces in filenames.
# @param $1 Complete filenames matching `.$1' and the uppercase version of it.
#           Ignored with `-d`.
# OPTIONS
#   -d      Complete only on directories
#   -f      Perform `compopt -o filenames` modifications manually.  This
#           suffixes a slash to a directory name.  This can be combined with
#           the `-C dir` option to `_comp_compgen`, where the generated
#           filenames do not exist in the current working directory and Bash
#           fails to properly detect the filenames.
# @return 0 if at least one completion is generated, or 1 otherwise.
#
# @since 2.12
_comp_compgen_filedir()
{
    _comp_compgen_tilde && return

    local -a toks
    local _dir="" _filenames=""
    local OPTIND=1 OPTARG="" OPTERR=0 _opt
    while getopts ":df" _opt "$@"; do
        case $_opt in
            d) _dir=set ;;
            f) _filenames=set ;;
            *)
                printf "bash_completion: %s: usage error\n" "$FUNCNAME" >&2
                return 2
                ;;
        esac
    done
    shift "$((OPTIND - 1))"
    local _arg=${1-}

    if [[ $_dir ]]; then
        _comp_compgen -v toks -- -d
    else
        local REPLY
        _comp_quote_compgen "${cur-}"
        local _quoted=$REPLY
        _comp_unlocal REPLY

        # work around bash-4.2 where compgen -f "''" produces nothing.
        [[ $_quoted == "''" ]] && _quoted=""

        # Munge xspec to contain uppercase version too
        # https://lists.gnu.org/archive/html/bug-bash/2010-09/msg00036.html
        # news://news.gmane.io/4C940E1C.1010304@case.edu
        local _xspec=${_arg:+"!*.@($_arg|${_arg^^})"} _plusdirs=()

        # Use plusdirs to get dir completions if we have a xspec; if we don't,
        # there's no need, dirs come along with other completions. Don't use
        # plusdirs quite yet if fallback is in use though, in order to not ruin
        # the fallback condition with the "plus" dirs.
        local _opts=(-f -X "$_xspec")
        [[ $_xspec ]] && _plusdirs=(-o plusdirs)
        [[ ${BASH_COMPLETION_FILEDIR_FALLBACK-} || ! ${_plusdirs-} ]] ||
            _opts+=("${_plusdirs[@]}")

        _comp_compgen -v toks -c "$_quoted" -- "${_opts[@]}"

        # Try without filter if it failed to produce anything and configured to
        [[ ${BASH_COMPLETION_FILEDIR_FALLBACK-} &&
            $_arg && ${#toks[@]} -lt 1 ]] &&
            _comp_compgen -av toks -c "$_quoted" -- \
                -f ${_plusdirs+"${_plusdirs[@]}"}
    fi

    if ((${#toks[@]} != 0)); then
        # Remove . and .. (as well as */. and */..) from suggestions, unless
        # .. or */.. was typed explicitly by the user (for users who use
        # tab-completion to append a slash after '..')
        if [[ $cur != ?(*/).. ]]; then
            _comp_compgen -Rv toks -- -X '?(*/)@(.|..)' -W '"${toks[@]}"'
        fi
    fi

    if ((${#toks[@]} != 0)); then
        # compopt 2>/dev/null for direct invocation, e.g. in
        # _comp_compgen_filedir unit test
        if [[ $_filenames ]]; then
            local i
            for i in "${!toks[@]}"; do
                if [[ -d ${toks[i]} ]]; then
                    toks[i]+=/
                    compopt -o nospace 2>/dev/null
                fi
            done
        fi
        compopt -o filenames 2>/dev/null
    fi

    # Note: bash < 4.4 has a bug that all the elements are connected with
    # ${v+"${a[@]}"} when IFS does not contain whitespace.
    local IFS=$' \t\n'
    _comp_compgen -U toks set ${toks[@]+"${toks[@]}"}
}

# This function splits $cur=--foo=bar into $prev=--foo, $cur=bar, making it
# easier to support both "--foo bar" and "--foo=bar" style completions.
# `=' should have been removed from COMP_WORDBREAKS when setting $cur for
# this to be useful.
# Returns 0 if current option was split, 1 otherwise.
#
_comp__split_longopt()
{
    if [[ $cur == --?*=* ]]; then
        # Cut also backslash before '=' in case it ended up there
        # for some reason.
        prev=${cur%%?(\\)=*}
        cur=${cur#*=}
        return 0
    fi

    return 1
}

# Complete variables.
# @return  True (0) if variables were completed,
#          False (> 0) if not.
# @since 2.12
_comp_compgen_variables()
{
    if [[ $cur =~ ^(\$(\{[!#]?)?)([A-Za-z0-9_]*)$ ]]; then
        # Completing $var / ${var / ${!var / ${#var
        if [[ $cur == '${'* ]]; then
            local arrs vars
            _comp_compgen -v vars -c "${BASH_REMATCH[3]}" -- -A variable -P "${BASH_REMATCH[1]}" -S '}'
            _comp_compgen -v arrs -c "${BASH_REMATCH[3]}" -- -A arrayvar -P "${BASH_REMATCH[1]}" -S '['
            if ((${#vars[@]} == 1 && ${#arrs[@]} != 0)); then
                # Complete ${arr with ${array[ if there is only one match, and that match is an array variable
                compopt -o nospace
                _comp_compgen -U vars -U arrs -R -- -W '"${arrs[@]}"'
            else
                # Complete ${var with ${variable}
                _comp_compgen -U vars -U arrs -R -- -W '"${vars[@]}"'
            fi
        else
            # Complete $var with $variable
            _comp_compgen -ac "${BASH_REMATCH[3]}" -- -A variable -P '$'
        fi
        return 0
    elif [[ $cur =~ ^(\$\{[#!]?)([A-Za-z0-9_]*)\[([^]]*)$ ]]; then
        # Complete ${array[i with ${array[idx]}
        local vars
        _comp_compgen -v vars -c "${BASH_REMATCH[3]}" -- -W '"${!'"${BASH_REMATCH[2]}"'[@]}"' \
            -P "${BASH_REMATCH[1]}${BASH_REMATCH[2]}[" -S ']}'
        # Complete ${arr[@ and ${arr[*
        if [[ ${BASH_REMATCH[3]} == [@*] ]]; then
            vars+=("${BASH_REMATCH[1]}${BASH_REMATCH[2]}[${BASH_REMATCH[3]}]}")
        fi
        # array indexes may have colons
        if ((${#vars[@]})); then
            _comp_compgen -U vars -c "$cur" ltrim_colon "${vars[@]}"
        else
            _comp_compgen_set
        fi
        return 0
    elif [[ $cur =~ ^\$\{[#!]?[A-Za-z0-9_]*\[.*\]$ ]]; then
        # Complete ${array[idx] with ${array[idx]}
        _comp_compgen -c "$cur" ltrim_colon "$cur}"
        return 0
    fi
    return 1
}

# Complete a delimited value.
#
# Usage: [-k] DELIMITER COMPGEN_ARG...
#         -k: do not filter out already present tokens in value
# @since 2.12
_comp_delimited()
{
    local prefix="" delimiter=$1 deduplicate=set
    shift
    if [[ $delimiter == -k ]]; then
        deduplicate=""
        delimiter=$1
        shift
    fi
    [[ $cur == *"$delimiter"* ]] && prefix=${cur%"$delimiter"*}$delimiter

    if [[ $deduplicate ]]; then
        # We could construct a -X pattern to feed to compgen, but that'd
        # conflict with possibly already set -X in $@, as well as have
        # glob char escaping issues to deal with. Do removals by hand instead.
        _comp_compgen -R -- "$@"
        local -a existing
        _comp_split -F "$delimiter" existing "$cur"
        # Do not remove the last from existing if it's not followed by the
        # delimiter so we get space appended.
        [[ ! $cur || $cur == *"$delimiter" ]] || unset -v "existing[${#existing[@]}-1]"
        if ((${#COMPREPLY[@]})); then
            local x i
            for x in ${existing+"${existing[@]}"}; do
                for i in "${!COMPREPLY[@]}"; do
                    if [[ $x == "${COMPREPLY[i]}" ]]; then
                        unset -v 'COMPREPLY[i]'
                        continue 2 # assume no dupes in COMPREPLY
                    fi
                done
            done
            ((${#COMPREPLY[@]})) &&
                _comp_compgen -c "${cur##*"$delimiter"}" -- -W '"${COMPREPLY[@]}"'
        fi
    else
        _comp_compgen -c "${cur##*"$delimiter"}" -- "$@"
    fi

    # It would seem that in some specific cases we could avoid adding the
    # prefix to all completions, thereby making the list of suggestions
    # cleaner, and only adding it when there's exactly one completion.
    # The cases where this opportunity has been observed involve having
    # `show-all-if-ambiguous` on, but even that has cases where it fails
    # and the last separator including everything before it is lost.
    # https://github.com/scop/bash-completion/pull/913#issuecomment-1490140309
    if [[ $prefix ]]; then
        local i
        for i in "${!COMPREPLY[@]}"; do
            COMPREPLY[i]="$prefix${COMPREPLY[i]}"
        done
    fi

    [[ $delimiter != : ]] || _comp_ltrim_colon_completions "$cur"
}

# Complete assignment of various known environment variables.
#
# The word to be completed is expected to contain the entire assignment,
# including the variable name and the "=". Some known variables are completed
# with colon separated values; for those to work, colon should not have been
# used to split words. See related parameters to _comp_initialize.
#
# @param  $1 variable assignment to be completed
# @return True (0) if variable value completion was attempted,
#         False (> 0) if not.
# @since 2.12
_comp_variable_assignments()
{
    local cur=${1-} i

    if [[ $cur =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
        prev=${BASH_REMATCH[1]}
        cur=${BASH_REMATCH[2]}
    else
        return 1
    fi

    case $prev in
        TZ)
            cur=/usr/share/zoneinfo/$cur
            _comp_compgen_filedir
            if ((${#COMPREPLY[@]})); then
                for i in "${!COMPREPLY[@]}"; do
                    if [[ ${COMPREPLY[i]} == *.tab ]]; then
                        unset -v 'COMPREPLY[i]'
                        continue
                    elif [[ -d ${COMPREPLY[i]} ]]; then
                        COMPREPLY[i]+=/
                        compopt -o nospace
                    fi
                    COMPREPLY[i]=${COMPREPLY[i]#/usr/share/zoneinfo/}
                done
            fi
            ;;
        TERM)
            _comp_compgen_terms
            ;;
        LANG | LC_*)
            _comp_compgen_split -- "$(locale -a 2>/dev/null)"
            ;;
        LANGUAGE)
            _comp_delimited : -W '$(locale -a 2>/dev/null)'
            ;;
        *)
            _comp_compgen_variables && return 0
            _comp_compgen -a filedir
            ;;
    esac

    return 0
}

# Initialize completion and deal with various general things: do file
# and variable completion where appropriate, and adjust prev, words,
# and cword as if no redirections exist so that completions do not
# need to deal with them.  Before calling this function, make sure
# cur, prev, words, and cword are local, ditto split if you use -s.
#
# Options:
#     -n EXCLUDE  Passed to _comp_get_words -n with redirection chars
#     -e XSPEC    Passed to _comp_compgen_filedir as first arg for stderr
#                 redirections
#     -o XSPEC    Passed to _comp_compgen_filedir as first arg for other output
#                 redirections
#     -i XSPEC    Passed to _comp_compgen_filedir as first arg for stdin
#                 redirections
#     -s          Split long options with _comp__split_longopt, implies -n =
# @param $1...$3 args Original arguments specified to the completion function.
#                     The first argument $1 is command name.  The second
#                     argument $2 is the string before the cursor in the
#                     current word.  The third argument $3 is the previous
#                     word.
# @var[out] cur           Reconstructed current word
# @var[out] prev          Reconstructed previous word
# @var[out] words         Reconstructed words
# @var[out] cword         Current word index in `words`
# @var[out] comp_args     Original arguments specified to the completion
#                         function are saved in this array, if the arguments
#                         $1...$3 is specified.
# @var[out,opt] was_split When "-s" is specified, `"set"/""` is set depending
#                         on whether the split happened.
# @return  True (0) if completion needs further processing,
#          False (> 0) no further processing is necessary.
#
# @since 2.12
_comp_initialize()
{
    local exclude="" opt_split="" outx="" errx="" inx=""

    local flag OPTIND=1 OPTARG="" OPTERR=0
    while getopts "n:e:o:i:s" flag "$@"; do
        case $flag in
            n) exclude+=$OPTARG ;;
            e) errx=$OPTARG ;;
            o) outx=$OPTARG ;;
            i) inx=$OPTARG ;;
            s)
                opt_split="set"
                was_split=""
                exclude+="="
                ;;
            *)
                echo "bash_completion: $FUNCNAME: usage error" >&2
                return 1
                ;;
        esac
    done
    shift "$((OPTIND - 1))"
    (($#)) && comp_args=("$@")

    COMPREPLY=()
    local redir='@(?(+([0-9])|{[a-zA-Z_]*([a-zA-Z_0-9])})@(>?([>|&])|<?([>&])|<<?([-<]))|&>?(>))'
    _comp_get_words -n "$exclude<>&" cur prev words cword

    # Complete variable names.
    _comp_compgen_variables && return 1

    # Complete on files if current is a redirect possibly followed by a
    # filename, e.g. ">foo", or previous is a "bare" redirect, e.g. ">".
    # shellcheck disable=SC2053
    if [[ $cur == $redir* || ${prev-} == $redir ]]; then
        local xspec
        case $cur in
            2'>'*) xspec=${errx-} ;;
            *'>'*) xspec=${outx-} ;;
            *'<'*) xspec=${inx-} ;;
            *)
                case $prev in
                    2'>'*) xspec=${errx-} ;;
                    *'>'*) xspec=${outx-} ;;
                    *'<'*) xspec=${inx-} ;;
                esac
                ;;
        esac
        # shellcheck disable=SC2295 # redir is a pattern
        cur=${cur##$redir}
        _comp_compgen_filedir "$xspec"
        return 1
    fi

    # Remove all redirections so completions don't have to deal with them.
    local i skip
    for ((i = 1; i < ${#words[@]}; )); do
        if [[ ${words[i]} == $redir* ]]; then
            # If "bare" redirect, remove also the next word (skip=2).
            # shellcheck disable=SC2053
            [[ ${words[i]} == $redir ]] && skip=2 || skip=1
            words=("${words[@]:0:i}" "${words[@]:i+skip}")
            ((i <= cword)) && ((cword -= skip))
        else
            ((i++))
        fi
    done

    ((cword <= 0)) && return 1
    prev=${words[cword - 1]}

    [[ $opt_split ]] && _comp__split_longopt && was_split="set"

    return 0
}

# Helper function for _comp_compgen_help and _comp_compgen_usage.
# Obtain the help output based on the arguments.
# @param $@ args  Arguments specified to the caller.
# @var[out] _lines
# @return 2 if the usage is wrong, 1 if no output is obtained, or otherwise 0.
_comp_compgen_help__get_help_lines()
{
    local -a help_cmd
    case ${1-} in
        -)
            if (($# > 1)); then
                printf 'bash_completion: %s -: extra arguments for -\n' "${FUNCNAME[1]}" >&2
                printf 'usage: %s -\n' "${FUNCNAME[1]}" >&2
                printf 'usage: %s -c cmd args...\n' "${FUNCNAME[1]}" >&2
                printf 'usage: %s [-- args...]\n' "${FUNCNAME[1]}" >&2
                return 2
            fi
            help_cmd=(exec cat)
            ;;
        -c)
            if (($# < 2)); then
                printf 'bash_completion: %s -c: no command is specified\n' "${FUNCNAME[1]}" >&2
                printf 'usage: %s -\n' "${FUNCNAME[1]}" >&2
                printf 'usage: %s -c cmd args...\n' "${FUNCNAME[1]}" >&2
                printf 'usage: %s [-- args...]\n' "${FUNCNAME[1]}" >&2
                return 2
            fi
            help_cmd=("${@:2}")
            ;;
        --) shift 1 ;&
        *)
            local REPLY
            _comp_dequote "${comp_args[0]-}"
            help_cmd=("${REPLY:-false}" "$@")
            ;;
    esac

    local REPLY
    _comp_split -l REPLY "$(LC_ALL=C "${help_cmd[@]}" 2>&1)" &&
        _lines=("${REPLY[@]}")
}

# Helper function for _comp_compgen_help and _comp_compgen_usage.
# @var[in,out] options Add options
# @return True (0) if an option was found, False (> 0) otherwise
_comp_compgen_help__parse()
{
    local option option2 i

    # Take first found long option, or first one (short) if not found.
    option=
    local -a array
    if _comp_split -F $' \t\n,/|' array "$1"; then
        for i in "${array[@]}"; do
            case "$i" in
                ---*) break ;;
                --?*)
                    option=$i
                    break
                    ;;
                -?*) [[ $option ]] || option=$i ;;
                *) break ;;
            esac
        done
    fi
    [[ $option ]] || return 1

    # Expand --[no]foo to --foo and --nofoo etc
    if [[ $option =~ (\[((no|dont)-?)\]). ]]; then
        option2=${option/"${BASH_REMATCH[1]}"/}
        option2=${option2%%[<{().[]*}
        options+=("${option2/=*/=}")
        option=${option/"${BASH_REMATCH[1]}"/"${BASH_REMATCH[2]}"}
    fi

    [[ $option =~ ^([^=<{().[]|\.[A-Za-z0-9])+=? ]] &&
        options+=("$BASH_REMATCH")
}

# Parse GNU style help output of the given command and generate and store
# completions in an array. The help output is produced in the way depending on
# the usage:
# usage: _comp_compgen_help -              # read from stdin
# usage: _comp_compgen_help -c cmd args... # run "cmd args..."
# usage: _comp_compgen_help [[--] args...] # run "${comp_args[0]} args..."
# When no arguments are specified, `--help` is assumed.
#
# @var[in] comp_args[0]
# @since 2.12
_comp_compgen_help()
{
    (($#)) || set -- -- --help

    local -a _lines
    _comp_compgen_help__get_help_lines "$@" || return "$?"

    local -a options=()
    local _line
    for _line in "${_lines[@]}"; do
        [[ $_line == *([[:blank:]])-* ]] || continue
        # transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc
        while [[ $_line =~ ((^|[^-])-[A-Za-z0-9?][[:space:]]+)(\[,?)?[A-Z0-9+]+([,_-]+[A-Z0-9]+)?(\.\.+)?\]* ]]; do
            _line=${_line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"}
        done
        _comp_compgen_help__parse "${_line// or /, }"
    done
    ((${#options[@]})) || return 1

    _comp_compgen -U options -- -W '"${options[@]}"'
    return 0
}

# Parse BSD style usage output (options in brackets) of the given command. The
# help output is produced in the way depending on the usage:
# usage: _comp_compgen_usage -              # read from stdin
# usage: _comp_compgen_usage -c cmd args... # run "cmd args..."
# usage: _comp_compgen_usage [[--] args...] # run "${comp_args[0]} args..."
# When no arguments are specified, `--usage` is assumed.
#
# @var[in] comp_args[0]
# @since 2.12
_comp_compgen_usage()
{
    (($#)) || set -- -- --usage

    local -a _lines
    _comp_compgen_help__get_help_lines "$@" || return "$?"

    local -a options=()
    local _line _match _option _i _char
    for _line in "${_lines[@]}"; do
        while [[ $_line =~ \[[[:space:]]*(-[^]]+)[[:space:]]*\] ]]; do
            _match=${BASH_REMATCH[0]}
            _option=${BASH_REMATCH[1]}
            case $_option in
                -?(\[)+([a-zA-Z0-9?]))
                    # Treat as bundled short options
                    for ((_i = 1; _i < ${#_option}; _i++)); do
                        _char=${_option:_i:1}
                        [[ $_char != '[' ]] && options+=("-$_char")
                    done
                    ;;
                *)
                    _comp_compgen_help__parse "$_option"
                    ;;
            esac
            _line=${_line#*"$_match"}
        done
    done
    ((${#options[@]})) || return 1

    _comp_compgen -U options -- -W '"${options[@]}"'
    return 0
}

# This function completes on signal names (minus the SIG prefix)
# @param $1 prefix
#
# @since 2.12
_comp_compgen_signals()
{
    local -a sigs
    _comp_compgen -v sigs -c "SIG${cur#"${1-}"}" -- -A signal &&
        _comp_compgen -RU sigs -- -P "${1-}" -W '"${sigs[@]#SIG}"'
}

# This function completes on known mac addresses
#
# @since 2.12
_comp_compgen_mac_addresses()
{
    local _re='\([A-Fa-f0-9]\{2\}:\)\{5\}[A-Fa-f0-9]\{2\}'
    local PATH="$PATH:/sbin:/usr/sbin"
    local -a addresses

    # Local interfaces
    # - ifconfig on Linux: HWaddr or ether
    # - ifconfig on FreeBSD: ether
    # - ip link: link/ether
    _comp_compgen -v addresses split -- "$(
        {
            ip -c=never link show || ip link show || LC_ALL=C ifconfig -a
        } 2>/dev/null | command sed -ne \
            "s/.*[[:space:]]HWaddr[[:space:]]\{1,\}\($_re\)[[:space:]].*/\1/p" -ne \
            "s/.*[[:space:]]HWaddr[[:space:]]\{1,\}\($_re\)[[:space:]]*$/\1/p" -ne \
            "s|.*[[:space:]]\(link/\)\{0,1\}ether[[:space:]]\{1,\}\($_re\)[[:space:]].*|\2|p" -ne \
            "s|.*[[:space:]]\(link/\)\{0,1\}ether[[:space:]]\{1,\}\($_re\)[[:space:]]*$|\2|p"
    )"

    # ARP cache
    _comp_compgen -av addresses split -- "$(
        {
            arp -an || ip -c=never neigh show || ip neigh show
        } 2>/dev/null | command sed -ne \
            "s/.*[[:space:]]\($_re\)[[:space:]].*/\1/p" -ne \
            "s/.*[[:space:]]\($_re\)[[:space:]]*$/\1/p"
    )"

    # /etc/ethers
    _comp_compgen -av addresses split -- "$(command sed -ne \
        "s/^[[:space:]]*\($_re\)[[:space:]].*/\1/p" /etc/ethers 2>/dev/null)"

    _comp_compgen -U addresses ltrim_colon "${addresses[@]}"
}

# This function completes on configured network interfaces
#
# @since 2.12
_comp_compgen_configured_interfaces()
{
    local -a files
    if [[ -f /etc/debian_version ]]; then
        # Debian system
        _comp_expand_glob files '/etc/network/interfaces /etc/network/interfaces.d/*' || return 0
        _comp_compgen -U files split -- "$(command sed -ne \
            's|^iface \([^ ]\{1,\}\).*$|\1|p' "${files[@]}" 2>/dev/null)"
    elif [[ -f /etc/SuSE-release ]]; then
        # SuSE system
        _comp_expand_glob files '/etc/sysconfig/network/ifcfg-*' || return 0
        _comp_compgen -U files split -- "$(printf '%s\n' "${files[@]}" |
            command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')"
    elif [[ -f /etc/pld-release ]]; then
        # PLD Linux
        _comp_compgen -U files split -- "$(command ls -B /etc/sysconfig/interfaces |
            command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')"
    else
        # Assume Red Hat
        _comp_expand_glob files '/etc/sysconfig/network-scripts/ifcfg-*' || return 0
        _comp_compgen -U files split -- "$(printf '%s\n' "${files[@]}" |
            command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')"
    fi
}

# Local IP addresses.
# If producing IPv6 completions, `_comp_initialize` with `-n :`.
#
# -4: IPv4 addresses only (default)
# -6: IPv6 addresses only
# -a: All addresses
#
# @since 2.12
_comp_compgen_ip_addresses()
{
    local _n
    case ${1-} in
        -a) _n='6\{0,1\}' ;;
        -6) _n='6' ;;
        *) _n= ;;
    esac
    local PATH=$PATH:/sbin
    local addrs
    _comp_compgen -v addrs split -- "$({
        ip -c=never addr show || ip addr show || LC_ALL=C ifconfig -a
    } 2>/dev/null |
        command sed -e 's/[[:space:]]addr:/ /' -ne \
            "s|.*inet${_n}[[:space:]]\{1,\}\([^[:space:]/]*\).*|\1|p")" ||
        return

    if [[ ! $_n ]]; then
        _comp_compgen -U addrs set "${addrs[@]}"
    else
        _comp_compgen -U addrs ltrim_colon "${addrs[@]}"
    fi
}

# This function completes on available kernel versions
#
# @since 2.12
_comp_compgen_kernel_versions()
{
    _comp_compgen_split -- "$(command ls /lib/modules)"
}

# This function completes on all available network interfaces
# -a: restrict to active interfaces only
# -w: restrict to wireless interfaces only
#
# @since 2.12
_comp_compgen_available_interfaces()
{
    local PATH=$PATH:/sbin
    local generated
    _comp_compgen -v generated split -- "$({
        if [[ ${1-} == -w ]]; then
            iwconfig
        elif [[ ${1-} == -a ]]; then
            # Note: we prefer ip (iproute2) to ifconfig (inetutils) since long
            # interface names will be truncated by ifconfig [1].
            # [1]: https://github.com/scop/bash-completion/issues/1089
            ip -c=never link show up || ip link show up || ifconfig
        else
            ip -c=never link show || ip link show || ifconfig -a
        fi
    } 2>/dev/null | _comp_awk \
        '/^[^ \t]/ { if ($1 ~ /^[0-9]+:/) { sub(/@.*/, "", $2); print $2 } else { print $1 } }')" &&
        _comp_compgen -U generated set "${generated[@]%:}"
}

# Echo number of CPUs, falling back to 1 on failure.
# @var[out] REPLY
# @return 0 if it successfully obtained the number of CPUs, or otherwise 1
# @since 2.12
_comp_get_ncpus()
{
    local var=NPROCESSORS_ONLN
    [[ $OSTYPE == *@(linux|msys|cygwin)* ]] && var=_$var
    if REPLY=$(getconf $var 2>/dev/null) && ((REPLY >= 1)); then
        return 0
    else
        REPLY=1
        return 1
    fi
}

# Perform tilde (~) completion
# @return  False (1) if completion needs further processing,
#          True (0) if tilde is followed by a valid username, completions are
#          put in COMPREPLY and no further processing is necessary.
# @since 2.12
_comp_compgen_tilde()
{
    if [[ ${cur-} == \~* && $cur != */* ]]; then
        # Try generate ~username completions
        if _comp_compgen -P '~' -- -u; then
            # 2>/dev/null for direct invocation, e.g. in the
            # _comp_compgen_tilde unit test
            compopt -o filenames 2>/dev/null
            return 0
        fi
    fi
    return 1
}

# Expand string starting with tilde (~)
# We want to expand ~foo/... to /home/foo/... to avoid problems when
# word-to-complete starting with a tilde is fed to commands and ending up
# quoted instead of expanded.
# Only the first portion of the variable from the tilde up to the first slash
# (~../) is expanded.  The remainder of the variable, containing for example
# a dollar sign variable ($) or asterisk (*) is not expanded.
# Example usage:
#
#    $ _comp_expand_tilde "~"; echo "$REPLY"
#
# Example output:
#
#       $1                 REPLY
#    --------         ----------------
#    ~                /home/user
#    ~foo/bar         /home/foo/bar
#    ~foo/$HOME       /home/foo/$HOME
#    ~foo/a  b        /home/foo/a  b
#    ~foo/*           /home/foo/*
#
# @param $1     Value to expand
# @var[out] REPLY Expanded result
# @since 2.12
_comp_expand_tilde()
{
    REPLY=$1
    if [[ $1 == \~* ]]; then
        printf -v REPLY '~%q' "${1#\~}"
        eval "REPLY=$REPLY"
    fi
}

# This function expands tildes in pathnames
#
# @since 2.12
_comp_expand()
{
    # Expand ~username type directory specifications.  We want to expand
    # ~foo/... to /home/foo/... to avoid problems when $cur starting with
    # a tilde is fed to commands and ending up quoted instead of expanded.

    case ${cur-} in
        ~*/*)
            local REPLY
            _comp_expand_tilde "$cur"
            cur=$REPLY
            ;;
        ~*)
            _comp_compgen -v COMPREPLY tilde &&
                eval "COMPREPLY[0]=$(printf ~%q "${COMPREPLY[0]#\~}")" &&
                return 1
            ;;
    esac
    return 0
}

# Process ID related functions.
# for AIX and Solaris we use X/Open syntax, BSD for others.
#
# @since 2.12
if [[ $OSTYPE == *@(solaris|aix)* ]]; then
    # This function completes on process IDs.
    _comp_compgen_pids()
    {
        _comp_compgen_split -- "$(command ps -efo pid | command sed 1d)"
    }

    _comp_compgen_pgids()
    {
        _comp_compgen_split -- "$(command ps -efo pgid | command sed 1d)"
    }
    _comp_compgen_pnames()
    {
        _comp_compgen_split -X '<defunct>' -- "$(command ps -efo comm |
            command sed -e 1d -e 's:.*/::' -e 's/^-//' | sort -u)"
    }
else
    _comp_compgen_pids()
    {
        _comp_compgen_split -- "$(command ps ax -o pid=)"
    }
    _comp_compgen_pgids()
    {
        _comp_compgen_split -- "$(command ps ax -o pgid=)"
    }
    # @param $1 if -s, don't try to avoid truncated command names
    _comp_compgen_pnames()
    {
        local -a procs=()
        if [[ ${1-} == -s ]]; then
            _comp_split procs "$(command ps ax -o comm | command sed -e 1d)"
        else
            # Some versions of ps don't support "command", but do "comm", e.g.
            # some busybox ones. Fall back
            local -a psout
            _comp_split -l psout "$({
                command ps ax -o command= || command ps ax -o comm=
            } 2>/dev/null)"
            local line i=-1
            for line in "${psout[@]}"; do
                if ((i == -1)); then
                    # First line, see if it has COMMAND column header. For
                    # example some busybox ps versions do that, i.e. don't
                    # respect command=
                    if [[ $line =~ ^(.*[[:space:]])COMMAND([[:space:]]|$) ]]; then
                        # It does; store its index.
                        i=${#BASH_REMATCH[1]}
                    else
                        # Nope, fall through to "regular axo command=" parsing.
                        break
                    fi
                else
                    #
                    line=${line:i}   # take command starting from found index
                    line=${line%% *} # trim arguments
                    [[ $line ]] && procs+=("$line")
                fi
            done
            if ((i == -1)); then
                # Regular command= parsing
                for line in "${psout[@]}"; do
                    if [[ $line =~ ^[[(](.+)[])]$ ]]; then
                        procs+=("${BASH_REMATCH[1]}")
                    else
                        line=${line%% *}      # trim arguments
                        line=${line##@(*/|-)} # trim leading path and -
                        [[ $line ]] && procs+=("$line")
                    fi
                done
            fi
        fi
        ((${#procs[@]})) &&
            _comp_compgen -U procs -- -X "<defunct>" -W '"${procs[@]}"'
    }
fi

# This function completes on user IDs
#
# @since 2.12
_comp_compgen_uids()
{
    if type getent &>/dev/null; then
        _comp_compgen_split -- "$(getent passwd | cut -d: -f3)"
    elif type perl &>/dev/null; then
        _comp_compgen_split -- "$(perl -e 'while (($uid) = (getpwent)[2]) { print $uid . "\n" }')"
    else
        # make do with /etc/passwd
        _comp_compgen_split -- "$(cut -d: -f3 /etc/passwd)"
    fi
}

# This function completes on group IDs
#
# @since 2.12
_comp_compgen_gids()
{
    if type getent &>/dev/null; then
        _comp_compgen_split -- "$(getent group | cut -d: -f3)"
    elif type perl &>/dev/null; then
        _comp_compgen_split -- "$(perl -e 'while (($gid) = (getgrent)[2]) { print $gid . "\n" }')"
    else
        # make do with /etc/group
        _comp_compgen_split -- "$(cut -d: -f3 /etc/group)"
    fi
}

# Glob for matching various backup files.
#
_comp_backup_glob='@(#*#|*@(~|.@(bak|orig|rej|swp|@(dpkg|ucf)-*|rpm@(orig|new|save)|zs-old)))'

# Complete on xinetd services
#
# @since 2.12
_comp_compgen_xinetd_services()
{
    local xinetddir=${_comp__test_xinetd_dir:-/etc/xinetd.d}
    if [[ -d $xinetddir ]]; then
        local -a svcs
        if _comp_expand_glob svcs '$xinetddir/!($_comp_backup_glob)'; then
            _comp_compgen -U svcs -U xinetddir -- -W '"${svcs[@]#$xinetddir/}"'
        fi
    fi
}

# This function completes on services
#
# @since 2.12
_comp_compgen_services()
{
    local sysvdirs
    _comp_sysvdirs || return 1

    local services
    _comp_expand_glob services '${sysvdirs[0]}/!($_comp_backup_glob|functions|README)'

    local _generated=$({
        systemctl list-units --full --all ||
            systemctl list-unit-files
    } 2>/dev/null |
        _comp_awk '$1 ~ /\.service$/ { sub("\\.service$", "", $1); print $1 }')
    _comp_split -la services "$_generated"

    if [[ -x /sbin/upstart-udev-bridge ]]; then
        _comp_split -la services "$(initctl list 2>/dev/null | cut -d' ' -f1)"
    fi

    ((${#services[@]})) || return 1
    _comp_compgen -U services -U sysvdirs -- -W '"${services[@]#${sysvdirs[0]}/}"'
}

# This completes on a list of all available service scripts for the
# 'service' command and/or the SysV init.d directory, followed by
# that script's available commands
# This function is in the main bash_completion file rather than in a separate
# one, because we set it up eagerly as completer for scripts in sysv init dirs
# below.
#
# @since 2.12
_comp_complete_service()
{
    local cur prev words cword comp_args
    _comp_initialize -- "$@" || return

    # don't complete past 2nd token
    ((cword > 2)) && return

    if [[ $cword -eq 1 && $prev == ?(*/)service ]]; then
        _comp_compgen_services
        [[ -e /etc/mandrake-release ]] && _comp_compgen_xinetd_services
    else
        local sysvdirs
        _comp_sysvdirs || return 1
        _comp_compgen_split -l -- "$(command sed -e 'y/|/ /' \
            -ne 's/^.*\(U\|msg_u\)sage.*{\(.*\)}.*$/\2/p' \
            "${sysvdirs[0]}/${prev##*/}" 2>/dev/null) start stop"
    fi
} &&
    complete -F _comp_complete_service service

_comp__init_set_up_service_completions()
{
    local sysvdirs svc svcdir svcs
    _comp_sysvdirs &&
        for svcdir in "${sysvdirs[@]}"; do
            if _comp_expand_glob svcs '"$svcdir"/!($_comp_backup_glob)'; then
                for svc in "${svcs[@]}"; do
                    [[ -x $svc ]] && complete -F _comp_complete_service "$svc"
                done
            fi
        done
    unset -f "$FUNCNAME"
}
_comp__init_set_up_service_completions

# This function completes on kernel modules
# @param $1 kernel version
#
# @since 2.12
_comp_compgen_kernel_modules()
{
    local _modpath=/lib/modules/$1
    _comp_compgen_split -- "$(command ls -RL "$_modpath" 2>/dev/null |
        command sed -ne 's/^\(.*\)\.k\{0,1\}o\(\.[gx]z\)\{0,1\}$/\1/p' \
            -e 's/^\(.*\)\.ko\.zst$/\1/p')"
}

# This function completes on inserted kernel modules
# @param $1 prefix to filter with, default $cur
#
# @since 2.12
_comp_compgen_inserted_kernel_modules()
{
    _comp_compgen -c "${1:-$cur}" split -- "$(PATH="$PATH:/sbin" lsmod |
        _comp_awk '{if (NR != 1) print $1}')"
}

# This function completes on user or user:group format; as for chown and cpio.
#
# The : must be added manually; it will only complete usernames initially.
# The legacy user.group format is not supported.
#
# @param $1  If -u, only return users/groups the user has access to in
#            context of current completion.
#
# @since 2.12
_comp_compgen_usergroups()
{
    if [[ $cur == *\\\\* || $cur == *:*:* ]]; then
        # Give up early on if something seems horribly wrong.
        return
    elif [[ $cur == *\\:* ]]; then
        # Completing group after 'user\:gr<TAB>'.
        # Reply with a list of groups prefixed with 'user:', readline will
        # escape to the colon.
        local tmp
        if [[ ${1-} == -u ]]; then
            _comp_compgen -v tmp -c "${cur#*:}" allowed_groups
        else
            _comp_compgen -v tmp -c "${cur#*:}" -- -g
        fi
        if ((${#tmp[@]})); then
            local _prefix=${cur%%*([^:])}
            _prefix=${_prefix//\\/}
            _comp_compgen -Rv tmp -- -P "$_prefix" -W '"${tmp[@]}"'
            _comp_compgen -U tmp set "${tmp[@]}"
        fi
    elif [[ $cur == *:* ]]; then
        # Completing group after 'user:gr<TAB>'.
        # Reply with a list of unprefixed groups since readline with split on :
        # and only replace the 'gr' part
        if [[ ${1-} == -u ]]; then
            _comp_compgen -c "${cur#*:}" allowed_groups
        else
            _comp_compgen -c "${cur#*:}" -- -g
        fi
    else
        # Completing a partial 'usernam<TAB>'.
        #
        # Don't suffix with a : because readline will escape it and add a
        # slash. It's better to complete into 'chown username ' than 'chown
        # username\:'.
        if [[ ${1-} == -u ]]; then
            _comp_compgen_allowed_users
        else
            _comp_compgen -- -u
        fi
    fi
}

# @since 2.12
_comp_compgen_allowed_users()
{
    if _comp_as_root; then
        _comp_compgen -- -u
    else
        _comp_compgen_split -- "$(id -un 2>/dev/null || whoami 2>/dev/null)"
    fi
}

# @since 2.12
_comp_compgen_allowed_groups()
{
    if _comp_as_root; then
        _comp_compgen -- -g
    else
        _comp_compgen_split -- "$(id -Gn 2>/dev/null || groups 2>/dev/null)"
    fi
}

# @since 2.12
_comp_compgen_selinux_users()
{
    _comp_compgen_split -- "$(semanage user -nl 2>/dev/null |
        _comp_awk '{ print $1 }')"
}

# This function completes on valid shells
# @param $1 chroot to search from
#
# @since 2.12
_comp_compgen_shells()
{
    local -a shells=()
    local _shell _rest
    while read -r _shell _rest; do
        [[ $_shell == /* ]] && shells+=("$_shell")
    done 2>/dev/null <"${1-}"/etc/shells
    _comp_compgen -U shells -- -W '"${shells[@]}"'
}

# This function completes on valid filesystem types
#
# @since 2.12
_comp_compgen_fstypes()
{
    local _fss

    if [[ -e /proc/filesystems ]]; then
        # Linux
        _fss="$(cut -d$'\t' -f2 /proc/filesystems)
             $(_comp_awk '! /\*/ { print $NF }' /etc/filesystems 2>/dev/null)"
    else
        # Generic
        _fss="$(_comp_awk '/^[ \t]*[^#]/ { print $3 }' /etc/fstab 2>/dev/null)
             $(_comp_awk '/^[ \t]*[^#]/ { print $3 }' /etc/mnttab 2>/dev/null)
             $(_comp_awk '/^[ \t]*[^#]/ { print $4 }' /etc/vfstab 2>/dev/null)
             $(_comp_awk '{ print $1 }' /etc/dfs/fstypes 2>/dev/null)
             $(lsvfs 2>/dev/null | _comp_awk '$1 !~ /^(Filesystem|[^a-zA-Z])/ { print $1 }')
             $([[ -d /etc/fs ]] && command ls /etc/fs)"
    fi

    [[ $_fss ]] && _comp_compgen_split -- "$_fss"
}

# Get absolute path to a file, with rudimentary canonicalization.
# No symlink resolution or existence checks are done;
# see `_comp_realcommand` for those.
# @param $1 The file
# @var[out] REPLY The path
# @since 2.12
_comp_abspath()
{
    REPLY=$1
    [[ $REPLY == /* ]] || REPLY=$PWD/$REPLY
    REPLY=${REPLY//+(\/)/\/}
    while true; do
        # Process "." and "..".  To avoid reducing "/../../ => /", we convert
        # "/*/../" one by one. "/.."  at the beginning is ignored. Then, /*/../
        # in the middle is processed.  Finally, /*/.. at the end is removed.
        case $REPLY in
            */./*) REPLY=${REPLY//\/.\//\/} ;;
            */.) REPLY=${REPLY%/.} ;;
            /..?(/*)) REPLY=${REPLY#/..} ;;
            */+([^/])/../*) REPLY=${REPLY/\/+([^\/])\/..\//\/} ;;
            */+([^/])/..) REPLY=${REPLY%/+([^/])/..} ;;
            *) break ;;
        esac
    done
    [[ $REPLY ]] || REPLY=/
}

# Get real command.
# Command is the filename of command in PATH with possible symlinks resolved
# (if resolve tooling available), empty string if command not found.
# @param    $1 Command
# @var[out] REPLY Resulting string
# @return   True (0) if command found, False (> 0) if not.
# @since 2.12
_comp_realcommand()
{
    REPLY=""
    local file
    file=$(type -P -- "$1") || return $?
    if type -p realpath >/dev/null; then
        REPLY=$(realpath "$file")
    elif type -p greadlink >/dev/null; then
        REPLY=$(greadlink -f "$file")
    elif type -p readlink >/dev/null; then
        REPLY=$(readlink -f "$file")
    else
        _comp_abspath "$file"
    fi
}

# This function returns the position of the first argument, excluding options
#
# Options:
#     -a GLOB  Pattern of options that take an option argument
#
# @var[out] REPLY Position of the first argument before the current one being
# completed if any, or otherwise an empty string
# @return True (0) if any argument is found, False (> 0) otherwise.
# @since 2.12
_comp_locate_first_arg()
{
    local has_optarg=""
    local OPTIND=1 OPTARG="" OPTERR=0 _opt
    while getopts ':a:' _opt "$@"; do
        case $_opt in
            a) has_optarg=$OPTARG ;;
            *)
                echo "bash_completion: $FUNCNAME: usage error" >&2
                return 2
                ;;
        esac
    done
    shift "$((OPTIND - 1))"

    local i
    REPLY=
    for ((i = 1; i < cword; i++)); do
        # shellcheck disable=SC2053
        if [[ $has_optarg && ${words[i]} == $has_optarg ]]; then
            ((i++))
        elif [[ ${words[i]} != -?* ]]; then
            REPLY=$i
            return 0
        elif [[ ${words[i]} == -- ]]; then
            ((i + 1 < cword)) && REPLY=$((i + 1)) && return 0
            break
        fi
    done
    return 1
}

# This function returns the first argument, excluding options
#
# Options:
#     -a GLOB  Pattern of options that take an option argument
#
# @var[out] REPLY First argument before the current one being completed if any,
# or otherwise an empty string
# @return True (0) if any argument is found, False (> 0) otherwise.
# @since 2.12
_comp_get_first_arg()
{
    _comp_locate_first_arg "$@" && REPLY=${words[REPLY]}
}

# This function counts the number of args, excluding options
#
# Options:
#     -n CHARS  Characters out of $COMP_WORDBREAKS which should
#               NOT be considered word breaks. See
#               _comp__reassemble_words.
#     -a GLOB   Options whose following argument should not be counted
#     -i GLOB   Options that should be counted as args
#
# @var[out] REPLY    Return the number of arguments
# @since 2.12
_comp_count_args()
{
    local has_optarg="" has_exclude="" exclude="" glob_include=""
    local OPTIND=1 OPTARG="" OPTERR=0 _opt
    while getopts ':a:n:i:' _opt "$@"; do
        case $_opt in
            a) has_optarg=$OPTARG ;;
            n) has_exclude=set exclude+=$OPTARG ;;
            i) glob_include=$OPTARG ;;
            *)
                echo "bash_completion: $FUNCNAME: usage error" >&2
                return 2
                ;;
        esac
    done
    shift "$((OPTIND - 1))"

    if [[ $has_exclude ]]; then
        local cword words
        _comp__reassemble_words "$exclude<>&" words cword
    fi

    local i
    REPLY=1
    for ((i = 1; i < cword; i++)); do
        # shellcheck disable=SC2053
        if [[ $has_optarg && ${words[i]} == $has_optarg ]]; then
            ((i++))
        elif [[ ${words[i]} != -?* || $glob_include && ${words[i]} == $glob_include ]]; then
            ((REPLY++))
        elif [[ ${words[i]} == -- ]]; then
            ((REPLY += cword - i - 1))
            break
        fi
    done
}

# This function completes on PCI IDs
#
# @since 2.12
_comp_compgen_pci_ids()
{
    _comp_compgen_split -- "$(PATH="$PATH:/sbin" lspci -n | _comp_awk '{print $3}')"
}

# This function completes on USB IDs
#
# @since 2.12
_comp_compgen_usb_ids()
{
    _comp_compgen_split -- "$(PATH="$PATH:/sbin" lsusb | _comp_awk '{print $6}')"
}

# CD device names
#
# @since 2.12
_comp_compgen_cd_devices()
{
    _comp_compgen -c "${cur:-/dev/}" -- -f -d -X "!*/?([amrs])cd!(c-*)"
}

# DVD device names
#
# @since 2.12
_comp_compgen_dvd_devices()
{
    _comp_compgen -c "${cur:-/dev/}" -- -f -d -X "!*/?(r)dvd*"
}

# TERM environment variable values
#
# @since 2.12
_comp_compgen_terms()
{
    _comp_compgen_split -- "$({
        command sed -ne 's/^\([^[:space:]#|]\{2,\}\)|.*/\1/p' /etc/termcap
        {
            toe -a || toe
        } | _comp_awk '{ print $1 }'
        _comp_expand_glob dirs '/{etc,lib,usr/lib,usr/share}/terminfo/?' &&
            find "${dirs[@]}" -type f -maxdepth 1 |
            _comp_awk -F / '{ print $NF }'
    } 2>/dev/null)"
}

# @since 2.12
_comp_try_faketty()
{
    if type unbuffer &>/dev/null; then
        unbuffer -p "$@"
    elif script --version 2>&1 | command grep -qF util-linux; then
        # BSD and Solaris "script" do not seem to have required features
        script -qaefc "$*" /dev/null
    else
        "$@" # no can do, fallback
    fi
}

# a little help for FreeBSD ports users
[[ $OSTYPE == *freebsd* ]] && complete -W 'index search fetch fetch-list
    extract patch configure build install reinstall deinstall clean
    clean-depends kernel buildworld' make

# This function provides simple user@host completion
#
# @since 2.12
_comp_complete_user_at_host()
{
    local cur prev words cword comp_args
    _comp_initialize -n : -- "$@" || return

    if [[ $cur == *@* ]]; then
        _comp_compgen_known_hosts "$cur"
    else
        _comp_compgen -- -u -S @
        compopt -o nospace
    fi
}
shopt -u hostcomplete && complete -F _comp_complete_user_at_host talk ytalk finger

# NOTE: Using this function as a helper function is deprecated.  Use
#       `_comp_compgen_known_hosts' instead.
# @since 2.12
_comp_complete_known_hosts()
{
    local cur prev words cword comp_args
    _comp_initialize -n : -- "$@" || return

    # NOTE: Using `_known_hosts' (the old name of `_comp_complete_known_hosts')
    #       as a helper function and passing options to `_known_hosts' is
    #       deprecated: Use `_comp_compgen_known_hosts' instead.
    local -a options=()
    [[ ${1-} == -a || ${2-} == -a ]] && options+=(-a)
    [[ ${1-} == -c || ${2-} == -c ]] && options+=(-c)
    local IFS=$' \t\n' # Workaround for connected ${v+"$@"} in bash < 4.4
    _comp_compgen_known_hosts ${options[@]+"${options[@]}"} -- "$cur"
}

# Helper function to locate ssh included files in configs
# This function looks for the "Include" keyword in ssh config files and
# includes them recursively, adding each result to the config variable.
_comp__included_ssh_config_files()
{
    (($# < 1)) &&
        echo "bash_completion: $FUNCNAME: missing mandatory argument CONFIG" >&2
    local configfile i files f REPLY
    configfile=$1

    # From man ssh_config:
    # "Files without absolute paths are assumed to be in ~/.ssh if included
    # in a user configuration file or /etc/ssh if included from the system
    # configuration file."
    # This behavior is not affected by the the including file location -
    # if the system configuration file is included from the user's config,
    # relative includes are still resolved in the user's ssh config directory.
    local relative_include_base
    if [[ $configfile == /etc/ssh* ]]; then
        relative_include_base="/etc/ssh"
    else
        relative_include_base="$HOME/.ssh"
    fi

    local depth=1
    local -a included
    local -a include_files
    included=("$configfile")

    # Max recursion depth per openssh's READCONF_MAX_DEPTH:
    # https://github.com/openssh/openssh-portable/blob/5ec5504f1d328d5bfa64280cd617c3efec4f78f3/readconf.c#L2240
    local max_depth=16
    while ((${#included[@]} > 0 && depth++ < max_depth)); do
        _comp_split include_files "$(command sed -ne 's/^[[:blank:]]*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][[:blank:]]\(.*\)$/\1/p' "${included[@]}")" || return
        included=()
        for i in "${include_files[@]}"; do
            if [[ $i != [~/]* ]]; then
                i="${relative_include_base}/${i}"
            fi
            _comp_expand_tilde "$i"
            if _comp_expand_glob files '$REPLY'; then
                # In case the expanded variable contains multiple paths
                for f in "${files[@]}"; do
                    if [[ -r $f && ! -d $f ]]; then
                        config+=("$f")
                        included+=("$f")
                    fi
                done
            fi
        done
    done
}

# Helper function for completing _comp_complete_known_hosts.
# This function performs host completion based on ssh's config and known_hosts
# files, as well as hostnames reported by avahi-browse if
# BASH_COMPLETION_KNOWN_HOSTS_WITH_AVAHI is set to a non-empty value.
# Also hosts from HOSTFILE (compgen -A hostname) are added, unless
# BASH_COMPLETION_KNOWN_HOSTS_WITH_HOSTFILE is set to an empty value.
# Usage: _comp_compgen_known_hosts [OPTIONS] CWORD
# Options:
#     -a             Use aliases from ssh config files
#     -c             Use `:' suffix
#     -F configfile  Use `configfile' for configuration settings
#     -p PREFIX      Use PREFIX
#     -4             Filter IPv6 addresses from results
#     -6             Filter IPv4 addresses from results
# @var[out] COMPREPLY  Completions, starting with CWORD, are added
# @return True (0) if one or more completions are generated, or otherwise False
# (1).
# @since 2.12
_comp_compgen_known_hosts()
{
    local known_hosts
    _comp_compgen_known_hosts__impl "$@" || return "$?"
    _comp_compgen -U known_hosts set "${known_hosts[@]}"
}
_comp_compgen_known_hosts__impl()
{
    known_hosts=()

    local configfile="" flag prefix=""
    local cur suffix="" aliases="" i host ipv4="" ipv6=""
    local -a kh tmpkh=() khd=() config=()

    # TODO remove trailing %foo from entries

    local OPTIND=1
    while getopts "ac46F:p:" flag "$@"; do
        case $flag in
            a) aliases=set ;;
            c) suffix=':' ;;
            F)
                if [[ ! $OPTARG ]]; then
                    echo "bash_completion: $FUNCNAME: -F: an empty filename is specified" >&2
                    return 2
                fi
                configfile=$OPTARG
                ;;
            p) prefix=$OPTARG ;;
            4) ipv4=set ;;
            6) ipv6=set ;;
            *)
                echo "bash_completion: $FUNCNAME: usage error" >&2
                return 2
                ;;
        esac
    done
    if (($# < OPTIND)); then
        echo "bash_completion: $FUNCNAME: missing mandatory argument CWORD" >&2
        return 2
    fi
    cur=${!OPTIND}
    ((OPTIND += 1))
    if (($# >= OPTIND)); then
        echo "bash_completion: $FUNCNAME($*): unprocessed arguments:" \
            "$(while (($# >= OPTIND)); do
                printf '%s ' ${!OPTIND}
                shift
            done)" >&2
        return 2
    fi

    [[ $cur == *@* ]] && prefix=$prefix${cur%@*}@ && cur=${cur#*@}
    kh=()

    # ssh config files
    if [[ $configfile ]]; then
        [[ -r $configfile && ! -d $configfile ]] && config+=("$configfile")
    else
        for i in /etc/ssh/ssh_config ~/.ssh/config ~/.ssh2/config; do
            [[ -r $i && ! -d $i ]] && config+=("$i")
        done
    fi

    # "Include" keyword in ssh config files
    if ((${#config[@]} > 0)); then
        for i in "${config[@]}"; do
            _comp__included_ssh_config_files "$i"
        done
    fi

    # Known hosts files from configs
    if ((${#config[@]} > 0)); then
        # expand paths (if present) to global and user known hosts files
        # TODO(?): try to make known hosts files with more than one consecutive
        #          spaces in their name work (watch out for ~ expansion
        #          breakage! Alioth#311595)
        if _comp_split -l tmpkh "$(_comp_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)"; then
            local tmpkh2 j REPLY
            for i in "${tmpkh[@]}"; do
                # First deal with quoted entries...
                while [[ $i =~ ^([^\"]*)\"([^\"]*)\"(.*)$ ]]; do
                    i=${BASH_REMATCH[1]}${BASH_REMATCH[3]}
                    _comp_expand_tilde "${BASH_REMATCH[2]}" # Eval/expand possible `~' or `~user'
                    [[ -r $REPLY ]] && kh+=("$REPLY")
                done
                # ...and then the rest.
                _comp_split tmpkh2 "$i" || continue
                for j in "${tmpkh2[@]}"; do
                    _comp_expand_tilde "$j" # Eval/expand possible `~' or `~user'
                    [[ -r $REPLY ]] && kh+=("$REPLY")
                done
            done
        fi
    fi

    if [[ ! $configfile ]]; then
        # Global and user known_hosts files
        for i in /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2 \
            /etc/known_hosts /etc/known_hosts2 ~/.ssh/known_hosts \
            ~/.ssh/known_hosts2; do
            [[ -r $i && ! -d $i ]] && kh+=("$i")
        done
        for i in /etc/ssh2/knownhosts ~/.ssh2/hostkeys; do
            [[ -d $i ]] || continue
            _comp_expand_glob tmpkh '"$i"/*.pub' && khd+=("${tmpkh[@]}")
        done
    fi

    # If we have known_hosts files to use
    if ((${#kh[@]} + ${#khd[@]} > 0)); then
        if ((${#kh[@]} > 0)); then
            # https://man.openbsd.org/sshd.8#SSH_KNOWN_HOSTS_FILE_FORMAT
            for i in "${kh[@]}"; do
                while read -ra tmpkh; do
                    ((${#tmpkh[@]} == 0)) && continue
                    # Skip entries starting with | (hashed) and # (comment)
                    [[ ${tmpkh[0]} == [\|\#]* ]] && continue
                    # Ignore leading @foo (markers)
                    local host_list=${tmpkh[0]}
                    [[ ${tmpkh[0]} == @* ]] && host_list=${tmpkh[1]-}
                    # Split entry on commas
                    local -a hosts
                    if _comp_split -F , hosts "$host_list"; then
                        for host in "${hosts[@]}"; do
                            # Skip hosts containing wildcards
                            [[ $host == *[*?]* ]] && continue
                            # Remove leading [
                            host=${host#[}
                            # Remove trailing ] + optional :port
                            host=${host%]?(:+([0-9]))}
                            # Add host to candidates
                            [[ $host ]] && known_hosts+=("$host")
                        done
                    fi
                done <"$i"
            done
        fi
        if ((${#khd[@]} > 0)); then
            # Needs to look for files called
            # .../.ssh2/key_22_<hostname>.pub
            # dont fork any processes, because in a cluster environment,
            # there can be hundreds of hostkeys
            for i in "${khd[@]}"; do
                if [[ $i == *key_22_*.pub && -r $i ]]; then
                    host=${i/#*key_22_/}
                    host=${host/%.pub/}
                    [[ $host ]] && known_hosts+=("$host")
                fi
            done
        fi

        # apply suffix and prefix
        ((${#known_hosts[@]})) &&
            _comp_compgen -v known_hosts -- -W '"${known_hosts[@]}"' -P "$prefix" -S "$suffix"
    fi

    # append any available aliases from ssh config files
    if [[ ${#config[@]} -gt 0 && $aliases ]]; then
        local -a hosts
        if _comp_split hosts "$(command sed -ne 's/^[[:blank:]]*[Hh][Oo][Ss][Tt][[:blank:]=]\{1,\}\(.*\)$/\1/p' "${config[@]}")"; then
            _comp_compgen -av known_hosts -- -P "$prefix" \
                -S "$suffix" -W '"${hosts[@]%%[*?%]*}"' -X '@(\!*|)'
        fi
    fi

    # Add hosts reported by avahi-browse, if desired and it's available.
    if [[ ${BASH_COMPLETION_KNOWN_HOSTS_WITH_AVAHI-} ]] &&
        type avahi-browse &>/dev/null; then
        # Some old versions of avahi-browse reportedly didn't have -k
        # (even if mentioned in the manpage); those we do not support any more.
        local generated=$(avahi-browse -cprak 2>/dev/null | _comp_awk -F ';' \
            '/^=/ && $5 ~ /^_(ssh|workstation)\._tcp$/ { print $7 }' |
            sort -u)
        _comp_compgen -av known_hosts -- -P "$prefix" -S "$suffix" -W '$generated'
    fi

    # Add hosts reported by ruptime.
    if type ruptime &>/dev/null; then
        local generated=$(ruptime 2>/dev/null | _comp_awk '!/^ruptime:/ { print $1 }')
        _comp_compgen -av known_hosts -- -W '$generated'
    fi

    # Add results of normal hostname completion, unless
    # `BASH_COMPLETION_KNOWN_HOSTS_WITH_HOSTFILE' is set to an empty value.
    if [[ ${BASH_COMPLETION_KNOWN_HOSTS_WITH_HOSTFILE-set} ]]; then
        _comp_compgen -av known_hosts -- -A hostname -P "$prefix" -S "$suffix"
    fi

    ((${#known_hosts[@]})) || return 1

    if [[ $ipv4 ]]; then
        known_hosts=("${known_hosts[@]/*:*$suffix/}")
    fi
    if [[ $ipv6 ]]; then
        known_hosts=("${known_hosts[@]/+([0-9]).+([0-9]).+([0-9]).+([0-9])$suffix/}")
    fi
    if [[ $ipv4 || $ipv6 ]]; then
        for i in "${!known_hosts[@]}"; do
            [[ ${known_hosts[i]} ]] || unset -v 'known_hosts[i]'
        done
    fi
    ((${#known_hosts[@]})) || return 1

    _comp_compgen -v known_hosts -c "$prefix$cur" ltrim_colon "${known_hosts[@]}"
}
complete -F _comp_complete_known_hosts traceroute traceroute6 \
    fping fping6 telnet rsh rlogin ftp dig drill ssh-installkeys showmount

# Convert the word index in `words` to the index in `COMP_WORDS`.
# @param $1           Index in the array WORDS.
# @var[in,opt] words  Words that contain reassmbled words.
# @var[in,opt] cword  Current word index in WORDS.
#                     WORDS and CWORD, if any, are expected to be created by
#                     _comp__reassemble_words.
#
_comp__find_original_word()
{
    REPLY=$1

    # If CWORD or WORDS are undefined, we return the first argument without any
    # processing.
    [[ -v cword && -v words ]] || return 0

    local reassembled_offset=$1 i=0 j
    for ((j = 0; j < reassembled_offset; j++)); do
        local word=${words[j]}
        while [[ $word && i -lt ${#COMP_WORDS[@]} && $word == *"${COMP_WORDS[i]}"* ]]; do
            word=${word#*"${COMP_WORDS[i++]}"}
        done
    done
    REPLY=$i
}
# A meta-command completion function for commands like sudo(8), which need to
# first complete on a command, then complete according to that command's own
# completion definition.
#
# @since 2.12
_comp_command_offset()
{
    # rewrite current completion context before invoking
    # actual command completion

    # obtain the word index in COMP_WORDS
    local REPLY
    _comp__find_original_word "$1"
    local word_offset=$REPLY

    # make changes to COMP_* local.  Note that bash-4.3..5.0 have a
    # bug that `local -a arr=("${arr[@]}")` fails.  We instead first
    # assign the values of `COMP_WORDS` to another array `comp_words`.
    local COMP_LINE=$COMP_LINE COMP_POINT=$COMP_POINT COMP_CWORD=$COMP_CWORD
    local -a comp_words=("${COMP_WORDS[@]}")
    local -a COMP_WORDS=("${comp_words[@]}")

    # find new first word position, then
    # rewrite COMP_LINE and adjust COMP_POINT
    local i tail
    for ((i = 0; i < word_offset; i++)); do
        tail=${COMP_LINE#*"${COMP_WORDS[i]}"}
        ((COMP_POINT -= ${#COMP_LINE} - ${#tail}))
        COMP_LINE=$tail
    done

    # shift COMP_WORDS elements and adjust COMP_CWORD
    COMP_WORDS=("${COMP_WORDS[@]:word_offset}")
    ((COMP_CWORD -= word_offset))

    COMPREPLY=()
    local cur
    _comp_get_words cur

    if ((COMP_CWORD == 0)); then
        _comp_compgen_commands
    else
        _comp_dequote "${COMP_WORDS[0]}"
        local cmd=${REPLY-} compcmd=${REPLY-}
        local cspec=$(complete -p -- "$cmd" 2>/dev/null)

        # If we have no completion for $cmd yet, see if we have for basename
        if [[ ! $cspec && $cmd == */* ]]; then
            cspec=$(complete -p -- "${cmd##*/}" 2>/dev/null)
            [[ $cspec ]] && compcmd=${cmd##*/}
        fi
        # If still nothing, just load it for the basename
        if [[ ! $cspec ]]; then
            compcmd=${cmd##*/}
            _comp_load -D -- "$compcmd"
            cspec=$(complete -p -- "$compcmd" 2>/dev/null)
        fi

        local retry_count=0
        while true; do # loop for the retry request by status 124
            local args original_cur=${comp_args[1]-$cur}
            if ((${#COMP_WORDS[@]} >= 2)); then
                args=("$cmd" "$original_cur" "${COMP_WORDS[-2]}")
            else
                args=("$cmd" "$original_cur")
            fi

            if [[ ! $cspec ]]; then
                if ((${#COMPREPLY[@]} == 0)); then
                    # XXX will probably never happen as long as completion loader loads
                    #     *something* for every command thrown at it ($cspec != empty)
                    _comp_complete_minimal "${args[@]}"
                fi
            elif [[ $cspec == *\ -[CF]\ * ]]; then
                if [[ $cspec == *' -F '* ]]; then
                    # complete -F <function>

                    # get function name
                    local func=${cspec#* -F }
                    func=${func%% *}
                    $func "${args[@]}"

                    # restart completion (once) if function exited with 124
                    if (($? == 124 && retry_count++ == 0)); then
                        # Note: When the completion function returns 124, the
                        # state of COMPREPLY is discarded.
                        COMPREPLY=()

                        cspec=$(complete -p -- "$compcmd" 2>/dev/null)

                        # Note: When completion spec is removed after 124, we
                        # do not generate any completions including the default
                        # ones. This is the behavior of the original Bash
                        # progcomp.
                        [[ $cspec ]] || break

                        continue
                    fi
                else
                    # complete -C <command>

                    # get command name
                    local completer=${cspec#* -C \'}

                    # completer commands are always single-quoted
                    if ! _comp_dequote "'$completer"; then
                        _minimal "${args[@]}"
                        break
                    fi
                    completer=${REPLY[0]}

                    local -a suggestions

                    local IFS=$' \t\n'
                    local reset_monitor=$(shopt -po monitor) reset_lastpipe=$(shopt -p lastpipe) reset_noglob=$(shopt -po noglob)
                    set +o monitor
                    shopt -s lastpipe
                    set -o noglob

                    COMP_KEY="$COMP_KEY" COMP_LINE="$COMP_LINE" \
                        COMP_POINT="$COMP_POINT" COMP_TYPE="$COMP_TYPE" \
                        $completer "${args[@]}" | mapfile -t suggestions

                    $reset_monitor
                    $reset_lastpipe
                    $reset_noglob
                    _comp_unlocal IFS

                    local suggestion
                    local i=0
                    COMPREPLY=()
                    for suggestion in "${suggestions[@]}"; do
                        COMPREPLY[i]+=${COMPREPLY[i]+$'\n'}$suggestion

                        if [[ $suggestion != *\\ ]]; then
                            ((i++))
                        fi
                    done
                fi

                # restore initial compopts
                local opt
                while [[ $cspec == *" -o "* ]]; do
                    # FIXME: should we take "+o opt" into account?
                    cspec=${cspec#*-o }
                    opt=${cspec%% *}
                    compopt -o "$opt"
                    cspec=${cspec#"$opt"}
                done
            else
                cspec=${cspec#complete}
                cspec=${cspec%%@("$compcmd"|"'${compcmd//\'/\'\\\'\'}'")}
                eval "_comp_compgen -- $cspec"
            fi
            break
        done
    fi
}

# A _comp_command_offset wrapper function for use when the offset is unknown.
# Only intended to be used as a completion function directly associated
# with a command, not to be invoked from within other completion functions.
#
# @since 2.12
_comp_command()
{
    # We unset the shell variable `words` locally to tell
    # `_comp_command_offset` that the index is intended to be that in
    # `COMP_WORDS` instead of `words`.
    local words
    unset -v words

    local offset i

    # find actual offset, as position of the first non-option
    offset=1
    for ((i = 1; i <= COMP_CWORD; i++)); do
        if [[ ${COMP_WORDS[i]} != -* ]]; then
            offset=$i
            break
        fi
    done
    _comp_command_offset $offset
}
complete -F _comp_command aoss command "do" else eval exec ltrace nice nohup padsp \
    "then" time tsocks vsound xargs

# @since 2.12
_comp_root_command()
{
    local PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
    local _comp_root_command=$1
    _comp_command
}
complete -F _comp_root_command fakeroot gksu gksudo kdesudo really

# Return true if the completion should be treated as running as root
#
# @since 2.12
_comp_as_root()
{
    [[ $EUID -eq 0 || ${_comp_root_command-} ]]
}

# Complete on available commands, subject to `no_empty_cmd_completion`.
# @return True (0) if one or more completions are generated, or otherwise False
# (1).  Note that it returns 1 even when the completion generation is canceled
# by `shopt -s no_empty_cmd_completion`.
#
# @since 2.12
_comp_compgen_commands()
{
    [[ ! ${cur-} ]] && shopt -q no_empty_cmd_completion && return 1
    # -o filenames for e.g. spaces in paths to and in command names
    _comp_compgen -- -c -o plusdirs && compopt -o filenames
}

# @since 2.12
_comp_complete_longopt()
{
    local cur prev words cword was_split comp_args
    _comp_initialize -s -- "$@" || return

    case "${prev,,}" in
        --help | --usage | --version)
            return
            ;;
        --!(no-*)dir*)
            _comp_compgen -a filedir -d
            return
            ;;
        --!(no-*)@(file|path)*)
            _comp_compgen -a filedir
            return
            ;;
        --+([-a-z0-9_]))
            local argtype=$(LC_ALL=C $1 --help 2>&1 | command sed -ne \
                "s|.*$prev\[\{0,1\}=[<[]\{0,1\}\([-A-Za-z0-9_]\{1,\}\).*|\1|p")
            case ${argtype,,} in
                *dir*)
                    _comp_compgen -a filedir -d
                    return
                    ;;
                *file* | *path*)
                    _comp_compgen -a filedir
                    return
                    ;;
            esac
            ;;
    esac

    [[ $was_split ]] && return

    if [[ $cur == -* ]]; then
        _comp_compgen_split -- "$(LC_ALL=C $1 --help 2>&1 |
            while read -r line; do
                [[ $line =~ --[A-Za-z0-9]+([-_][A-Za-z0-9]+)*=? ]] &&
                    printf '%s\n' "${BASH_REMATCH[0]}"
            done)"
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
    elif [[ $1 == *@(rmdir|chroot) ]]; then
        _comp_compgen -a filedir -d
    else
        [[ $1 == *mkdir ]] && compopt -o nospace
        _comp_compgen -a filedir
    fi
}
# makeinfo and texi2dvi are defined elsewhere.
complete -F _comp_complete_longopt \
    a2ps awk base{32,64,nc} bash bc bison cat chroot colordiff comm cp \
    csplit cut date df diff dir du enscript expand fmt fold gperf \
    grep grub head irb ld ldd less ln ls m4 mkdir mkfifo mknod \
    mv netstat nl nm numfmt objcopy objdump od paste pr ptx readelf \
    readlink realpath rm rmdir sed seq shar shred \
    shuf sort split stat strip sum sync tac tail tee \
    texindex touch tr uname unexpand uniq units vdir wc who

# @since 2.12
declare -Ag _comp_xspecs

# @since 2.12
_comp_complete_filedir_xspec()
{
    local cur prev words cword comp_args
    _comp_initialize -- "$@" || return
    _comp_compgen_filedir_xspec "$1"
}

# @since 2.12
_comp_compgen_filedir_xspec()
{
    _comp_compgen_tilde && return

    local REPLY
    _comp_quote_compgen "$cur"
    local quoted=$REPLY

    local xspec=${_comp_xspecs[${1##*/}]-${_xspecs[${1##*/}]-}}
    local -a toks
    _comp_compgen -v toks -c "$quoted" -- -d

    # Munge xspec to contain uppercase version too
    # https://lists.gnu.org/archive/html/bug-bash/2010-09/msg00036.html
    # news://news.gmane.io/4C940E1C.1010304@case.edu
    eval xspec="${xspec}"
    local matchop=!
    if [[ $xspec == !* ]]; then
        xspec=${xspec#!}
        matchop=@
    fi
    xspec="$matchop($xspec|${xspec^^})"

    _comp_compgen -av toks -c "$quoted" -- -f -X "@(|!($xspec))"

    # Try without filter if it failed to produce anything and configured to
    [[ ${BASH_COMPLETION_FILEDIR_FALLBACK-} && ${#toks[@]} -lt 1 ]] &&
        _comp_compgen -av toks -c "$quoted" -- -f

    ((${#toks[@]})) || return 1

    # Remove . and .. (as well as */. and */..) from suggestions, unless .. or
    # */.. was typed explicitly by the user (for users who use tab-completion
    # to append a slash after '..')
    if [[ $cur != ?(*/).. ]]; then
        _comp_compgen -Rv toks -- -X '?(*/)@(.|..)' -W '"${toks[@]}"' || return 1
    fi

    compopt -o filenames
    _comp_compgen -RU toks -- -W '"${toks[@]}"'
}

_comp__init_install_xspec()
{
    local xspec=$1 cmd
    shift
    for cmd in "$@"; do
        _comp_xspecs[$cmd]=$xspec
    done
}
# bzcmp, bzdiff, bz*grep, bzless, bzmore intentionally not here, see Debian: #455510
_comp__init_install_xspec '!*.?(t)bz?(2)' bunzip2 bzcat pbunzip2 pbzcat lbunzip2 lbzcat
_comp__init_install_xspec '!*.@(zip|[aegjkswx]ar|exe|pk3|wsz|zargo|xpi|crx|s[tx][cdiw]|sx[gm]|o[dt][tspgfc]|od[bm]|oxt|?(o)xps|epub|cbz|apk|apk[ms]|aab|xapk|ipa|hap|do[ct][xm]|p[op]t[mx]|xl[st][xm]|pyz|vsix|whl|[Ff][Cc][Ss]td)' unzip zipinfo
_comp__init_install_xspec '*.Z' compress znew
# zcmp, zdiff, z*grep, zless, zmore intentionally not here, see Debian: #455510
_comp__init_install_xspec '!*.@(Z|[gGd]z|t[ag]z)' gunzip zcat
_comp__init_install_xspec '!*.@(Z|[gGdz]z|t[ag]z)' unpigz
_comp__init_install_xspec '!*.Z' uncompress
# lzcmp, lzdiff intentionally not here, see Debian: #455510
_comp__init_install_xspec '!*.@(tlz|lzma)' lzcat lzegrep lzfgrep lzgrep lzless lzmore unlzma
_comp__init_install_xspec '!*.@(?(t)xz|tlz|lzma)' unxz xzcat
_comp__init_install_xspec '!*.lrz' lrunzip
_comp__init_install_xspec '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx)' ee
_comp__init_install_xspec '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|svg)' qiv
_comp__init_install_xspec '!*.@(gif|jp?(e)g?(2)|j2[ck]|jp[2f]|tif?(f)|png|p[bgpn]m|webp|bmp|x[bp]m|rle|rgb|pcx|fits|pm|?(e)ps)' xv
_comp__init_install_xspec '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview
_comp__init_install_xspec '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi kdvi
_comp__init_install_xspec '!*.dvi' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx
_comp__init_install_xspec '!*.[pf]df' acroread gpdf xpdf
_comp__init_install_xspec '!*.@(?(e)ps|pdf)' kpdf
_comp__init_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|md|markdown)?(.?(gz|GZ|bz2|BZ2|xz|XZ)))' okular
_comp__init_install_xspec '!*.pdf' epdfview pdfunite
_comp__init_install_xspec '!*.@(cb[rz7t]|djv?(u)|?(e)ps|pdf)' zathura
_comp__init_install_xspec '!*.@(?(e)ps|pdf)' ps2pdf ps2pdf12 ps2pdf13 ps2pdf14 ps2pdfwr
_comp__init_install_xspec '!*.texi*' makeinfo texi2html
_comp__init_install_xspec '!*.@(?(la)tex|texi|dtx|ins|ltx|dbj)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi xetex xelatex luatex lualatex
_comp__init_install_xspec '!*.mp3' mpg123 mpg321 madplay
_comp__init_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[agmvx]|OG[AGMVX]|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 cacaxine fbxine
_comp__init_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[agmvx]|OG[AGMVX]|opus|OPUS|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 totem
_comp__init_install_xspec '!*.@(avi|asf|wmv)' aviplay
_comp__init_install_xspec '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay
_comp__init_install_xspec '!*.@(mpg|mpeg|avi|mov|qt)' xanim
_comp__init_install_xspec '!*.@(og[ag]|m3u|flac|spx)' ogg123
_comp__init_install_xspec '!*.@(mp3|og[ag]|pls|m3u)' gqmpeg freeamp
_comp__init_install_xspec '!*.fig' xfig
_comp__init_install_xspec '!*.@(mid?(i)|cmf)' playmidi
_comp__init_install_xspec '!*.@(mid?(i)|rmi|rcp|[gr]36|g18|mod|xm|it|x3m|s[3t]m|kar)' timidity
_comp__init_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
_comp__init_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
_comp__init_install_xspec '!*.@(zip|z|gz|tgz)' bzme
# konqueror not here on purpose, it's more than a web/html browser
_comp__init_install_xspec '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx galeon dillo elinks amaya epiphany
_comp__init_install_xspec '!*.@(sxw|stw|sxg|sgl|doc?([mx])|dot?([mx])|rtf|txt|htm|html|?(f)odt|ott|odm|pdf)' oowriter lowriter
_comp__init_install_xspec '!*.@(sxi|sti|pps?(x)|ppt?([mx])|pot?([mx])|?(f)odp|otp)' ooimpress loimpress
_comp__init_install_xspec '!*.@(sxc|stc|xls?([bmx])|xlw|xlt?([mx])|[ct]sv|?(f)ods|ots)' oocalc localc
_comp__init_install_xspec '!*.@(sxd|std|sda|sdd|?(f)odg|otg)' oodraw lodraw
_comp__init_install_xspec '!*.@(sxm|smf|mml|odf)' oomath lomath
_comp__init_install_xspec '!*.odb' oobase lobase
_comp__init_install_xspec '!*.[rs]pm' rpm2cpio
_comp__init_install_xspec '!*.aux' bibtex
_comp__init_install_xspec '!*.po' poedit gtranslator kbabel lokalize
_comp__init_install_xspec '!*.@([Pp][Rr][Gg]|[Cc][Ll][Pp])' harbour gharbour hbpp
_comp__init_install_xspec '!*.[Hh][Rr][Bb]' hbrun
_comp__init_install_xspec '!*.ly' lilypond ly2dvi
_comp__init_install_xspec '!*.@(dif?(f)|?(d)patch)?(.@([gx]z|bz2|lzma))' cdiff
_comp__init_install_xspec '!@(*.@(ks|jks|jceks|p12|pfx|bks|ubr|gkr|cer|crt|cert|p7b|pkipath|pem|p10|csr|crl)|cacerts)' portecle
_comp__init_install_xspec '!*.@(mp[234c]|og[ag]|@(fl|a)ac|m4[abp]|spx|tta|w?(a)v|wma|aif?(f)|asf|ape)' kid3 kid3-qt
unset -f _comp__init_install_xspec

# Minimal completion to use as fallback in _comp_complete_load.
# TODO:API: rename per conventions
_comp_complete_minimal()
{
    local cur prev words cword comp_args
    _comp_initialize -- "$@" || return
    compopt -o bashdefault -o default
}
# Complete the empty string to allow completion of '>', '>>', and '<' on < 4.3
# https://lists.gnu.org/archive/html/bug-bash/2012-01/msg00045.html
complete -F _comp_complete_minimal ''

# Initialize the variable "_comp__base_directory"
# @var[out] _comp__base_directory
_comp__init_base_directory()
{
    local REPLY
    _comp_abspath "${BASH_SOURCE[0]-./bash_completion}"
    _comp__base_directory=${REPLY%/*}
    [[ $_comp__base_directory ]] || _comp__base_directory=/
    unset -f "$FUNCNAME"
}
_comp__init_base_directory

# @since 2.12
_comp_load()
{
    local flag_fallback_default="" IFS=$' \t\n'
    local OPTIND=1 OPTARG="" OPTERR=0 opt
    while getopts ':D' opt "$@"; do
        case $opt in
            D) flag_fallback_default=set ;;
            *)
                echo "bash_completion: $FUNCNAME: usage error" >&2
                return 2
                ;;
        esac
    done
    shift "$((OPTIND - 1))"

    local cmd=$1 cmdname=${1##*/} dir compfile
    local -a paths
    [[ $cmdname ]] || return 1

    local backslash=
    if [[ $cmd == \\* ]]; then
        cmd=${cmd:1}
        # If we already have a completion for the "real" command, use it
        $(complete -p -- "$cmd" 2>/dev/null || echo false) "\\$cmd" && return 0
        backslash=\\
    fi

    # Resolve absolute path to $cmd
    local REPLY pathcmd origcmd=$cmd
    if pathcmd=$(type -P -- "$cmd"); then
        _comp_abspath "$pathcmd"
        cmd=$REPLY
    fi

    local -a dirs=()

    # Lookup order:
    # 1) From BASH_COMPLETION_USER_DIR (e.g. ~/.local/share/bash-completion):
    # User installed completions.
    if [[ ${BASH_COMPLETION_USER_DIR-} ]]; then
        _comp_split -F : paths "$BASH_COMPLETION_USER_DIR" &&
            dirs+=("${paths[@]/%//completions}")
    else
        dirs=("${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions")
    fi

    # 2) From the location of bash_completion: Completions relative to the main
    # script. This is primarily for run-in-place-from-git-clone setups, where
    # we want to prefer in-tree completions over ones possibly coming with a
    # system installed bash-completion. (Due to usual install layouts, this
    # often hits the correct completions in system installations, too.)
    dirs+=("$_comp__base_directory/completions")

    # 3) From bin directories extracted from the specified path to the command,
    # the real path to the command, and $PATH
    paths=()
    [[ $cmd == /* ]] && paths+=("${cmd%/*}")
    _comp_realcommand "$cmd" && paths+=("${REPLY%/*}")
    _comp_split -aF : paths "$PATH"
    for dir in "${paths[@]%/}"; do
        [[ $dir == ?*/@(bin|sbin) ]] &&
            dirs+=("${dir%/*}/share/bash-completion/completions")
    done

    # 4) From XDG_DATA_DIRS or system dirs (e.g. /usr/share, /usr/local/share):
    # Completions in the system data dirs.
    _comp_split -F : paths "${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" &&
        dirs+=("${paths[@]/%//bash-completion/completions}")

    # Set up default $IFS in case loaded completions depend on it,
    # as well as for $compspec invocation below.
    local IFS=$' \t\n'

    # Look up and source
    shift
    local i prefix compspec
    for prefix in "" _; do # Regular from all dirs first, then fallbacks
        for i in ${!dirs[*]}; do
            dir=${dirs[i]}
            if [[ ! -d $dir ]]; then
                unset -v 'dirs[i]'
                continue
            fi
            for compfile in "$prefix$cmdname" "$prefix$cmdname.bash"; do
                compfile="$dir/$compfile"
                # Avoid trying to source dirs as long as we support bash < 4.3
                # to avoid an fd leak; https://bugzilla.redhat.com/903540
                if [[ -d $compfile ]]; then
                    # Do not warn with . or .. (especially the former is common)
                    [[ $compfile == */.?(.) ]] ||
                        echo "bash_completion: $compfile: is a directory" >&2
                elif [[ -e $compfile ]] && . "$compfile" "$cmd" "$@"; then
                    # At least $cmd is expected to have a completion set when
                    # we return successfully; see if it already does
                    if compspec=$(complete -p -- "$cmd" 2>/dev/null); then
                        # $cmd is the case in which we do backslash processing
                        [[ $backslash ]] && eval "$compspec \"\$backslash\$cmd\""
                        # If invoked without path, that one should be set, too
                        # ...but let's not overwrite an existing one, if any
                        [[ $origcmd != */* ]] &&
                            ! complete -p -- "$origcmd" &>/dev/null &&
                            eval "$compspec \"\$origcmd\""
                        return 0
                    fi
                    # If not, see if we got one for $cmdname
                    if [[ $cmdname != "$cmd" ]] && compspec=$(complete -p -- "$cmdname" 2>/dev/null); then
                        # Use that for $cmd too, if we have a full path to it
                        [[ $cmd == /* ]] && eval "$compspec \"\$cmd\""
                        return 0
                    fi
                    # Nothing expected was set, continue lookup
                fi
            done
        done
    done

    # Look up simple "xspec" completions
    [[ -v _comp_xspecs[$cmdname] || -v _xspecs[$cmdname] ]] &&
        complete -F _comp_complete_filedir_xspec "$cmdname" "$backslash$cmdname" && return 0

    if [[ $flag_fallback_default ]]; then
        complete -F _comp_complete_minimal -- "$origcmd" && return 0
    fi

    return 1
}

# set up dynamic completion loading
# @since 2.12
_comp_complete_load()
{
    # $1=_EmptycmD_ already for empty cmds in bash 4.3, set to it for earlier
    local cmd=${1:-_EmptycmD_}

    # Pass -D to define *something*, or otherwise there will be no completion
    # at all.
    _comp_load -D -- "$cmd" && return 124
} &&
    complete -D -F _comp_complete_load

# Function for loading and calling functions from dynamically loaded
# completion files that may not have been sourced yet.
# @param $1 completion file to load function from in case it is missing
# @param $2 the xfunc name.  When it does not start with `_',
#   `_comp_xfunc_${1//[^a-zA-Z0-9_]/_}_$2' is used for the actual name of the
#   shell function.
# @param $3... if any, specifies the arguments that are passed to the xfunc.
# @since 2.12
_comp_xfunc()
{
    local xfunc_name=$2
    [[ $xfunc_name == _* ]] ||
        xfunc_name=_comp_xfunc_${1//[^a-zA-Z0-9_]/_}_$xfunc_name
    declare -F -- "$xfunc_name" &>/dev/null || _comp_load -- "$1"
    "$xfunc_name" "${@:3}"
}

# Call a POSIX-compatible awk.  Solaris awk is not POSIX-compliant, but Solaris
# provides a POSIX-compatible version through /usr/xpg4/bin/awk.  We switch the
# implementation to /usr/xpg4/bin/awk in Solaris if any.
# @since 2.12
if [[ $OSTYPE == *solaris* && -x /usr/xpg4/bin/awk ]]; then
    _comp_awk()
    {
        /usr/xpg4/bin/awk "$@"
    }
else
    _comp_awk()
    {
        command awk "$@"
    }
fi

# List custom/extra completion files to source on the startup
## @param $1 path Path to "bash_completion"
## @var[out] _comp__init_startup_configs
_comp__init_collect_startup_configs()
{
    local base_path=${1:-${BASH_SOURCE[1]}}
    _comp__init_startup_configs=()

    # source compat completion directory definitions
    local -a compat_dirs=()
    local compat_dir
    if [[ ${BASH_COMPLETION_COMPAT_DIR-} ]]; then
        compat_dirs+=("$BASH_COMPLETION_COMPAT_DIR")
    else
        # Keep in sync with Makefile.am install-data-hook
        compat_dirs+=(/usr/local/share/examples/bash-completion//bash_completion.d)
        # Similarly as for the "completions" dir, look up from relative to
        # bash_completion, primarily for installed-with-prefix and
        # run-in-place-from-git-clone setups.  Notably we do it after the
        # system location here, in order to prefer in-tree variables and
        # functions.
        if [[ $_comp__base_directory == */share/bash-completion ]]; then
            compat_dir=${_comp__base_directory%/share/bash-completion}/etc/bash_completion.d
        else
            compat_dir=$_comp__base_directory/bash_completion.d
        fi
        [[ ${compat_dirs[0]} == "$compat_dir" ]] ||
            compat_dirs+=("$compat_dir")
    fi
    for compat_dir in "${compat_dirs[@]}"; do
        [[ -d $compat_dir && -r $compat_dir && -x $compat_dir ]] || continue
        local compat_files
        _comp_expand_glob compat_files '"$compat_dir"/*'
        local compat_file
        for compat_file in "${compat_files[@]}"; do
            [[ ${compat_file##*/} != @($_comp_backup_glob|Makefile*|${BASH_COMPLETION_COMPAT_IGNORE-}) &&
                -f $compat_file && -r $compat_file ]] &&
                _comp__init_startup_configs+=("$compat_file")
        done
    done

    # source user completion file
    #
    # Remark: We explicitly check that $user_completion is not '/dev/null'
    #   since /dev/null may be a regular file in broken systems and can contain
    #   arbitrary garbages of suppressed command outputs.
    local user_file=${BASH_COMPLETION_USER_FILE:-~/.bash_completion}
    [[ $user_file != "$base_path" && $user_file != /dev/null && -r $user_file && -f $user_file ]] &&
        _comp__init_startup_configs+=("$user_file")

    unset -f "$FUNCNAME"
}
_comp__init_collect_startup_configs "$BASH_SOURCE"
# shellcheck disable=SC2154
for _comp_init_startup_config in "${_comp__init_startup_configs[@]}"; do
    . "$_comp_init_startup_config"
done
unset -v _comp__init_startup_configs _comp_init_startup_config
unset -f have
unset -v have

set $_comp__init_original_set_v
unset -v _comp__init_original_set_v

# ex: filetype=sh
