# bash completion for qvm -*- shell-script -*- ## vim: set ft=bash ## SPDX-FileCopyrightText: 2018 Bernhard Haaber ## SPDX-FileCopyrightText: 2018 Ivan ## ## SPDX-License-Identifier: GPL-2.0-only ## Credits: ## - https://www.mail-archive.com/qubes-users@googlegroups.com/msg20088.html ## - https://github.com/taradiddles/qubes-os-qvm-bash-completion # --------------------------------------------------------------------------- # COMP_CWORD related functions # Get the relative position of COMP_CWORD with option words ignored # Note: This logic is flawed when using option arguments (eg. -s blah). # Unfortunately there is no way to solve this except parsing every # known option for a given qvm-* command _get-cword-pos() { local index=0 local i for ((i=1; i<=COMP_CWORD; i++)); do [[ ${COMP_WORDS[i]} == -* ]] && continue ((index++)) done printf '%s\n' "${index}" } # Get the relative position of the first COMP_CWORD with option words ignored # Note: Same limitation as _get-cword-pos() above. _get-first-cword() { local i for ((i=1; i<=COMP_CWORD; i++)); do [[ ${COMP_WORDS[i]} == -* ]] && continue printf '%s\n' "${COMP_WORDS[i]}" return 0 done printf '%s\n' "" } # --------------------------------------------------------------------------- # Completion functions (set COMPREPLY) # Sets COMPREPLY to an array of qubes in a given state _complete-qubes() { local qubes local state="${1}" local state_re='' local cur="${COMP_WORDS[COMP_CWORD]}" case "${state}" in running|halted|paused) state_re="${state}" ;; runtrans) state_re='\(running\|transient\)' ;; any_state) state_re='[^|]\+' ;; esac qubes=$(qvm-ls --raw-data | grep -v -e '^dom0|' | \ grep -i -e "^[^|]\+|${state_re}|" | cut -f1 -d"|") mapfile -t COMPREPLY < <(compgen -W "${qubes}" -- "${cur}") return 0 } # Sets COMPREPLY to an array of qube properties (prefs, features, tags, service) _complete-qubeprops() { local qube="${1}" local property="${2}" local props local cur="${COMP_WORDS[COMP_CWORD]}" if qvm-check "${qube}" > /dev/null 2>&1; then case "${property}" in prefs|features|tags|service) props=$("qvm-${property}" "${qube}" | \ cut -f1 -d " ") ;; esac fi mapfile -t COMPREPLY < <(compgen -W "${props}" -- "${cur}") } # Sets COMPREPLY to an array of qvm-tags options _complete-qvm-tags-actions() { local cur="${COMP_WORDS[COMP_CWORD]}" local options=$(qvm-tags --help | \ sed -ne 's/.*VMNAME {\([^}]\+\)}.*/\1/p' | tr ',' '\n') mapfile -t COMPREPLY < <(compgen -W "${options}" -- "${cur}") } # Sets COMPREPLY to an array of file names _complete-filenames() { local cur="${COMP_WORDS[COMP_CWORD]}" mapfile -t COMPREPLY < <(compgen -f -- "$cur") return 0 } # --------------------------------------------------------------------------- # "Link" completion functions to qvm-* commands # complete each argument with qubes in any state _qvmcmd-any_state-all_args() { _complete-qubes "any_state" } complete -F _qvmcmd-any_state-all_args qvm-backup complete -F _qvmcmd-any_state-all_args qvm-ls # complete the first argument with qubes in a given state _qvmcmd-in_state() { local state="$1" [ "$(_get-cword-pos "${COMP_CWORD}")" = 1 ] && _complete-qubes "${state}" } _qvmcmd-any_state() { _qvmcmd-in_state "any_state"; } complete -F _qvmcmd-any_state qvm-check complete -F _qvmcmd-any_state qvm-clone complete -F _qvmcmd-any_state qvm-firewall complete -F _qvmcmd-any_state qvm-remove complete -F _qvmcmd-any_state qvm-run complete -F _qvmcmd-any_state qvm-service complete -F _qvmcmd-any_state qvm-start-gui complete -F _qvmcmd-any_state qvm-usb _qvmcmd-halted() { _qvmcmd-in_state "halted"; } complete -F _qvmcmd-halted qvm-start _qvmcmd-paused() { _qvmcmd-in_state "paused"; } complete -F _qvmcmd-paused qvm-unpause _qvmcmd-running() { _qvmcmd-in_state "running"; } complete -F _qvmcmd-running qvm-pause complete -F _qvmcmd-running qvm-shutdown _qvmcmd-runtrans() { _qvmcmd-in_state "runtrans"; } complete -F _qvmcmd-runtrans qvm-kill # complete the first argument with qubes in any state # complete n>=2 arguments with filenames _qvmcmd-any_state-filenames() { if [ "$(_get-cword-pos "${COMP_CWORD}")" = 1 ]; then _complete-qubes "any_state" else _complete-filenames fi } complete -F _qvmcmd-any_state-filenames qvm-copy-to-vm complete -F _qvmcmd-any_state-filenames qvm-move-to-vm # complete the first argument with qubes in any state # complete the second argument with qube properties (features, prefs, ...) _qvmcmd-any_state-qubeprop() { local property="$1" case "$(_get-cword-pos "${COMP_CWORD}")" in 1) _complete-qubes "any_state" ;; 2) _complete-qubeprops "$(_get-first-cword)" "${property}" ;; esac } _qvmcmd-any_state-qubeprefs() { _qvmcmd-any_state-qubeprop "prefs"; } complete -F _qvmcmd-any_state-qubeprefs qvm-prefs _qvmcmd-any_state-qubefeatures() { _qvmcmd-any_state-qubeprop "features"; } complete -F _qvmcmd-any_state-qubefeatures qvm-features _qvmcmd-any_state-qubeservice() { _qvmcmd-any_state-qubeprop "service"; } complete -F _qvmcmd-any_state-qubeservice qvm-service # complete the first argument with qubes in any state # complete the second argument with qvm-tags actions # complete the third argument with qube properties (features, prefs, ...) _qvmcmd-any_state-qubetags() { case "$(_get-cword-pos "${COMP_CWORD}")" in 1) _complete-qubes "any_state" ;; 2) _complete-qvm-tags-actions ;; 3) _complete-qubeprops "$(_get-first-cword)" "tags" ;; esac } complete -F _qvmcmd-any_state-qubetags qvm-tags