diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index e1716f6024..45dfe89c34 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -1,16 +1,65 @@ -# nmcli(1) completion -*- shell-script -*- -# Originally based on -# https://github.com/GArik/bash-completion/blob/master/completions/nmcli +# nmcli(1) completion -_nmcli_list() +_nmcli_array_delete_at() { - COMPREPLY=( $( compgen -W '$1' -- $cur ) ) + eval "local ARRAY=(\"\${$1[@]}\")" + local i + local tmp=() + local lower=$2 + local upper=${3:-$lower} + + # for some reason the following fails. So this clumsy workaround... + # A=(a "") + # echo " >> ${#A[@]}" + # >> 2 + # A=("${A[@]:1}") + # echo " >> ${#A[@]}" + # >> 0 + # ... seriously??? + + for i in "${!ARRAY[@]}"; do + if [[ "$i" -lt "$2" || "$i" -gt "${3-$2}" ]]; then + tmp=("${tmp[@]}" "${ARRAY[$i]}") + fi + done + eval "$1=(\"\${tmp[@]}\")" } -_nmcli_list_nl() +_nmcli() { + local cur words cword i output + _init_completion || return + + # we don't care about any arguments after the current cursor position + # because we only parse from left to right. So, if there are some arguments + # right of the cursor, just ignore them. Also don't care about ${words[0]}. + _nmcli_array_delete_at words $((cword+1)) ${#words[@]} + _nmcli_array_delete_at words 0 + + # _init_completion returns the words with all the quotes and escaping + # characters. We don't care about them, drop them at first. + for i in ${!words[@]}; do + words[i]="$(printf '%s' "${words[i]}" | xargs printf '%s\n' 2>/dev/null || true)" + done + + # In case the cursor is not at the end of the line, + # $cur consists of spaces that we want do remove. + # For example: `nmcli connection modify id lo` + if [[ "$cur" =~ ^[[:space:]]+ ]]; then + cur='' + fi + + output="$(nmcli --complete-args "${words[@]}" 2>/dev/null)" + + # Bail out early if we're completing a file name + if [ $? = 65 ]; then + compopt -o default + COMPREPLY=() + return 0 + fi + local IFS=$'\n' - COMPREPLY=( $( compgen -W '$1' -- $cur ) ) + COMPREPLY=( $( compgen -W '$output' -- $cur ) ) # Now escape special characters (spaces, single and double quotes), # so that the argument is really regarded a single argument by bash. @@ -25,7 +74,7 @@ _nmcli_list_nl() # [']bla'bla"bla\bla bla --> [']bla'\''bla"bla\bla bla COMPREPLY[$i]="${entry//\'/${escaped_single_quote}}" elif [[ "${cur:0:1}" == '"' ]]; then - # started with double quote, escaping all double quotes, backslashes and ! + # started with double quote, escaping all double quotes and all backslashes # ["]bla'bla"bla\bla bla --> ["]bla'bla\"bla\\bla bla entry="${entry//\\/\\\\}" entry="${entry//\"/\\\"}" @@ -60,1239 +109,7 @@ _nmcli_list_nl() COMPREPLY[$i]=${entry} (( i++ )) done -} -_nmcli_con_show() -{ - nmcli -t -f "$1" connection show $2 2> /dev/null -} - -_nmcli_wifi_list() -{ - nmcli -t -f "$1" device wifi list 2>/dev/null -} - -_nmcli_dev_status() -{ - nmcli -t -f "$1" device status 2>/dev/null -} - -_nmcli_array_has_value() { - # expects the name of an array as first parameter and - # returns true if if one of the remaining arguments is - # contained in the array ${$1[@]} - eval "local ARRAY=(\"\${$1[@]}\")" - local arg a - shift - for arg; do - for a in "${ARRAY[@]}"; do - if [[ "$a" = "$arg" ]]; then - return 0 - fi - done - done - return 1 -} - -_nmcli_array_delete_at() -{ - eval "local ARRAY=(\"\${$1[@]}\")" - local i - local tmp=() - local lower=$2 - local upper=${3:-$lower} - - # for some reason the following fails. So this clumsy workaround... - # A=(a "") - # echo " >> ${#A[@]}" - # >> 2 - # A=("${A[@]:1}") - # echo " >> ${#A[@]}" - # >> 0 - # ... seriously??? - - for i in "${!ARRAY[@]}"; do - if [[ "$i" -lt "$2" || "$i" -gt "${3-$2}" ]]; then - tmp=("${tmp[@]}" "${ARRAY[$i]}") - fi - done - eval "$1=(\"\${tmp[@]}\")" -} - -_nmcli_compl_match_option() -{ - local S="$1" - local V - shift - if [[ "${S:0:2}" == "--" ]]; then - S="${S:2}" - elif [[ "${S:0:1}" == "-" ]]; then - S="${S:1}" - else - return 1 - fi - for V; do - case "$V" in - "$S"*) - printf "%s" "$V" - return 0 - ;; - esac - done - return 1 -} - -# OPTIONS appear first at the command line (before the OBJECT). -# This iterates over the argument list and tries to complete -# the options. If there are options that are to be completed, -# zero is returned and completion will be performed. -# Otherwise it will remove all the option parameters from the ${words[@]} -# array and return with zero (so that completion of OBJECT can continue). -_nmcli_compl_OPTIONS() -{ - local i W - - for (( ; ; )); do - if [[ "${#words[@]}" -le 1 ]]; then - return 1 - fi - W="$(_nmcli_compl_match_option "${words[0]}" "${LONG_OPTIONS[@]}")" - if [[ $? != 0 ]]; then - return 2 - fi - - # remove the options already seen. - for i in ${!LONG_OPTIONS[@]}; do - if [[ "${LONG_OPTIONS[$i]}" == "$W" ]]; then - _nmcli_array_delete_at LONG_OPTIONS $i - break - fi - done - - if [[ "$HELP_ONLY_AS_FIRST" == '1' ]]; then - for i in ${!LONG_OPTIONS[@]}; do - if [[ "${LONG_OPTIONS[$i]}" == "help" ]]; then - _nmcli_array_delete_at LONG_OPTIONS $i - break - fi - done - fi - - case "$W" in - terse) - _nmcli_array_delete_at words 0 - ;; - pretty) - _nmcli_array_delete_at words 0 - ;; - ask) - _nmcli_array_delete_at words 0 - ;; - show-secrets) - _nmcli_array_delete_at words 0 - ;; - order) - if [[ "${#words[@]}" -eq 2 ]]; then - local ord="${words[1]}" - local ord_sta="" - local i - local c=() - - # FIXME: currently the completion considers colon as separator - # for words. Hence the following doesn't work as $ord will - # not contain any colons at this point. - # See https://bugzilla.gnome.org/show_bug.cgi?id=745157 - - if [[ $ord = *":"* ]]; then - ord_sta="${ord%:*}:" - ord="${ord##*:}" - fi - if [[ $ord = [-+]* ]]; then - ord_sta="$ord_sta${ord:0:1}" - fi - for i in active name type path; do - c=("${c[@]}" "$ord_sta$i") - done - _nmcli_list "${c[*]}" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - active) - _nmcli_array_delete_at words 0 - ;; - version) - _nmcli_array_delete_at words 0 - ;; - help) - _nmcli_array_delete_at words 0 - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - HELP_ONLY_AS_FIRST=0 - return 0 - fi - HELP_ONLY_AS_FIRST=0 - ;; - temporary) - _nmcli_array_delete_at words 0 - ;; - mode) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "tabular multiline" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - colors) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "yes no auto" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - fields) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "all common - NAME UUID TYPE TIMESTAMP TIMESTAMP-REAL AUTOCONNECT READONLY DBUS-PATH ACTIVE DEVICE STATE ACTIVE-PATH - connection 802-3-ethernet 802-1x 802-11-wireless 802-11-wireless-security ipv4 ipv6 serial ppp pppoe gsm cdma bluetooth 802-11-olpc-mesh vpn wimax infiniband bond vlan adsl bridge bridge-port team team-port dcb tun ip-tunnel macvlan vxlan - GENERAL IP4 DHCP4 IP6 DHCP6 VPN - profile active" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - escape) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "no yes" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - wait) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - *) - # something unexpected. We are finished with parsing the OPTIONS. - return 2 - ;; - esac - done -} - -# after the OPTIONS, the OBJECT, the COMMAND and possible the COMMAND_CONNECTION, the syntax for nmcli -# expects several options with parameters. This function can parse them and remove them from the words array. -_nmcli_compl_ARGS() -{ - local aliases=${@} - local OPTIONS_ALL N_REMOVE_WORDS REMOVE_OPTIONS OPTIONS_HAS_MANDATORY i - OPTIONS_ALL=("${OPTIONS[@]}") - OPTIONS_UNKNOWN_OPTION= - - OPTIONS_HAS_MANDATORY=0 - if [[ "${#OPTIONS_MANDATORY[@]}" -ge 1 ]]; then - OPTIONS_HAS_MANDATORY=1 - fi - - for (( ; ; )); do - if [[ "${#words[@]}" -le 1 ]]; then - # we have no more words left... - if [[ ${#OPTIONS[@]} -eq 0 ]]; then - return 1; - fi - if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then - _nmcli_list "$(echo "${OPTIONS[@]}")" - return 0 - fi - COMMAND_ARGS_WAIT_OPTIONS=0 - return 1 - fi - if ! _nmcli_array_has_value OPTIONS_ALL "${words[0]}"; then - # This is an entirely unknown option. - OPTIONS_UNKNOWN_OPTION="?${words[0]}" - return 1 - fi - if [[ "$OPTIONS_HAS_MANDATORY" -eq 1 && "${#OPTIONS_MANDATORY[@]}" -eq 0 ]]; then - # we had some mandatory options, but they are all satisfied... stop here... - # This means, that we can continue with more additional options from the NEXT_GROUP. - return 1 - fi - - N_REMOVE_WORDS=2 - REMOVE_OPTIONS=("${words[0]}") - - # change option name to alias - WORD0="${words[0]}" - for alias in "${aliases[@]}" ; do - if [[ "${WORD0}" == ${alias%%:*} ]]; then - WORD0=${alias#*:} - break - fi - done - - case "${WORD0}" in - level) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "OFF ERR WARN INFO DEBUG TRACE KEEP" - return 0 - fi - ;; - domains) - if [[ "${#words[@]}" -eq 2 ]]; then - local OPTIONS_DOM=(ALL DEFAULT DHCP IP PLATFORM RFKILL ETHER WIFI BT MB DHCP4 DHCP6 PPP WIFI_SCAN IP4 IP6 AUTOIP4 DNS VPN SHARING SUPPLICANT AGENTS SETTINGS SUSPEND CORE DEVICE OLPC WIMAX INFINIBAND FIREWALL ADSL BOND VLAN BRIDGE DBUS_PROPS TEAM CONCHECK DCB DISPATCH AUDIT SYSTEMD VPN_PLUGIN) - if [[ "${words[1]}" != "" ]]; then - - # split the comma separaeted domain string into its parts LOGD - local oIFS="$IFS" - IFS="," - local LOGD=($(printf '%s' "${words[1]}" | sed 's/\(^\|,\)/,#/g')) - IFS="$oIFS" - unset oIFS - - local LOGDLAST LOGDLAST_IS_OPTION LOGDI i - # first we iterate over all present domains and remove them from OPTIONS_DOM - for LOGDI in ${LOGD[@]}; do - LOGDI="${LOGDI:1}" - LOGDLAST="$LOGDI" - LOGDLAST_IS_OPTION=0 - for i in ${!OPTIONS_DOM[*]}; do - if [[ "${OPTIONS_DOM[$i]}" = "$LOGDI" ]]; then - LOGDLAST_IS_OPTION=1 - unset OPTIONS_DOM[$i] - fi - done - done - - local OPTIONS_DOM2=() - if [[ "$LOGDLAST" = "" ]]; then - # we have a word that ends with ','. Just append all remaining options. - for i in ${!OPTIONS_DOM[*]}; do - OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}${OPTIONS_DOM[$i]}" - done - else - # if the last option is not "" we keep only those option with the same prefix - # as the last domain (LOGDLAST) - for i in ${!OPTIONS_DOM[*]}; do - if [[ "${OPTIONS_DOM[$i]:0:${#LOGDLAST}}" == "$LOGDLAST" ]]; then - # modify the option with the present prefix - OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}${OPTIONS_DOM[$i]:${#LOGDLAST}}" - fi - done - - if [[ $LOGDLAST_IS_OPTION -eq 1 ]]; then - # if the last logd itself was a valid iption, ${words[1]} is itself a valid match - OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]}" - - # also, add all remaining options by comma separated to the word. - for i in ${!OPTIONS_DOM[*]}; do - OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${words[1]},${OPTIONS_DOM[$i]}" - done - fi - if [[ ${#OPTIONS_DOM2[@]} -eq 1 ]]; then - for i in ${!OPTIONS_DOM[*]}; do - if [[ "$LOGDLAST" != "${OPTIONS_DOM[$i]:0:${#LOGDLAST}}" ]]; then - OPTIONS_DOM2[${#OPTIONS_DOM2[@]}]="${OPTIONS_DOM2[0]},${OPTIONS_DOM[$i]}" - fi - done - fi - - fi - OPTIONS_DOM=(${OPTIONS_DOM2[@]}) - fi - - _nmcli_list "$(echo "${OPTIONS_DOM[@]}")" - return 0 - fi - ;; - type) - if [[ "$OPTIONS_TYPE" != "" ]]; then - return 1 - fi - if [[ "${#words[@]}" -eq 2 ]]; then - if [[ "${words[1]:0:1}" = "8" ]]; then - # usually we don't want to show the 802-x types (because the shorter aliases are more - # user friendly. Only complete them, if the current word already starts with an "8". - _nmcli_list "802-3-ethernet 802-11-wireless 802-11-olpc-mesh" - else - _nmcli_list "ethernet wifi wimax gsm cdma infiniband bluetooth vpn olpc-mesh vlan bond bridge team pppoe adsl tun ip-tunnel macvlan vxlan" - fi - return 0 - fi - OPTIONS_TYPE="${words[1]}" - - if [[ "x$OPTIONS_MANDATORY_IFNAME" != x ]]; then - # the ifname is not a mandatory option for a few connection types... - # Check, if we have such a 'type' and remove the 'ifname' from the list - # of mandatory options. - case "$OPTIONS_TYPE" in - vl|vla|vlan| \ - bond| \ - team| \ - bridge) - for i in ${!OPTIONS_MANDATORY[*]}; do - if [[ "${OPTIONS_MANDATORY[$i]}" = "ifname" ]]; then - unset OPTIONS_MANDATORY[$i] - fi - done - ;; - *) - ;; - - esac - OPTIONS_MANDATORY_IFNAME= - fi - ;; - master) - if [[ "${#words[@]}" -eq 2 ]]; then - if [[ "${words[1]}" = "" ]]; then - _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" - else - _nmcli_list_nl "$(printf "%s\n%s\n%s" "$(_nmcli_dev_status DEVICE)" "$(_nmcli_con_show UUID)")" - fi - return 0 - fi - ;; - dev) - if [[ "${#words[@]}" -eq 2 ]]; then - if [[ "${words[1]}" = "" ]]; then - _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" - else - _nmcli_list_nl "$(printf "%s\n%s\n%s" "$(_nmcli_dev_status DEVICE)" "$(_nmcli_wifi_list BSSID)" "$(_nmcli_con_show UUID)")" - fi - return 0 - fi - ;; - primary| \ - ifname) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" - return 0 - fi - ;; - mode) - if [[ "${#words[@]}" -eq 2 ]]; then - case "$OPTIONS_TYPE" in - "wifi") - _nmcli_list "infrastructure ap adhoc" - ;; - "tun") - _nmcli_list "tun tap" - ;; - "ip-tunnel") - _nmcli_list "ipip gre sit isatap vti ip6ip6 ipip6 ip6gre vti6" - ;; - "macvlan") - _nmcli_list "vepa bridge private passthru source" - ;; - "bond"| \ - *) - _nmcli_list "balance-rr active-backup balance-xor broadcast 802.3ad balance-tlb balance-alb" - esac - return 0 - fi - ;; - transport-mode) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "datagram connected" - return 0 - fi - ;; - vpn-type) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "vpnc openvpn pptp openconnect openswan libreswan strongswan ssh l2tp iodine fortisslvpn" - return 0 - fi - ;; - slave-type) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "bond team bridge" - return 0 - fi - ;; - lacp-rate) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "slow fast" - return 0 - fi - ;; - bt-type) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "panu dun-gsm dun-cdma" - return 0 - fi - ;; - wep-key-type) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "key phrase" - return 0 - fi - ;; - managed| \ - autoconnect| \ - stp| \ - hairpin| \ - save| \ - hidden| \ - private| \ - pi| \ - vnet-hdr| \ - multi-queue|\ - tap) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "yes no" - return 0 - fi - ;; - config) - if [[ "${#words[@]}" -eq 2 ]]; then - compopt -o default - COMPREPLY=() - return 0 - fi - ;; - ip4| \ - ip6| \ - gw4| \ - gw6| \ - priority| \ - forward-delay| \ - hello-time| \ - max-age| \ - ageing-time| \ - nsp| \ - path-cost| \ - name| \ - mtu| \ - cloned-mac| \ - addr| \ - parent| \ - miimon| \ - arp-interval| \ - arp-ip-target| \ - downdelay| \ - updelay| \ - p-key| \ - mac| \ - id| \ - flags| \ - ingress| \ - dhcp-anycast| \ - channel| \ - egress| \ - apn| \ - con-name| \ - user| \ - username| \ - service| \ - password) - if [[ "${#words[@]}" -eq 2 ]]; then - return 0 - fi - ;; - passwd-file| \ - file) - if [[ "${#words[@]}" -eq 2 ]]; then - compopt -o default - COMPREPLY=() - return 0 - fi - ;; - ssid) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list_nl "$(_nmcli_wifi_list SSID)" - return 0 - fi - ;; - ap| \ - bssid) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list_nl "$(_nmcli_wifi_list BSSID)" - return 0 - fi - ;; - encapsulation) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "vcmux llc" - return 0 - fi - ;; - protocol) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "pppoa pppoe ipoatm" - return 0 - fi - ;; - band) - if [[ "${#words[@]}" -eq 2 ]]; then - _nmcli_list "a bg" - return 0 - fi - ;; - *) - return 1 - ;; - esac - - - if [[ "${#OPTIONS_NEXT_GROUP[@]}" -gt 0 ]]; then - if _nmcli_array_has_value OPTIONS_NEXT_GROUP "${words[0]}"; then - # the current value is from the next group... - # We back off, because the current group is complete. - return 1 - fi - fi - - _nmcli_array_delete_at words 0 $((N_REMOVE_WORDS-1)) - # remove the options already seen. - for i in ${!OPTIONS[*]}; do - if [[ "${OPTIONS[$i]}" = "${REMOVE_OPTIONS[0]}" || "${OPTIONS[$i]}" = "${REMOVE_OPTIONS[1]}" ]]; then - if ! _nmcli_array_has_value OPTIONS_REPEATABLE "${OPTIONS[$i]}" ; then - unset OPTIONS[$i] - fi - fi - done - for i in ${!OPTIONS_MANDATORY[*]}; do - if [[ "${OPTIONS_MANDATORY[$i]}" = "${REMOVE_OPTIONS[0]}" || "${OPTIONS_MANDATORY[$i]}" = "${REMOVE_OPTIONS[1]}" ]]; then - unset OPTIONS_MANDATORY[$i] - fi - done - done -} - -# some commands expect a connection as parameter. This connection can either be given -# as id|uuid|path|apath. Parse that connection parameter. -# Actually, it can also ask for a device name, like `nmcli device set [ifname] ` -_nmcli_compl_ARGS_CONNECTION() -{ - if ! _nmcli_array_has_value OPTIONS "${words[0]}"; then - COMMAND_CONNECTION_TYPE= - COMMAND_CONNECTION_ID="${words[0]}" - _nmcli_array_delete_at words 0 - return 1 - fi - COMMAND_CONNECTION_TYPE="${words[0]}" - COMMAND_CONNECTION_ID="${words[1]}" - local CON_TYPE= - if [[ "x$COMMAND_CONNECTION_ACTIVE" != x ]]; then - CON_TYPE=--active - fi - case "${words[0]}" in - id) - if [[ ${#words[@]} -le 2 ]]; then - _nmcli_list_nl "$(_nmcli_con_show NAME $CON_TYPE)" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - uuid) - if [[ ${#words[@]} -le 2 ]]; then - _nmcli_list_nl "$(_nmcli_con_show UUID $CON_TYPE)" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - path) - if [[ ${#words[@]} -le 2 ]]; then - _nmcli_list_nl "$(_nmcli_con_show DBUS-PATH $CON_TYPE)" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - apath) - if [[ ${#words[@]} -le 2 ]]; then - _nmcli_list_nl "$(_nmcli_con_show ACTIVE-PATH --active)" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - ifname) - if [[ ${#words[@]} -le 2 ]]; then - _nmcli_list_nl "$(_nmcli_dev_status DEVICE)" - return 0 - fi - _nmcli_array_delete_at words 0 1 - ;; - *) - COMMAND_CONNECTION_TYPE= - COMMAND_CONNECTION_ID="${words[0]}" - _nmcli_array_delete_at words 0 - ;; - esac - return 1 -} - -_nmcli_compl_COMMAND() { - local command="$1" - shift - local V=("$@") - local H= - if [[ "${command[0]:0:1}" != '-' ]]; then - H=help - elif [[ "${command[0]:1:1}" == '-' || "${command[0]}" == "-" ]]; then - H=--help - else - H=-help - fi - if [[ "x$COMPL_COMMAND_NO_HELP" == x ]]; then - V=("${V[@]}" "$H") - fi - _nmcli_list "${V[*]}" -} - -_nmcli_compl_COMMAND_nl() { - local command="$1" - local a="$2" - shift - shift - local V=("$@") - local H= - if [[ "${command[0]:0:1}" != '-' ]]; then - V=("${V[@]/#/--}") - H=help - elif [[ "${command[0]:1:1}" == '-' || "${command[0]}" == "-" ]]; then - V=("${V[@]/#/--}") - H=--help - else - V=("${V[@]/#/-}") - H=-help - fi - if [[ "x$COMPL_COMMAND_NO_HELP" == x ]]; then - V=("${V[@]}" "$H") - fi - local IFS=$'\n' - V="${V[*]}" - _nmcli_list_nl "$(printf "%s%s\n%s" "" "$V" "$a")" -} - -_nmcli_compl_PROPERTIES() -{ - while [[ ${#words[@]} -gt 0 ]]; do - if [[ ${#words[@]} -eq 1 ]]; then - _nmcli_list_nl "$(nmcli --complete-args connection modify "$@" "${words[@]}" 2>/dev/null)" - return 0 - fi - _nmcli_array_delete_at words 0 1 - done - return 0 -} - -_nmcli() -{ - local cur prev words cword i - _init_completion || return - - # we don't care about any arguments after the current cursor position - # because we only parse from left to right. So, if there are some arguments - # right of the cursor, just ignore them. Also don't care about ${words[0]}. - _nmcli_array_delete_at words $((cword+1)) ${#words[@]} - _nmcli_array_delete_at words 0 - - # _init_completion returns the words with all the quotes and escaping - # characters. We don't care about them, drop them at first. - for i in ${!words[@]}; do - words[i]="$(printf '%s' "${words[i]}" | xargs printf '%s\n' 2>/dev/null || true)" - done - - # In case the cursor is not at the end of the line, - # $cur consists of spaces that we want do remove. - # For example: `nmcli connection modify id lo` - if [[ "$cur" =~ ^[[:space:]]+ ]]; then - cur='' - fi - - local OPTIONS OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS_IP OPTIONS_NEXT_GROUP \ - OPTIONS_SEP OPTIONS_REPEATABLE OPTIONS_MANDATORY OPTIONS_MANDATORY_IFNAME \ - COMMAND_ARGS_WAIT_OPTIONS COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID \ - COMMAND_CONNECTION_ACTIVE="" HELP_ONLY_AS_FIRST="" \ - LONG_OPTIONS=(terse pretty mode fields colors escape ask show-secrets wait version help) - - _nmcli_compl_OPTIONS - i=$? - - case $i in - 0) - # We have just completed an option or got an --help: terminate. - return 0 - ;; - 1) - # we show for completion either the (remaining) OPTIONS - # (if the current word starts with a dash) or the OBJECT list - # otherwise. - if [[ "${words[0]:0:1}" != '-' ]]; then - OPTIONS=(help general networking radio connection device agent monitor) - elif [[ "${words[0]:1:1}" == '-' || "${words[0]}" == "-" ]]; then - OPTIONS=("${LONG_OPTIONS[@]/#/--}") - else - OPTIONS=("${LONG_OPTIONS[@]/#/-}") - fi - _nmcli_list "${OPTIONS[*]}" - return 0 - ;; - esac - - local command="${words[1]}" - case "${words[0]}" in - h|he|hel|help) - ;; - g|ge|gen|gene|gener|genera|general) - if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" status permissions logging hostname - elif [[ ${#words[@]} -gt 2 ]]; then - case "$command" in - ho|hos|host|hostn|hostna|hostnam|hostname) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" \ - "$(printf '%s\n%s\n%s\n' \ - "$(nmcli general hostname 2>/dev/null)" \ - "$(cat /etc/hostname 2>/dev/null)" \ - "$(hostnamectl status 2>/dev/null | sed -n '1s/^.\+hostname: \(.\+\)$/\1/p')" \ - "$HOSTNAME")" - fi - ;; - l|lo|log|logg|loggi|loggin|logging) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" level domains - else - _nmcli_array_delete_at words 0 1 - OPTIONS=(level domains) - _nmcli_compl_ARGS - fi - ;; - s|st|sta|stat|statu|status| \ - p|pe|per|perm|permi|permis|permiss|permissi|permissio|permission|permissions) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" - fi - ;; - esac - fi - ;; - n|ne|net|netw|netwo|networ|network|networki|networkin|networking) - if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" on off connectivity - elif [[ ${#words[@]} -eq 3 ]]; then - case "$command" in - c|co|con|conn|conne|connec|connect|connecti|connectiv|connectivi|connectivit|connectivity) - _nmcli_compl_COMMAND "${words[2]}" "check" - ;; - esac - fi - ;; - r|ra|rad|radi|radio) - if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" all wifi wwan - elif [[ ${#words[@]} -eq 3 ]]; then - case "$command" in - a|al|all | w|wi|wif|wifi | ww|wwa|wwan) - _nmcli_compl_COMMAND "${words[2]}" "on off" - ;; - esac - fi - ;; - c|co|con|conn|conne|connec|connect|connecti|connectio|connection) - if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" show up down add modify clone edit delete monitor reload load import export - elif [[ ${#words[@]} -gt 2 ]]; then - case "$command" in - s|sh|sho|show) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" active order - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help active order) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - i=$? - - if ! _nmcli_array_has_value LONG_OPTIONS active; then - COMMAND_CONNECTION_ACTIVE=1 - fi - - case $i in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - if [[ "x$COMMAND_CONNECTION_ACTIVE" = x ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" - - else - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" "${LONG_OPTIONS[@]}" - fi - fi - return 0 - ;; - esac - - OPTIONS=(id uuid path apath) - while [[ ${#words[@]} -gt 0 ]]; do - _nmcli_compl_ARGS_CONNECTION && return 0 - done - if [[ "x$COMMAND_CONNECTION_ACTIVE" = x ]]; then - _nmcli_list_nl "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME)")" - else - _nmcli_list_nl "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" - fi - fi - ;; - u|up) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "ifname\nid\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - - case $? in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "ifname\nid\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" - fi - return 0 - ;; - esac - - local COMMAND_CONNECTION_TYPE='' - OPTIONS=(ifname id uuid path) - _nmcli_compl_ARGS_CONNECTION && return 0 - - if [[ "$COMMAND_CONNECTION_TYPE" = "ifname" ]]; then - OPTIONS=(ap nsp passwd-file) - else - OPTIONS=(ifname ap nsp passwd-file) - fi - _nmcli_compl_ARGS - fi - ;; - d|do|dow|down) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - case $? in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\napath\n%s" "$(_nmcli_con_show NAME --active)")" "${LONG_OPTIONS[@]}" - fi - return 0 - ;; - esac - - OPTIONS=(id uuid path apath) - COMMAND_CONNECTION_ACTIVE=1 - _nmcli_compl_ARGS_CONNECTION && return 0 - fi - ;; - a|ad|add) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(nmcli --complete-args connection add "" 2>/dev/null)" - else - _nmcli_array_delete_at words 0 1 - _nmcli_list_nl "$(nmcli --complete-args connection add "${words[@]}" 2>/dev/null)" - fi - ;; - e|ed|edi|edit) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\ntype\ncon-name\n%s" "$(_nmcli_con_show NAME)")" - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - - case $? in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\ntype\ncon-name\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" - fi - return 0 - ;; - esac - - if [[ "${words[0]}" = 'type' || "${words[0]}" = 'con-name' ]]; then - OPTIONS=(type con-name) - _nmcli_compl_ARGS - else - OPTIONS=(id uuid path apath) - _nmcli_compl_ARGS_CONNECTION - fi - fi - ;; - m|mo|mod|modi|modif|modify) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" temporary - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help temporary) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - case $? in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" - fi - return 0 - ;; - esac - - OPTIONS=(id uuid path) - _nmcli_compl_ARGS_CONNECTION && return 0 - - _nmcli_compl_PROPERTIES ${COMMAND_CONNECTION_TYPE} "$COMMAND_CONNECTION_ID" - - return 0 - fi - ;; - c|cl|clo|clon|clone) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" temporary - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help temporary) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - case $? in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" - fi - return 0 - ;; - esac - - OPTIONS=(id uuid path) - _nmcli_compl_ARGS_CONNECTION && return 0 - - return 0 - fi - ;; - - de|del|dele|delet|delete| \ - mon|moni|monit|monito|monitor) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help) - _nmcli_compl_OPTIONS - case $? in - 0) - return 0 - ;; - 1) - if ! _nmcli_array_has_value LONG_OPTIONS "help"; then - return 0 - fi - ;; - esac - - OPTIONS=(id uuid path apath) - while [[ ${#words[@]} -gt 0 ]]; do - _nmcli_compl_ARGS_CONNECTION && return 0 - done - _nmcli_list_nl "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" - fi - ;; - l|lo|loa|load) - if [[ ${#words[@]} -gt 2 ]]; then - # we should also complete for help/--help, but who to mix that - # with file name completion? - compopt -o default - COMPREPLY=() - fi - ;; - i|im|imp|impo|impor|import) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" type file --temporary - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help temporary) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - case $? in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND "${words[2]}" type file - fi - return 0 - ;; - esac - - OPTIONS=(type file) - OPTIONS_MANDATORY=(type file) - _nmcli_compl_ARGS type:vpn-type - return 0 - fi - ;; - e|ex|exp|expo|expor|export) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" - elif [[ ${#words[@]} -gt 3 ]]; then - _nmcli_array_delete_at words 0 1 - - LONG_OPTIONS=(help) - HELP_ONLY_AS_FIRST=1 - _nmcli_compl_OPTIONS - case $? in - 0) - return 0 - ;; - 1) - if [[ "$HELP_ONLY_AS_FIRST" == 1 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_show NAME)")" "${LONG_OPTIONS[@]}" - fi - return 0 - ;; - esac - - OPTIONS=(id uuid path) - _nmcli_compl_ARGS_CONNECTION && return 0 - return 0 - fi - ;; - - esac - fi - ;; - d|de|dev|devi|devic|device) - if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" status show connect reapply modify disconnect delete monitor wifi set lldp - elif [[ ${#words[@]} -gt 2 ]]; then - case "$command" in - s|st|sta|stat|statu|status) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" - fi - ;; - sh|sho|show| \ - r|re|rea|reap|reapp|reappl|reapply| \ - c|co|con|conn|conne|connec|connect) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" - fi - ;; - mod|modi|modif|modify) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(nmcli --complete-args device modify "" 2>/dev/null)" - else - _nmcli_array_delete_at words 0 1 - _nmcli_list_nl "$(nmcli --complete-args device modify "${words[@]}" 2>/dev/null)" - fi - ;; - d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect| \ - de|del|dele|delet|delete| \ - m|mo|mon|moni|monit|monito|monitor) - if [[ ${#words[@]} -ge 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" - fi - ;; - se|set) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND_nl "${words[2]}" "$(printf "ifname\n%s" "$(_nmcli_dev_status DEVICE)")" - else - _nmcli_array_delete_at words 0 1 - OPTIONS=(ifname) - _nmcli_compl_ARGS_CONNECTION && return 0 - OPTIONS=(autoconnect managed) - _nmcli_compl_ARGS - fi - ;; - w|wi|wif|wifi) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" list connect hotspot rescan - else - case "${words[2]}" in - l|li|lis|list) - _nmcli_array_delete_at words 0 2 - OPTIONS=(ifname bssid) - _nmcli_compl_ARGS - ;; - c|co|con|conn|conne|connec|connect) - if [[ ${#words[@]} -eq 4 ]]; then - if [[ "${words[3]}" = "" ]]; then - _nmcli_list_nl "$(_nmcli_wifi_list SSID)" - else - _nmcli_list_nl "$(printf "%s\n%s" "$(_nmcli_wifi_list SSID)" "$(_nmcli_wifi_list BSSID)")" - fi - else - _nmcli_array_delete_at words 0 3 - local OPTIONS=(password wep-key-type ifname bssid name private hidden) - _nmcli_compl_ARGS - fi - ;; - h|ho|hot|hots|hotsp|hotspo|hotspot) - _nmcli_array_delete_at words 0 2 - OPTIONS=(ifname con-name ssid band channel password) - _nmcli_compl_ARGS - ;; - r|re|res|resc|resca|rescan) - _nmcli_array_delete_at words 0 2 - OPTIONS_REPEATABLE=(ssid) - OPTIONS=(ifname ssid) - _nmcli_compl_ARGS - ;; - esac - fi - ;; - l|ll|lld|lldp) - if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" list - else - case "${words[2]}" in - l|li|lis|list) - _nmcli_array_delete_at words 0 2 - OPTIONS=(ifname) - _nmcli_compl_ARGS - ;; - esac - fi - ;; - esac - fi - ;; - a|ag|age|agen|agent) - if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" secret polkit all - fi - ;; - m|mo|mon|moni|monit|monito|monitor) - ;; - esac - - return 0 } && complete -F _nmcli nmcli