NetworkManager/cli/completion/nmcli
Thomas Haller ab231cb0d9 cli: improve bash completion by parsing options separately.
To support optimal completion, more context must be considered.
Especially the OPTIONS, which must appear before the OBJECT.
Modify bash completion to try parsing first the options.

Before, whenever you had options, completion did not work
anymore (because the object was expected as first argument).
Moreover, options were also suggested after specifying the
object. This is now mitigated by parsing the command line
in two steps.

Signed-off-by: Thomas Haller <thaller@redhat.com>
2013-08-28 14:36:49 +02:00

425 lines
13 KiB
Bash

# 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 ) )
}
_nmcli_con_id()
{
echo "$(nmcli -t -f NAME con show c 2>/dev/null)"
}
_nmcli_con_uuid()
{
echo "$(nmcli -t -f UUID con show c 2>/dev/null)"
}
_nmcli_con_path()
{
echo "$(nmcli -t -f DBUS-PATH con show c 2>/dev/null)"
}
_nmcli_con_apath()
{
echo "$(nmcli -t -f DBUS-PATH con show a 2>/dev/null)"
}
_nmcli_ap_ssid()
{
echo "$(nmcli -t -f SSID dev wifi list 2>/dev/null)"
# without quotes
#ssids="$(nmcli -t -f SSID dev wifi list 2>/dev/null)"
#local IFS=$'\n'
#for ssid in $ssids; do
# temp="${ssid%\'}"
# temp="${temp#\'}"
# echo "$temp"
#done
}
_nmcli_ap_bssid()
{
echo "$(nmcli -e no -t -f BSSID dev wifi list 2>/dev/null)"
}
_nmcli_NM_devices()
{
echo "$(nmcli -t -f DEVICE dev status 2>/dev/null)"
}
_nmcli_NM_dev_MAC()
{
echo "$(nmcli -t dev show | grep HWADDR | cut -d':' -f2- | sort | uniq)"
}
# 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_complete_OPTIONS()
{
local OPTIONS=( -t --terse -p --pretty -m --mode -f --fields -e --escape -n --nocheck -a --ask -w --wait -v --version -h --help )
for (( ; ; )); do
if [[ "${#words[@]}" -le 1 ]]; then
# 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)
fi
_nmcli_list "$(echo "${OPTIONS[@]}")"
return 0
fi
case "${words[0]}" in
-t|--terse)
REMOVE_OPTIONS=(-t --terse)
words=("${words[@]:1}")
;;
-p|--pretty)
REMOVE_OPTIONS=(-p --pretty)
words=("${words[@]:1}")
;;
-n|--nocheck)
REMOVE_OPTIONS=(-n --nocheck)
words=("${words[@]:1}")
;;
-a|--ask)
REMOVE_OPTIONS=(-a --ask)
words=("${words[@]:1}")
;;
-v|--version)
REMOVE_OPTIONS=(-v --version)
words=("${words[@]:1}")
;;
-h|--help)
REMOVE_OPTIONS=(-h --help)
words=("${words[@]:1}")
;;
-m|--mode)
if [[ "${#words[@]}" -eq 2 ]]; then
_nmcli_list "tabular multiline"
return 0
fi
REMOVE_OPTIONS=(-m --mode)
words=("${words[@]:2}")
;;
-f|--fields)
if [[ "${#words[@]}" -eq 2 ]]; then
_nmcli_list "all common"
return 0
fi
REMOVE_OPTIONS=(-f --fields)
words=("${words[@]:2}")
;;
-e|--escape)
if [[ "${#words[@]}" -eq 2 ]]; then
_nmcli_list "no yes"
return 0
fi
REMOVE_OPTIONS=(-e --escape)
words=("${words[@]:2}")
;;
-w|--wait)
if [[ "${#words[@]}" -eq 2 ]]; then
_nmcli_list ""
return 0
fi
REMOVE_OPTIONS=(-w --wait)
words=("${words[@]:2}")
;;
*)
# something unexpected. We are finished with parsing the OPTIONS.
return 1
;;
esac
# 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
done
}
_nmcli()
{
local cur prev words cword
_init_completion || return
# we don't care about any arguments after the current curser position
# because we only parse from left to right. So, if there are some arguments
# right of the curser, just ignore them. Also don't care about ${words[0]}.
words=("${words[@]:1:$cword}")
_nmcli_complete_OPTIONS && return 0
# FIXME: the following block completes certain parameters without
# considering enough context, while they only make sense depending on
# the OBJECT and COMMAND.
case $prev in
id)
_nmcli_list_nl "$(_nmcli_con_id)"
return 0
;;
uuid)
_nmcli_list_nl "$(_nmcli_con_uuid)"
return 0
;;
path)
_nmcli_list_nl "$(_nmcli_con_path)"
return 0
;;
apath)
_nmcli_list_nl "$(_nmcli_con_apath)"
return 0
;;
iface | ifname)
#_available_interfaces
_nmcli_list_nl "$(_nmcli_NM_devices)"
return 0
;;
bssid)
_nmcli_list_nl "$(_nmcli_ap_bssid)"
return 0
;;
wep-key-type)
_nmcli_list "key phrase"
return 0
;;
autoconnect | stp | hairpin | private)
_nmcli_list "yes no"
return 0
;;
# connection types
type)
_nmcli_list "ethernet wifi wimax gsm cdma infiniband adsl bluetooth vpn \
olpc-mesh vlan bond bridge bond-slave bridge-slave"
return 0
;;
# VPN types
vpn-type)
_nmcli_list "vpn-type vpnc openvpn pptp openconnect openswan"
return 0
;;
# Bluetooth modes
bt-type)
_nmcli_list "panu dun-gsm dun-cdma"
return 0
;;
# InfiniBand transport modes
transport-mode)
_nmcli_list "datagram connected"
return 0
;;
# bonding modes
mode)
_nmcli_list "balance-rr active-backup balance-xor broadcast \
802.3ad balance-tlb balance-alb"
return 0
;;
# MAC addresses
mac)
_mac_addresses
return 0
;;
# master interface
master)
UUIDS=$(_nmcli_con_uuid)
DEVICES=$(_nmcli_NM_devices)
_nmcli_list "$DEVICES $UUIDS"
return 0
;;
esac
local object=${words[0]}
local command=${words[1]}
case $object in
h|he|hel|help)
return 0
;;
g|ge|gen|gene|gener|genera|general)
if [[ ${#words[@]} -gt 2 ]]; then
case $command in
s|st|sta|stat|statu|status | p|pe|per|perm|permi|permis|permiss|permissi|permissio|permission|permissions)
return 0
;;
l|lo|log|logg|loggi|loggin|logging)
_nmcli_list "level domains"
return 0
;;
esac
fi
_nmcli_list "status permissions logging help"
;;
n|ne|net|netw|netwo|networ|network|networki|networkin|networking)
if [[ ${#words[@]} -gt 2 ]]; then
case $command in
on | off)
return 0
;;
esac
fi
_nmcli_list "on off help"
;;
r|ra|rad|radi|radio)
if [[ ${#words[@]} -gt 2 ]]; then
case $command in
a|al|all | w|wi|wif|wifi | ww|wwa|wwan | wim|wima|wimax)
_nmcli_list "on off"
return 0
;;
esac
fi
_nmcli_list "all wifi wwan wimax help"
;;
c|co|con|conn|conne|connec|connect|connecti|connectio|connection)
if [[ ${#words[@]} -gt 2 ]]; then
case $command in
s|sh|sho|show)
local subcommand=${words[2]}
if [[ ${#words[@]} -gt 3 ]]; then
case $subcommand in
c|co|con|conf|confi|config|configu|configur|configure|configured)
_nmcli_list "id uuid path"
return 0
;;
a|ac|act|acti|activ|active)
_nmcli_list "id uuid path apath"
return 0
;;
esac
fi
_nmcli_list "configured active"
return 0
;;
u|up)
if [[ "$cur" == -* ]]; then
_nmcli_list "--nowait --timeout"
else
_nmcli_list "id uuid path iface ap nsp"
fi
return 0
;;
d|do|dow|down)
_nmcli_list "id uuid path apath"
return 0
;;
a|ad|add)
_nmcli_list "type con-name autoconnect ifname help"
return 0
;;
e|ed|edi|edit)
_nmcli_list "id uuid path type con-name"
return 0
;;
m|mo|mod|modi|modif|modify)
_nmcli_list_nl "$(_nmcli_con_id)"
return 0
;;
de|del|dele|delet|delete)
_nmcli_list "id uuid path"
return 0
;;
esac
fi
_nmcli_list "show up down add modify edit delete reload help"
;;
d|de|dev|devi|devic|device)
if [[ ${#words[@]} -gt 2 ]]; then
case $command in
sh|sho|show)
_nmcli_list_nl "$(_nmcli_NM_devices)"
return 0
;;
d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect)
if [[ "$cur" == -* ]]; then
_nmcli_list "--nowait --timeout"
else
_nmcli_list_nl "$(_nmcli_NM_devices)"
fi
return 0
;;
w|wi|wif|wifi)
local subcommand=${words[2]}
case $subcommand in
l|li|lis|list)
_nmcli_list "iface bssid"
return 0
;;
c|co|con|conn|conne|connec|connect)
if [[ "$cur" == -* ]]; then
_nmcli_list "--private --nowait --timeout"
else
if [[ "$prev" == "connect" ]]; then
_nmcli_list_nl "$(_nmcli_ap_ssid)"
else
_nmcli_list "password wep-key-type iface bssid name"
fi
fi
return 0
;;
r|re|res|resc|resca|rescan)
if [[ "$cur" == i* ]]; then
_nmcli_list "iface"
else
_nmcli_list_nl "$(_nmcli_NM_devices)"
fi
return 0
;;
esac
_nmcli_list "list connect scan"
return 0
;;
wim|wima|wimax)
local subcommand=${words[2]}
if [[ ${#words[@]} -gt 3 ]]; then
case $subcommand in
l|li|lis|list)
_nmcli_list "iface nsp"
return 0
;;
esac
fi
_nmcli_list "list"
return 0
;;
esac
fi
_nmcli_list "status show disconnect wifi wimax help"
;;
esac
return 0
} &&
complete -F _nmcli nmcli
# ex: ts=4 sw=4 et filetype=sh