# 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, backslashes and ! # ["]bla'bla"bla\bla bla --> ["]bla'bla\"bla\\bla bla entry="${entry//\\/\\\\}" 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// /\\ }" 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}" 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 # ex: ts=4 sw=4 et filetype=sh