# lxc-* commands completion

__lxc_names() {
    declare -a names
    case ${words[0]} in
        lxc-attach | lxc-cgroup | lxc-checkpoint | lxc-console | lxc-device | lxc-freeze | lxc-stop )
            mapfile -t names < <(command lxc-ls --running -1)
            ;;
        lxc-destroy | lxc-execute | lxc-snapshot | lxc-start )
            mapfile -t names < <(command lxc-ls --stopped -1)
            ;;
        lxc-copy | lxc-info | lxc-monitor | lxc-wait )
            mapfile -t names < <(command lxc-ls --defined -1)
            ;;
        lxc-autostart | lxc-create | lxc-checkconfig | lxc-config | lxc-ls | \
        lxc-top | lxc-unshare | lxc-update-config | lxc-usernsexec )
            ;;
        lxc-unfreeze )
            mapfile -t names < <(command lxc-ls --frozen -1)
            ;;
        *)
            # If we are running as an alias or symlink with different name,
            # fallback to old behaviour.
            mapfile -t names < <(command lxc-ls -1)
            ;;
    esac

    COMPREPLY=()
    for i in "${!names[@]}"; do
        # For composed names with spaces escaped by '\'.
        names[${i}]=$(command printf "%q" "${names[${i}]}")
        if [[ -n $(compgen -W "${names[${i}]}" -- "${cur}") ]]; then
            COMPREPLY+=("${names[${i}]}")
        fi
    done
}

__lxc_check_name_present() {
    mapfile -t names < <(command lxc-ls -1)
    local -r shortoptnamexp="^-[0-9A-Za-mo-z]*n[0-9A-Za-mo-z]*$"
    local container
    local param
    local parsed
    # Current word on command line, minus double/single quote and backslash.
    local -r current="${cur//[\\\"\']}"
    # If `--name` or `-n` are present, then a container name should be available.
    for i in "${!words[@]}"; do
        param="${words[${i}]}"
        # Parse names from command line when space is escaped by backslash.
        parsed="${param//[\\\"\']}"
        if [[ ${parsed} =~ ^--name(=(.*))?$ ]]; then
            if [[ -n "${BASH_REMATCH[2]}" ]]; then
                container="${BASH_REMATCH[2]}"
            else
                container="${words[${i}+1]}"
            fi
            command printf "%q" "${container}"
            return 0
        elif [[ ${parsed} =~ ${shortoptnamexp} ]]; then
            command printf "%q" "${words[${i}+1]}"
            return 0
        fi
        for name in "${names[@]}"; do
            if [[ "${parsed}" == "${name}" ]] && [[ "${current}" != "${parsed}" ]]; then
                command printf "%q" "${name}"
                return 0
            fi
        done
    done
    return 1
}

__lxc_append_name() {
    local -r name=$(__lxc_check_name_present)
    if [[ -z "${name}" ]]; then
        __lxc_names
    fi
}

__lxc_get_snapshots() {
    local -r container=$(__lxc_check_name_present)
    [[ -z "${container}" ]] && return
    mapfile -t snaps < <(command lxc-snapshot --name="${container}" --list)
    local -r nosnapxp="^No snapshots$"
    if [[ ! "${snaps[*]}" =~ ${nosnapxp} ]]; then
        for i in "${!snaps[@]}"; do
            read -r -e -a line <<< "${snaps[${i}]}"
            command printf "%s " "${line[0]}"
        done
    fi
}

__lxc_common_opt() {
    # End of options.
    if [[ "${words[*]}" =~ ' -- ' ]]; then
        return 1
    fi

    case ${prev} in
        --help | -h | -\? | --usage | --version )
            return 1
            ;;
        --lxcpath | -P )
            _filedir -d
            return 1
            ;;
        --logfile | -o )
            _filedir log
            return 1
            ;;
        --logpriority | -l )
            COMPREPLY=( $( compgen -W 'FATAL CRIT WARN ERROR NOTICE INFO DEBUG' -- "${cur}" ) )
            return 1
            ;;
        --quiet | -q )
            # Only flags.
            return
            ;;
    esac
}

__lxc_concat_array_sep() {
    local -r sep="${1}"
    local concat
    for word in "${@:2}"; do
	if [[ "${word}" == "${sep}" ]]; then
	    concat+="${word}"
	else
	    concat+="${word}${sep}"
	fi
    done
    command printf "%s" "${concat}"
}

__lxc_check_completion_avail() {
    local -r word="${1}"
    local -r pattern="^${word}"
    for w in "${@:2}"; do
        if [[ "${w}" =~ ${pattern} ]] && [[ "${w}" != "${word}" ]]; then
           return 0
        fi
    done
    return 1
}

__lxc_array_has_duplicates() {
    declare -A unique
    for word in "${@}"; do
	if [[ -z "${unique[${word}]}" ]]; then
	    unique["${word}"]="${word}"
	else
	    return 0
	fi
    done
    return 1
}

__lxc_check_word_in_array() {
    local -r word="${1}"
    for w in "${@:2}"; do
        if [[ "${w}" == "${word}" ]]; then
           return 0
        fi
    done
    return 1
}

__lxc_piped_args() {
    local -r currentWord="${1}"
    local -r sep="${2}"
    declare -a completionWords=("${@:3}")
    # Remove double/single quote and backslash from current completion parameter.
    IFS=$"${sep}" read -r -e -a current <<< "${currentWord//[\\\"\']}"

    # Add separator back to current in case it is part of completion list.
    for i in "${!current[@]}"; do
        if [[ -z "${current[${i}]}" ]]; then
	    current["${i}"]="${sep}"
        fi
    done

    # Remove words from completion already added to argument.
    declare -a minuslast=("${current[@]::${#current[@]}-1}")
    declare -a completion=("${completionWords[@]}")
    for i in "${!completion[@]}"; do
        if __lxc_check_word_in_array "${completion[${i}]}" "${minuslast[@]}"; then
            command unset -v 'completion[${i}]'
        fi
    done
    completion=("${completion[@]}")

    # Check if words from argument are uniquely part of completion.
    if __lxc_array_has_duplicates "${minuslast[@]}"; then
        return
    fi
    declare -a allcomps=("${completionWords[@]}")
    for i in "${!minuslast[@]}"; do
        if ! __lxc_check_word_in_array "${minuslast[${i}]}" "${allcomps[@]}"; then
            return
        fi
    done

    # Actual completion array.
    declare -a extcompletion
    local -r nparts="${#current[@]}"

    if [[ "${nparts}" -gt 0 ]]; then
        local prefix=$(__lxc_concat_array_sep "${sep}" "${current[@]::${nparts}-1}")
        local -r lastword="${current[${nparts}-1]}"
        if __lxc_check_completion_avail "${lastword}" "${completion[@]}"; then
            for comp in "${completion[@]}"; do
                extcompletion+=("\"${prefix}${comp}\"")
            done
        fi
        # TAB after quotes to complete for next value.
        if ! __lxc_array_has_duplicates "${current[@]}" && __lxc_check_word_in_array "${lastword}" "${allcomps[@]}"; then
            if ! __lxc_check_completion_avail "${lastword}" "${completion[@]}" || [[ "${#currentWord}" -lt "${#sep}" ]] || [[ "${currentWord: -${#sep}}" == "${sep}" ]]; then
                prefix=$(__lxc_concat_array_sep "${sep}" "${current[@]}")
                for comp in "${completion[@]}"; do
                    [[ "${comp}" == "${lastword}" ]] && continue
                    if [[ "${comp}" != "${sep}" ]]; then
                        extcompletion+=("\"${prefix}${comp}\"")
                    else
                        # Trailing sep.
                        extcompletion+=("\"${prefix}\"")
                        [[ "${#completion[@]}" -gt 2 ]] && extcompletion+=("\"${prefix}${comp}\"")
                    fi
                done
            fi
        fi
    else # [[ "${nparts}" -eq 0 ]]
        for word in "${allcomps[@]}"; do
            extcompletion+=("${word}")
        done
    fi

    COMPREPLY=( $( compgen -P '"' -S '"' -W "$(command echo -e ${extcompletion[@]})" -- "${cur}" ) )
    [[ "${#extcompletion[@]}" -gt 1 ]] && compopt -o nospace
}

__lxc_get_selinux_contexts() {
    declare -a sepolicies=()
    local sepolicy
    # Check for SElinux tool.
    if ! command -v semanage > /dev/null 2>&1; then
        return
    fi
    # Skip header + following empty line.
    mapfile -s 2 -t output < <(command semanage fcontext -l 2>/dev/null)
    local -r none="<<None>>"
    for line in "${output[@]}"; do
        if [[ "${line}" =~ "SELinux Distribution fcontext Equivalence" ]]; then
            break
        fi
        read -r -e -a current <<< "${line}"
        if [[ "${#current[@]}" -gt 0 ]]; then
            sepolicy="${current[${#current[@]}-1]}"
            [[ ! "${sepolicy}" =~ ${none} ]] && sepolicies+=("${sepolicy}")
        fi
    done
    # Default context.
    sepolicies+=("unconfined_u:object_r:default_t:s0")
    COMPREPLY=( $( compgen -P'"' -S'"' -W "${sepolicies[*]}" -- "${cur}" ) )
}

_lxc_attach() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile | -f )
            _filedir
            return
            ;;
        --pty-log | -L )
            _filedir log
            return
            ;;
        --arch | -a )
            # https://github.com/lxc/lxc/blob/stable-4.0/src/tests/arch_parse.c#L37
            COMPREPLY=( $( compgen -W 'arm armel armhf armv7l athlon i386 i486 i586 i686 linux32 mips mipsel ppc powerpc x86 aarch64 amd64 arm64 linux64 loongarch64 mips64 mips64el ppc64 ppc64el ppc64le powerpc64 riscv64 s390x x86_64' -- "${cur}" ) )
            return
            ;;
        --elevated-privileges | -e )
            __lxc_piped_args "${cur}" '|' CGROUP CAP LSM
            return
            ;;
        --namespaces | -s )
            __lxc_piped_args "${cur}" '|' MOUNT PID UTSNAME IPC USER NETWORK
            return
            ;;
        --remount-sys-proc | -R | --keep-env | --clear-env )
            # Only flags.
            ;;
        --set-var | -v )
            # custom VAR=VALUE
            return
            ;;
        --keep-var )
            COMPREPLY=( $( compgen -A variable -- "${cur}" ) )
            return
            ;;
        --uid | -u )
            _uids
            return
            ;;
        --gid | -g )
            _gids
            return
            ;;
        --context | -c )
            __lxc_get_selinux_contexts
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_attach lxc-attach

__lxc_get_groups() {
    declare -A groups
    local key
    declare -a linegroups
    # Discard "GROUPS" header and lines without any groups (with only '-').
    mapfile -s 1 -t lines < <(command lxc-ls -f --fancy-format GROUPS | command sed -e '/^-/d')
    for line in "${lines[@]}"; do
        line=$(command echo -e "${line}" | command sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
        IFS=$',' read -r -e -a linegroups <<< "${line}"
        for entry in "${linegroups[@]}"; do
            key=$(command printf "%q" "${entry}")
            groups+=(["${key}"]=1)
        done
    done
    declare -a output=("${!groups[@]}")
    command printf "%s" "${output[*]}"
}

_lxc_autostart() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --reboot | -r | --shutdown | -s | --kill | -k | --list | -L | --all | -a | --ignore-auto | -A )
            # Only flags.
            ;;
        --timeout | -t )
            COMPREPLY=( $( compgen -P "${cur}" -W "{0..9}" ) )
            compopt -o nospace
            return
            ;;
        --groups | -g )
            __lxc_piped_args "${cur}" ',' $( __lxc_get_groups ) ','
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
} &&
    complete -F _lxc_autostart lxc-autostart

__lxc_cgroup_v2() {
    declare -a stateObjects
    local -r path="${1}"
    [[ ! -f "${path}/cgroup.controllers" ]] && return
    for controller in $(<"${path}/cgroup.controllers"); do
        for so in "${path}/${controller}".*; do
            [[ ! -f "${so}" ]] && continue
            stateObjects+=("${so##*/}")
        done
    done
    command printf "%s" "${stateObjects[*]}"
}

__lxc_cgroup_v1() {
    declare -a stateObjects
    local -r path="${1}"
    local prefix
    for controller in "${path}"/*; do
        [[ ! -d "${controller}" ]] && continue
        prefix="${controller##*/}"
        for so in "${controller}/${prefix}".*; do
            [[ ! -f "${so}" ]] && continue
            stateObjects+=("${so##*/}")
        done
    done
    command printf "%s" "${stateObjects[*]}"
}

__lxc_cgroup_state_object() {
    local -r name="${1}"
    local -r cgroupPath="/sys/fs/cgroup"
    local output
    local -r userSlicePath="${cgroupPath}/user.slice"
    local -r lxcPayloadPath="${cgroupPath}/lxc.payload.${name}"
    if [[ -d "${userSlicePath}" ]]; then
        # cgroup_v2 + user.slice
        read -r -e -a output <<< $(__lxc_cgroup_v2 "${userSlicePath}")
    elif [[ -d "${lxcPayloadPath}" ]]; then
        # cgroup_v2 + lxc.payload
        read -r -e -a output <<< $(__lxc_cgroup_v2 "${lxcPayloadPath}")
    else
        # cgroup_v1
        read -r -e -a output <<< $(__lxc_cgroup_v1 "${cgroupPath}")
    fi

    # Check if state-object is present already.
    for w in "${words[@]}"; do
        if [[ "${cur}" != "${w}" ]] && __lxc_check_word_in_array "${w}" "${output[@]}"; then
            return
        elif [[ "${cur}" == "${w}" ]] && ! __lxc_check_completion_avail "${w}" "${output[@]}"; then
            return
        fi
    done

    COMPREPLY=( $( compgen -W "${output[*]}" -- "${cur}" ) )
}

_lxc_cgroup() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi

    local -r custom=$(__lxc_check_name_present)
    if [[ -n "${custom}" ]]; then
        __lxc_cgroup_state_object "${custom}"
    else
        __lxc_append_name
    fi
} &&
    complete -F _lxc_cgroup lxc-cgroup

_lxc_checkpoint() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --restore | -r | --stop | -s | --verbose | -v | --daemon | -d | --foreground | -F )
            # Only flags.
            ;;
        --checkpoint-dir | -D )
            _filedir -d
            return
            ;;
        --action-script | -A )
            _filedir
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_checkpoint lxc-checkpoint

_lxc_config() {
    local cur prev words cword split
    _init_completion -s -n : || return

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '-l' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    COMPREPLY=( $( compgen -W "$( command lxc-config -l )" -- "${cur}" ) )
} &&
    complete -F _lxc_config lxc-config

_lxc_console() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --escape | -e )
            COMPREPLY+=( $( compgen -P "'" -S "'" -W "^{a..z} {a..z}" -- "${cur}" ) )
            return
            ;;
        --tty | -t )
            COMPREPLY=( $( compgen -P "${cur}" -W "{0..9}" ) )
            compopt -o nospace
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_console lxc-console

__lxc_backing_stores() {
    COMPREPLY=( $( compgen -W 'best btrfs dir loop lvm nbd overlay overlayfs rbd zfs' -- "${cur}" ) )
}

__lxc_size_unit() {
    if [[ -n "${cur}" ]] && [[ ! "${cur}" =~ ^[0-9]+$ ]]; then
        return
    fi
    # Size.
    if [[ -z "${cur}" ]]; then
        COMPREPLY=( $( compgen -P "${cur}" -W "{1..9}" ) )
    else
        COMPREPLY=( $( compgen -P "${cur}" -W "{0..9}" ) )
        # Unit
        COMPREPLY+=( $( compgen -P "${cur}" -W "$( command echo ${@})" ) )
    fi
    compopt -o nospace
}

_lxc_copy() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --newname | -N | --mount | -m )
            return
            ;;
        --newpath | -p )
            _filedir -d
            return
            ;;
        --rename | -R | --snapshot | -s | --allowrunning | -a | --foreground | -F | --daemon | -d | --tmpfs | -t | --keepname | -K | --keepdata | -D | --keepmac | -M )
            # Only flags.
            ;;
        --backingstorage | -B )
            __lxc_backing_stores
            return
            ;;
        --fssize | -L )
            __lxc_size_unit K M G T
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_copy lxc-copy

__lxc_templates() {
    COMPREPLY=( $( compgen -W "$(command ls /usr/share/lxc/templates/ | command sed -e 's|^lxc-||' )" -- "${cur}" ) )
}

_lxc_create() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --config | -f )
            _filedir
            return
            ;;
        --template | -t )
            __lxc_templates
            return
            ;;
        --bdev | -B )
            __lxc_backing_stores
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
} &&
    complete -F _lxc_create lxc-create

_lxc_destroy() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --force | -f | --snapshots | -s )
            # Only flags.
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_destroy lxc-destroy

_lxc_device() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    case ${prev} in
        -h )
            return
            ;;
        --name | -n )
            __lxc_names
            return
            ;;
        add )
            _available_interfaces
            COMPREPLY+=( $( compgen -f -d -X "!*/?*" -- "${cur:-/dev/}" ) )
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_device lxc-device

_lxc_execute() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile | -f )
            _filedir
            return
            ;;
        --define | -s )
            # @TODO: list values from source code.
            # tag=value
            # https://github.com/lxc/lxc/blob/stable-4.0/src/lxc/confile.c#L178
            return
            ;;
        --daemon | -d )
            # Only flags.
            ;;
        --uid | -u )
            _uids
            return
            ;;
        --gid | -g )
            _gids
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_execute lxc-execute

_lxc_freeze() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile | -f )
            _filedir
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_freeze lxc-freeze

_lxc_info() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --config | -c )
            # @TODO: list values from source code.
            # tag
            # https://github.com/lxc/lxc/blob/stable-4.0/src/lxc/confile.c#L178
            return
            ;;
        --ips | -i | --pid | -p | --stats | -S | --no-humanize | -H | --state | -s )
            # Only flags.
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_info lxc-info

_lxc_ls() {
    local cur prev words cword split
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --line | -1 | --fancy | -f | --active | --frozen | --running | --stopped | --defined )
            # Only flags.
            ;;
        --fancy-format | -F )
            __lxc_piped_args "${cur}" ',' NAME STATE PID RAM SWAP AUTOSTART GROUPS INTERFACE IPV4 IPV6 UNPRIVILEGED
            return
            ;;
        --groups | -g )
            __lxc_piped_args "${cur}" ',' $( __lxc_get_groups ) ','
            return
            ;;
        --nesting )
            COMPREPLY=( $( compgen -P "${cur}" -W "{0..9}" ) )
            compopt -o nospace
            return
            ;;
        --filter )
            # POSIX extended regular expression.
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
} &&
    complete -F _lxc_ls lxc-ls

_lxc_monitor() {
    local cur prev words cword split
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --quit | -Q )
            # Only flags.
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_monitor lxc-monitor

_lxc_snapshot() {
    local cur prev words cword split
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --comment | -c )
            _filedir
            return
            ;;
        --destroy | -d )
            COMPREPLY=( $( compgen -W 'ALL $( __lxc_get_snapshots )' -- "${cur}" ) )
            return
            ;;
        --list | -L | --showcomments | -C )
            # Only flags.
            ;;
        --restore | -r )
            COMPREPLY=( $( compgen -W '$( __lxc_get_snapshots )' -- "${cur}" ) )
            return
            ;;
        --newname | -N )
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_snapshot lxc-snapshot

_lxc_start() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --daemon | -d | --foreground | -F | --close-all-fds | -C )
            # Only flags.
            ;;
        --pidfile | -p )
            _filedir pid
            return
            ;;
        --rcfile | -f )
            _filedir
            return
            ;;
        --console | -c )
            # Output devices, such as /dev/tty*
            _filedir
            return
            ;;
        --console-log | -L)
            _filedir
            return
            ;;
        --define | -s )
            # @TODO: list values from source code.
            # tag=value
            # https://github.com/lxc/lxc/blob/stable-4.0/src/lxc/confile.c#L178
            return
            ;;
        --share-net | --share-ipc | --share-uts )
            _pids
            COMPREPLY+=( $( compgen -W "$( command lxc-ls --active )" -- "${cur}" ) )
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )

        # Needed due to weird `_parse_help` output for the `--share-*` options.
	COMPREPLY=( $( compgen -W '${COMPREPLY[@]/--share-} --share-net --share-ipc --share-uts' -- "${cur}" ) )

        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_start lxc-start

_lxc_stop() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --reboot | -r | --kill | -k | --nokill | --nolock | --nowait | -W )
            # Only flags.
            ;;
        --timeout | -t )
            COMPREPLY=( $( compgen -P "${cur}" -W "{0..9}" ) )
            compopt -o nospace
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_stop lxc-stop

_lxc_top() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --batch | -b | --reverse | -r )
            # Only flags.
            ;;
        --delay | -d )
            COMPREPLY=( $( compgen -P "${cur}" -W "{0..9}" ) )
            compopt -o nospace
            return
            ;;
        --sort | -s )
            COMPREPLY=( $( compgen -W 'n c b m k' -- "${cur}" ) )
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
} &&
    complete -F _lxc_top lxc-top

_lxc_unfreeze() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_unfreeze lxc-unfreeze

_lxc_unshare() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --namespaces | -s )
            __lxc_piped_args "${cur}" '|' MOUNT PID UTSNAME IPC USER NETWORK
            return
            ;;
        --user | -u )
            _uids
            return
            ;;
        --hostname | -H )
            return
            ;;
        --ifname | -i )
            _available_interfaces
            return
            ;;
        --daemon | -d | --remount | -M )
            # Only flags.
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
} &&
    complete -F _lxc_unshare lxc-unshare

_lxc_update_config() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    case ${prev} in
        --help | -h )
            return
            ;;
        --config | -c )
            _filedir
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
} &&
    complete -F _lxc_update_config lxc-update-config

__lxc_id_mapping() {
    local -r tmp="${cur//[^:]}"
    if [[ "${#tmp}" -eq 0 ]]; then
        COMPREPLY=( $( compgen -W "u: g: b:" -- "${cur}" ) )
    fi
    compopt -o nospace
}

_lxc_usernsexec() {
    local cur prev words cword split
    COMPREPLY=()
    _init_completion -s -n : || return

    # End of options.
    if [[ "${words[*]}" =~ ' -- ' ]]; then
        return
    fi

    case ${prev} in
        -h )
            return
            ;;
        -m )
            # ^[ugb]:[0-9]+:[0-9]+(:[0-9]+)?$
            __lxc_id_mapping
            return
            ;;
        -s )
            # Only flags.
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '-h -m -s' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
    fi
} &&
    complete -F _lxc_usernsexec lxc-usernsexec

_lxc_wait() {
    local cur prev words cword split
    _init_completion -s -n : || return

    __lxc_common_opt || return

    case ${prev} in
        --name | -n )
            __lxc_names
            return
            ;;
        --rcfile )
            _filedir
            return
            ;;
        --state | -s )
            __lxc_piped_args "${cur}" '|' STOPPED STARTING RUNNING STOPPING ABORTING FREEZING FROZEN THAWED
            return
            ;;
        --timeout | -t )
            COMPREPLY=( $( compgen -P "${cur}" -W "{0..9}" ) )
            compopt -o nospace
            return
            ;;
    esac

    $split && return

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "${cur}" ) )
        [[ ${COMPREPLY-} == *= ]] && compopt -o nospace
        return
    fi
    __lxc_append_name
} &&
    complete -F _lxc_wait lxc-wait

# ex: filetype=sh
