about summary refs log tree commit diff
path: root/cli/completion/nmcli
diff options
context:
space:
mode:
Diffstat (limited to 'cli/completion/nmcli')
-rw-r--r--cli/completion/nmcli1307
1 files changed, 1173 insertions, 134 deletions
diff --git a/cli/completion/nmcli b/cli/completion/nmcli
index 7bd9431a..343c7322 100644
--- a/cli/completion/nmcli
+++ b/cli/completion/nmcli
@@ -1,5 +1,5 @@
-# nmcli completion                                           -*- shell-script -*-
-# Based on
+# nmcli(1) completion                                      -*- shell-script -*-
+# Originally based on
 # https://github.com/GArik/bash-completion/blob/master/completions/nmcli
 
 _nmcli_list()
@@ -7,188 +7,1227 @@ _nmcli_list()
     COMPREPLY=( $( compgen -W '$1' -- $cur ) )
 }
 
-_nmcli_con_id()
+_nmcli_list_nl()
 {
     local IFS=$'\n'
-    COMPREPLY=( $( compgen -W "$(nmcli -t -f NAME con list 2>/dev/null )" -- $cur ) )
+    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_uuid()
+_nmcli_con_show()
 {
-    COMPREPLY=( $( compgen -W "$(nmcli -t -f UUID con list 2>/dev/null )" -- $cur ) )
+    nmcli -t -f "$1" connection show $2 2> /dev/null
 }
 
-_nmcli_ap_ssid()
+_nmcli_wifi_list()
 {
-    local IFS=$'\n'
-    COMPREPLY=( $( compgen -W "$(nmcli -t -f SSID dev wifi list 2>/dev/null )" -- $cur ) )
+    nmcli -t -f "$1" device wifi list 2>/dev/null
 }
 
-_nmcli_ab_bssid()
+_nmcli_dev_status()
 {
-    COMPREPLY=( $( compgen -W "$(nmcli -t -f BSSID dev wifi list 2>/dev/null )" -- $cur ) )
+    nmcli -t -f "$1" device status 2>/dev/null
 }
 
-_nmcli()
+_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()
 {
-    local cur prev words cword
-    _init_completion || return
+    eval "local ARRAY=(\"\${$1[@]}\")"
+    local i
+    local tmp=()
+    local lower=$2
+    local upper=${3:-$lower}
 
-    case $prev in
-        -m|--mode)
-            COMPREPLY=( $( compgen -W 'tabular multiline' -- $cur ) )
-            return 0
+    # 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
             ;;
-        -f|--fields)
-            COMPREPLY=( $( compgen -W 'all common' -- $cur ) )
-            return 0
+        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
             ;;
-        -e|--escape)
-            _nmcli_list "yes no"
-            return 0
+        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
             ;;
-        id)
-            _nmcli_con_id
-            return 0
+        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
             ;;
-        uuid)
-            _nmcli_con_uuid
-            return 0
+        ifname)
+            if [[ ${#words[@]} -le 2 ]]; then
+                _nmcli_list_nl "$(_nmcli_dev_status DEVICE)"
+                return 0
+            fi
+            _nmcli_array_delete_at words 0 1
             ;;
-        iface)
-            _available_interfaces
-            return 0
+        *)
+            COMMAND_CONNECTION_TYPE=
+            COMMAND_CONNECTION_ID="${words[0]}"
+            _nmcli_array_delete_at words 0
             ;;
-        bssid)
-            _nmcli_ab_bssid
+    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
             ;;
-        wep-key-type)
-            _nmcli_list "key phrase"
+        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
 
-    if [[ $cword -eq 1 ]] ; then
-        if [[ "$cur" == -* ]]; then
-            COMPREPLY=( $( compgen -W '--terse --pretty --mode --fields \
-                --escape --version --help' -- $cur ) )
-        else
-            COMPREPLY=( $( compgen -W "nm con dev" -- $cur ) )
-        fi
-    else
-        local object=${words[1]}
-        local command=${words[2]}
-
-        case $object in
-            nm)
-                case $command in
-                    enable)
-                        _nmcli_list "true false"
-                        return 0
-                        ;;
-                    sleep)
-                        _nmcli_list "true false"
-                        return 0
+    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
                         ;;
-                    wifi)
-                        _nmcli_list "on off"
-                        return 0
+                    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
                         ;;
-                    wwan)
-                        _nmcli_list "on off"
-                        return 0
+                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"
                         ;;
-                    wimax)
-                        _nmcli_list "on off"
-                        return 0
+                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
 
-                COMPREPLY=( $( compgen -W 'status permissions enable sleep \
-                    wifi wwan wimax' -- $cur ) )
-                ;;
-            con)
-                case $command in
-                    list)
-                        COMPREPLY=( $( compgen -W 'id uuid' -- $cur ) )
-                        return 0
-                        ;;
-                    up)
-                        if [[ "$cur" == -* ]]; then
-                            COMPREPLY=( $( compgen -W '--nowait --timeout' \
-                                -- $cur ) )
-                        else
-                            COMPREPLY=( $( compgen -W 'id uuid iface ap nsp' \
-                                -- $cur ) )
+                            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
-                        return 0
                         ;;
-                    down)
-                        COMPREPLY=( $( compgen -W 'id uuid' -- $cur ) )
-                        return 0
-                        ;;
-                    delete)
-                        COMPREPLY=( $( compgen -W 'id uuid' -- $cur ) )
-                        return 0
+                    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
                         ;;
-                esac
+                    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
 
-                COMPREPLY=( $( compgen -W 'list status up down delete' \
-                        -- $cur ) )
-                ;;
-            dev)
-                case $command in
-                    list)
-                        COMPREPLY=( $( compgen -W 'iface' -- $cur ) )
-                        return 0
-                        ;;
-                    disconnect)
-                        if [[ "$cur" == -* ]]; then
-                            COMPREPLY=( $( compgen -W '--nowait --timeout' \
-                                -- $cur ) )
-                        else
-                            COMPREPLY=( $( compgen -W 'iface' -- $cur ) )
+                            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
-                        return 0
                         ;;
-                    wifi)
-                        local subcommand=${words[3]}
+                    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
 
-                        case $subcommand in
-                            list)
-                                COMPREPLY=( $( compgen -W 'iface bssid' \
-                                    -- $cur ) )
+                            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
-                                ;;
-                            connect)
-                                if [[ "$cur" == -* ]]; then
-                                    COMPREPLY=( $( compgen -W '--private \
-                                        --nowait --timeout' -- $cur ) )
+                            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
-                                    if [[ "$prev" == "connect" ]]; then
-                                        _nmcli_ap_ssid
+                                    _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
-                                        COMPREPLY=( $( compgen -W 'password \
-                                            wep-key-type iface bssid name' \
-                                            -- $cur ) )
+                                        _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
-                                ;;
-                        esac
+                            fi
 
-                        COMPREPLY=( $( compgen -W 'list connect' -- $cur ) )
-                        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.
+                                _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
                         ;;
-                esac
+                    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
 
-                COMPREPLY=( $( compgen -W 'status list disconnect wifi' \
-                    -- $cur ) )
-                ;;
-        esac
+                            LONG_OPTIONS=(help)
+                            HELP_ONLY_AS_FIRST=1
+                            _nmcli_compl_OPTIONS
 
-    fi
+                           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 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)
+                        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
 } &&