diff options
Diffstat (limited to 'clients/cli/nmcli-completion')
-rw-r--r-- | clients/cli/nmcli-completion | 1237 |
1 files changed, 1237 insertions, 0 deletions
diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion new file mode 100644 index 0000000000..e6dc5b5d2d --- /dev/null +++ b/clients/cli/nmcli-completion @@ -0,0 +1,1237 @@ +# 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 +} + +_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 + ;; + nocheck) + _nmcli_array_delete_at words 0 + ;; + ask) + _nmcli_array_delete_at words 0 + ;; + 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 + ;; + 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 + 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 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]}") + case "${words[0]}" in + level) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "ERR WARN INFO DEBUG" + 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 bond-slave bridge bridge-slave team team-slave pppoe" + 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 + _nmcli_list "balance-rr active-backup balance-xor broadcast 802.3ad balance-tlb balance-alb" + 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 ssh l2tp iodine" + 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 + ;; + autoconnect| \ + stp| \ + hairpin| \ + save| \ + private) + 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 + ;; + 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 + ;; + *) + 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 + unset OPTIONS[$i] + 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. +_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() +{ + 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 <TAB> 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 + 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 escape nocheck ask 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) + 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 + ;; + 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 wimax + elif [[ ${#words[@]} -eq 3 ]]; then + case "$command" in + a|al|all | w|wi|wif|wifi | ww|wwa|wwan | wim|wima|wimax) + _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 edit delete reload load + 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 + elif [[ ${#words[@]} -gt 3 ]]; then + _nmcli_array_delete_at words 0 1 + + LONG_OPTIONS=(help active) + 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) + else + OPTIONS=(ifname ap nsp) + 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 + 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 + fi + return 0 + ;; + esac + + OPTIONS_TYPE= + OPTIONS=(type ifname con-name autoconnect save) + 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_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_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_TYPED=(ssid mac cloned-mac mtu) + OPTIONS_MANDATORY=(ssid) + ;; + wim|wima|wimax) + OPTIONS_TYPED=(mac nsp) + ;; + g|gs|gsm) + OPTIONS_TYPED=(apn user password) + OPTIONS_MANDATORY=(apn) + ;; + c|cd|cdm|cdma) + OPTIONS_TYPED=(user password) + ;; + i|in|inf|infi|infin|infini|infinib|infiniba|infiniban|infiniband) + OPTIONS_TYPED=(mac mtu transport-mode parent p-key) + ;; + bl|blu|blue|bluet|blueto|bluetoo|bluetoot|bluetooth) + OPTIONS_TYPED=(addr bt-type) + ;; + vl|vla|vlan) + OPTIONS_TYPED=(dev id flags ingress egress mtu) + OPTIONS_MANDATORY=(dev) + ;; + bond) + OPTIONS_TYPED=(mode miimon downdelay updelay arp-interval arp-ip-target primary) + ;; + bond-|bond-s|bond-sl|bond-sla|bond-slav|bond-slave) + OPTIONS_TYPED=(master) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + team) + OPTIONS_TYPED=(config) + ;; + team-|team-s|team-sl|team-sla|team-slav|team-slave) + OPTIONS_TYPED=(master config) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + bridge) + OPTIONS_TYPED=(stp priority forward-delay hello-time max-age ageing-time mac) + ;; + bridge-|bridge-s|bridge-sl|bridge-sla|bridge-slav|bridge-slave) + OPTIONS_TYPED=(master priority path-cost hairpin) + OPTIONS_MANDATORY=(master) + OPTIONS_IP=() + ;; + vp|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_TYPED=(ssid channel dhcp-anycast) + OPTIONS_MANDATORY=(ssid) + ;; + p|pp|ppp|pppo|pppoe) + OPTIONS_TYPED=(username password service mtu mac) + OPTIONS_MANDATORY=(username) + ;; + *) + # 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[@]}")" + 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[@]}")" + 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}"; then + # the unknown option is NOT an IP option. + return 0 + fi + # The unknown option is an IP option, 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}"; then + # the unknown option is NOT an IP option. + return 0 + fi + fi + fi + + + # no mandatory options... do final completion including IP options + OPTIONS=("${OPTIONS[@]}" "${OPTIONS_IP[@]}") + OPTIONS_NEXT_GROUP=("${OPTIONS_IP[@]}") + _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_NEXT_GROUP=() + COMMAND_ARGS_WAIT_OPTIONS=0 + _nmcli_compl_ARGS && 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 apath) + _nmcli_compl_ARGS_CONNECTION && return 0 + while [[ "${#words[@]}" -gt 0 ]]; do + if [[ ${#words[@]} -le 1 ]]; then + _nmcli_list_nl "$(nmcli --fields profile connection show "${COMMAND_CONNECTION_TYPE:-id}" "$COMMAND_CONNECTION_ID" 2>/dev/null | sed -n 's/^\([^:]\+\):.*/\1/p')" + return 0 + elif [[ ${#words[@]} -le 2 ]]; then + return 0 + fi + _nmcli_array_delete_at words 0 1 + done + _nmcli_list_nl "$(nmcli --fields profile connection show "${COMMAND_CONNECTION_TYPE:-id}" "$COMMAND_CONNECTION_ID" 2>/dev/null | sed -n 's/^\([^:]\+\):.*/\1/p')" + return 0 + fi + ;; + de|del|dele|delet|delete) + 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 + ;; + esac + fi + ;; + d|de|dev|devi|devic|device) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_compl_COMMAND "$command" status show connect disconnect delete wifi wimax + 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| \ + c|co|con|conn|conne|connec|connect| \ + d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect| \ + de|del|dele|delet|delete) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" + fi + ;; + w|wi|wif|wifi) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND "${words[2]}" list connect 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) + _nmcli_compl_ARGS + fi + ;; + r|re|res|resc|resca|rescan) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname) + _nmcli_compl_ARGS + ;; + esac + fi + ;; + wim|wima|wimax) + 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 nsp) + _nmcli_compl_ARGS + ;; + esac + fi + ;; + + esac + fi + ;; + esac + + return 0 +} && +complete -F _nmcli nmcli + +# ex: ts=4 sw=4 et filetype=sh |