# nmcli(1) completion -*- shell-script -*- # Originally based on # https://github.com/GArik/bash-completion/blob/master/completions/nmcli _nmcli_list() { COMPREPLY=( $( compgen -W '$1' -- $cur ) ) } _nmcli_list_nl() { local IFS=$'\n' COMPREPLY=( $( compgen -W '$1' -- $cur ) ) # Now escape special characters (spaces, single and double quotes), # so that the argument is really regarded a single argument by bash. # See http://stackoverflow.com/questions/1146098/properly-handling-spaces-and-quotes-in-bash-completion local escaped_single_quote="'\''" local i=0 local entry for entry in ${COMPREPLY[*]} do if [[ "${cur:0:1}" == "'" ]]; then # started with single quote, escaping only other single quotes # [']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 and all backslashes # ["]bla'bla"bla\bla bla --> ["]bla'bla\"bla\\bla bla entry="${entry//\\/\\\\}" entry="${entry//\"/\\\"}" COMPREPLY[$i]="$entry" else # no quotes in front, escaping _everything_ # [ ]bla'bla"bla\bla bla --> [ ]bla\'bla\"bla\\bla\ bla entry="${entry//\\/\\\\}" entry="${entry//\'/\'}" entry="${entry//\"/\\\"}" entry="${entry// /\\ }" COMPREPLY[$i]="$entry" fi (( i++ )) done # Work-around bash_completion issue where bash interprets a colon # as a separator. # Colon is escaped here. Change "\\:" back to ":". # See also: # http://stackoverflow.com/questions/28479216/how-to-give-correct-suggestions-to-tab-complete-when-my-words-contains-colons # http://stackoverflow.com/questions/2805412/bash-completion-for-maven-escapes-colon/12495727 i=0 for entry in ${COMPREPLY[*]} do entry="${entry//\\\\:/:}" 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}" 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" return 0 fi ;; domains) if [[ "${#words[@]}" -eq 2 ]]; then local OPTIONS_DOM=(ALL DEFAULT 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) 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[@]} -le 1 ]]; then local PREFIX="" if [[ "${words[0]:0:1}" == [+-] ]]; then PREFIX="${words[0]:0:1}" fi _nmcli_list_nl "$(echo -e 'print\nquit\nyes' |nmcli c edit "$@" 2>/dev/null |awk -F: '/\..*:/ {print "'$PREFIX'"$1}')" return 0 elif [[ ${#words[@]} -le 2 ]]; then return 0 fi _nmcli_array_delete_at words 0 1 done _nmcli_list_nl "$(echo -e 'print\nquit\nyes' |nmcli c edit "$@" 2>/dev/null |awk -F: '/\..*:/ {print $1}')" } _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_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS OPTIONS_MANDATORY COMMAND_ARGS_WAIT_OPTIONS OPTIONS_IP OPTIONS_MANDATORY OPTIONS_NEXT_GROUP OPTIONS_SEP OPTIONS_REPEATABLE local COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID OPTIONS_MANDATORY_IFNAME HELP_ONLY_AS_FIRST local COMMAND_CONNECTION_ACTIVE="" HELP_ONLY_AS_FIRST= local LONG_OPTIONS=(terse pretty mode fields colors escape ask show-secrets wait version help) _nmcli_compl_OPTIONS i=$? if [[ "$HELP_ONLY_AS_FIRST" == '0' ]]; then # got a --help. No more completion. return 0 fi case $i in 0) 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 "${words[2]}" type ifname con-name autoconnect master slave-type 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 "${words[2]}" type ifname con-name autoconnect master slave-type fi return 0 ;; esac OPTIONS_TYPE= OPTIONS=(type ifname con-name autoconnect save master slave-type) OPTIONS_MANDATORY=(type ifname) COMMAND_ARGS_WAIT_OPTIONS=1 OPTIONS_MANDATORY_IFNAME=1 _nmcli_compl_ARGS && return 0 OPTIONS_MANDATORY_IFNAME= if _nmcli_array_has_value OPTIONS "${OPTIONS_MANDATORY[@]}"; then # we still have some missing mandatory options... if [[ "$OPTIONS_UNKNOWN_OPTION" != '' ]]; then if ! _nmcli_array_has_value OPTIONS "${OPTIONS_UNKNOWN_OPTION:1}"; then # if we encountered an unknown option while having mandatory # options, just return. return 0 fi fi _nmcli_list "$(echo "${OPTIONS[@]}")" return 0 fi OPTIONS_IP=(ip4 ip6 gw4 gw6) OPTIONS_SEP=(--) OPTIONS_MANDATORY=() case "$OPTIONS_TYPE" in 802-3|802-3-|802-3-e|802-3-et|802-3-eth|802-3-ethe|802-3-ether|802-3-ethern|802-3-etherne|802-3-ethernet| \ e|et|eth|ethe|ether|ethern|etherne|ethernet) OPTIONS_TYPE=ethernet OPTIONS_TYPED=(mac cloned-mac mtu) ;; 802-11-w|802-11-wi|802-11-wir|802-11-wire|802-11-wirel|802-11-wirele|802-11-wireles|802-11-wireless| \ wif|wifi) OPTIONS_TYPE=wifi OPTIONS_TYPED=(ssid mac cloned-mac mtu mode) OPTIONS_MANDATORY=(ssid) ;; wim|wima|wimax) OPTIONS_TYPE=wimax OPTIONS_TYPED=(mac nsp) ;; g|gs|gsm) OPTIONS_TYPE=gsm OPTIONS_TYPED=(apn user password) OPTIONS_MANDATORY=(apn) ;; c|cd|cdm|cdma) OPTIONS_TYPE=cdma OPTIONS_TYPED=(user password) ;; i|in|inf|infi|infin|infini|infinib|infiniba|infiniban|infiniband) OPTIONS_TYPE=infiniband OPTIONS_TYPED=(mac mtu transport-mode parent p-key) ;; bl|blu|blue|bluet|blueto|bluetoo|bluetoot|bluetooth) OPTIONS_TYPE=bluetooth OPTIONS_TYPED=(addr bt-type) ;; vl|vla|vlan) OPTIONS_TYPE=vlan OPTIONS_TYPED=(dev id flags ingress egress mtu) OPTIONS_MANDATORY=(dev) ;; bond) OPTIONS_TYPE=bond OPTIONS_TYPED=(mode miimon downdelay updelay arp-interval arp-ip-target primary lacp-rate) ;; team) OPTIONS_TYPE=team OPTIONS_TYPED=(config) ;; bridge) OPTIONS_TYPE=bridge OPTIONS_TYPED=(stp priority forward-delay hello-time max-age ageing-time mac) ;; vp|vpn) OPTIONS_TYPE=vpn OPTIONS_TYPED=(vpn-type user) OPTIONS_MANDATORY=(vpn-type) ;; 802-11-o|802-11-ol|802-11-olp|802-11-olpc|802-11-olpc-|802-11-olpc-m|802-11-olpc-me|802-11-olpc-mes|802-11-olpc-mesh| \ o|ol|olp|olpc|olpc-|olpc-m|olpc-me|olpc-mes|olpc-mesh) OPTIONS_TYPE=olpc-mesh OPTIONS_TYPED=(ssid channel dhcp-anycast) OPTIONS_MANDATORY=(ssid) ;; p|pp|ppp|pppo|pppoe) OPTIONS_TYPE=pppoe OPTIONS_TYPED=(username password service mtu mac) OPTIONS_MANDATORY=(username) ;; a|ad|ads|adsl) OPTIONS_TYPE=adsl OPTIONS_TYPED=(username password protocol encapsulation) OPTIONS_MANDATORY=(username protocol) ;; tu|tun) OPTIONS_TYPE=tun OPTIONS_TYPED=(mode owner group pi vnet-hdr multi-queue) OPTIONS_MANDATORY=(mode) ;; ip|ip-|ip-t|ip-tu|ip-tun|ip-tunn|ip-tunne|ip-tunnel) OPTIONS_TYPE=ip-tunnel OPTIONS_TYPED=(mode remote local dev) OPTIONS_MANDATORY=(mode remote) ;; m|ma|mac|macv|macvl|macvla|macvlan) OPTIONS_TYPE=macvlan OPTIONS_TYPED=(dev mode tap) OPTIONS_MANDATORY=(dev mode) ;; vx|vxl|vxla|vxlan) OPTIONS_TYPE=vxlan OPTIONS_TYPED=(id remote local dev source-port-min source-port-max destination-port) OPTIONS_MANDATORY=(id remote) ;; *) # for an unknown connection type, we stop completion here return 0 ;; esac if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then # means, we are at the end of options. Nothing more to parse, just show # what are the options now. if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_TYPED[@]}")" else _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_TYPED[@]}") $(echo "${OPTIONS_IP[@]}") $(echo "${OPTIONS_SEP[@]}")" fi return 0 fi if [[ "${#OPTIONS[@]}" -gt 0 ]]; then # we still have some options from before, but no mandatory ones. Mix them with OPTIONS_TYPED # and continue parsing the options... OPTIONS=("${OPTIONS[@]}" "${OPTIONS_TYPED[@]}") OPTIONS_NEXT_GROUP=("${OPTIONS_TYPED[@]}") _nmcli_compl_ARGS && return 0 if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then # means, we are at the end of options. Nothing more to parse, just show # what are the options now. if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then _nmcli_list "$(echo "${OPTIONS[@]}")" else _nmcli_list "$(echo "${OPTIONS[@]}") $(echo "${OPTIONS_IP[@]}") $(echo "${OPTIONS_SEP[@]}")" fi return 0 fi if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then # there was an unknown option specified. Maybe we have to stop with the completion. if [[ "${#OPTIONS_MANDATORY[@]}" -gt 0 ]]; then # we have an unknown option, but still mandatory ones that must be fullfiled first. return 0 fi if ! (_nmcli_array_has_value OPTIONS_IP "${OPTIONS_UNKNOWN_OPTION:1}" || _nmcli_array_has_value OPTIONS_SEP "${OPTIONS_UNKNOWN_OPTION:1}"); then # the unknown option is neither an IP option nor a separator. return 0 fi # The unknown option is an IP option or a separator, which is fine... continue... fi fi OPTIONS=("${OPTIONS_TYPED[@]}") OPTIONS_NEXT_GROUP=() if [[ "${#OPTIONS_MANDATORY[@]}" -ge 1 ]]; then # we have some mandatory options... don't check for IP options yet... _nmcli_compl_ARGS && return 0 if _nmcli_array_has_value OPTIONS "${OPTIONS_MANDATORY[@]}"; then _nmcli_list "$(echo "${OPTIONS[@]}")" return 0 fi if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then if ! (_nmcli_array_has_value OPTIONS_IP "${OPTIONS_UNKNOWN_OPTION:1}" || _nmcli_array_has_value OPTIONS_SEP "${OPTIONS_UNKNOWN_OPTION:1}"); then # the unknown option is neither an IP option nor a separator. return 0 fi fi fi # no mandatory options... do final completion including IP options OPTIONS=("${OPTIONS[@]}" "${OPTIONS_IP[@]}" "${OPTIONS_SEP[@]}") OPTIONS_NEXT_GROUP=("${OPTIONS_IP[@]}" "${OPTIONS_SEP[@]}") _nmcli_compl_ARGS && return 0 if [[ "$OPTIONS_UNKNOWN_OPTION" != "" ]]; then return 0 fi if [[ "$COMMAND_ARGS_WAIT_OPTIONS" -ne 1 ]]; then # means, we are at the end of options. Nothing more to parse, just show # what are the options now. _nmcli_list "$(echo "${OPTIONS[@]}")" return 0 fi # process the last group of options, as the OPTIONS_TYPED are already handled... OPTIONS=("${OPTIONS_IP[@]}" "${OPTIONS_SEP[@]}") OPTIONS_NEXT_GROUP=("${OPTIONS_SEP[@]}") COMMAND_ARGS_WAIT_OPTIONS=0 _nmcli_compl_ARGS && return 0 _nmcli_array_delete_at words 0 _nmcli_compl_PROPERTIES type "$OPTIONS_TYPE" return 0 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 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 ;; 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 # ex: ts=4 sw=4 et filetype=sh